Hi,
I hope I am submitting this message to the correct list.
I have a blocking issue on thread safety in our PHP extension module
that we have developed at Saxonica called Saxon/C, which provides XSLT
2.0 and XQuery support in PHP. The extension is built in C++ using Zend
module framework. The aplication is quite involved as it was
cross-compiled from Java using Excelsior JET, with its own JVM. The JVM
can only be accessed in one os process.
The issue is I get some intermittent crashes in my php scripts, which I
believe is due to more than one process accessing same JET JVM
resource. It seems like my global objects are getting reused in another
php request.
I am wondering if someone could give me some advice on how to build the
Saxon/C extension library to overcome this problem or just point me in
the right direction.
I have tried to follow some forum posts in regards to creating a TSRM
layer but I am not getting very far with this.
kind regards,
--
O'Neil Delpratt
Software Developer, Saxonica Limited
Email: oneil@saxonica.com mailto:oneil@saxonica.com
Tel: +44 118 946 5894
Web: http://www.saxonica.com
Saxonica Community Site: http://dev.saxonica.com Saxonica Bug tracking
System: https://saxonica.plan.io/
I have tried to follow some forum posts in regards to creating a TSRM
layer but I am not getting very far with this.
Yes, TSRM is our thread-safe resource manager providing a thread local
storage for PHP request isolation.
The information in your message is sparse though. Without any code,
examples of crashes etc it is hard to see what you are actually doing,
where you are stuck and how to help.
johannes
Yes, TSRM is our thread-safe resource manager providing a thread local
storage for PHP request isolation.The information in your message is sparse though. Without any code,
examples of crashes etc it is hard to see what you are actually doing,
where you are stuck and how to help.
Thanks for your quick response. Please find code below:
I have commented out my attempt to do some thread safety.
I have cut out some PHP_METHODS to ease the readability.
The line of code of interest is indicated by //**** in two places.
Specifically, where we create the SaxonProcessor object and also destroy
the JVM needs to be in a single os process (i.e.:
"saxonProc = new SaxonProcessor(false); " and "obj->saxonProc->close();" )
===============================
#ifndef PHP_SAXON_H
#define PHP_SAXON_H
#define PHP_SAXON_EXTNAME "Saxon/C"
#define PHP_SAXON_EXTVER "0.2"
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
/*#ifdef ZTS
#include "TSRM.h"
#define COUNTER_G(v) TSRMG(counter_globals_id, zend_counter_globals , v)
#else
#define COUNTER_G(v) (counter_globals.v)
#endif/
extern "C" {
#ifdef linux
#include "php.h"
#include "ext/standard/info.h"
#endif
#include "zend_exceptions.h"
}
/*ZEND_BEGIN_MODULE_GLOBALS(saxon)
long counter;
ZEND_END_MODULE_GLOBALS(saxon)
ZEND_DECLARE_MODULE_GLOBALS(saxon)
int saxon_globals_id;
*/
extern zend_module_entry saxon_module_entry;
#define phpext_saxon_ptr &saxon_module_entry;
zend_object_handlers XsltProcessor_object_handlers;
zend_object_handlers XQueryProcessor_object_handlers;
zend_object_handlers xdmValue_object_handlers;
struct SaxonProcessor_object {
zend_object std;
SaxonProcessor * saxonProc;
};
struct XsltProcessor_object {
zend_object std;
XsltProcessor *xsltProcessor;
SaxonProcessor * saxonProc;
};
struct xdmValue_object {
zend_object std;
XdmValue * xdmValue;
};
zend_class_entry *xsltProcessor_ce;
zend_class_entry *xqueryProcessor_ce;
zend_class_entry *xdmValue_ce;
void XsltProcessor_free_storage(void object TSRMLS_DC)
{
php_error(E_WARNING, "XXXXXXXXXXXXXXXX - free storage");
XsltProcessor_object obj = (XsltProcessor_object )object;
if (obj->saxonProc != NULL) {
obj->saxonProc->close(); //* - CONTAINS CODE THAT CAN ONLY BE
RUN IN SINGLE OS PROCESS
}
delete obj->saxonProc;
delete obj->xsltProcessor;
zend_hash_destroy(obj->std.properties);
FREE_HASHTABLE(obj->std.properties);
efree(obj);
}
zend_object_value XsltProcessor_create_handler(zend_class_entry *type
TSRMLS_DC)
{
zval *tmp;
zend_object_value retval;
XsltProcessor_object *obj = (XsltProcessor_object
*)emalloc(sizeof(XsltProcessor_object));
memset(obj, 0, sizeof(XsltProcessor_object));
obj->std.ce = type;
ALLOC_HASHTABLE(obj->std.properties);
zend_hash_init(obj->std.properties, 0, NULL, ZVAL_PTR_DTOR, 0);
retval.handle = zend_objects_store_put(obj, NULL,
XsltProcessor_free_storage, `NULL` TSRMLS_CC);
retval.handlers = &XsltProcessor_object_handlers;
return retval;
}
PHP_METHOD(Xslt2Processor, __construct)
{
char cwd[256];
getcwd(cwd, sizeof(cwd));
if (getcwd(cwd, sizeof(cwd)) == NULL) {
php_error(E_WARNING,"getcwd error");
}
XsltProcessor *xsltProcessor = NULL;
zval *object = getThis();
SaxonProcessor * saxonProc;
SaxonProcessor_object *saxonProcObj = (SaxonProcessor_object
)zend_object_store_get_object(object TSRMLS_CC);
SaxonProcessor * saxonProc = saxonProcObj->saxonProc;
if(saxonProc == NULL) {
saxonProc = new SaxonProcessor(false); //*** - CONTAINS CODE
THAT CAN ONLY BE RUN IN SINGLE OS PROCESS
saxonProcObj->saxonProc = saxonProc;
}
saxonProc->setcwd(cwd);
XsltProcessor_object *obj = (XsltProcessor_object
*)zend_object_store_get_object(object TSRMLS_CC);
obj->saxonProc = saxonProc;
/saxonProc = obj->saxonProc;
if(saxonProc == NULL) {
php_error(E_WARNING, "Xslt2Processor Construct - call to
SAxonProcessor");
saxonProc = new SaxonProcessor(false);
obj->saxonProc = saxonProc;
}
saxonProc->setcwd(cwd);/
xsltProcessor = saxonProc->newTransformer();
obj->xsltProcessor = xsltProcessor;
}
PHP_METHOD(Xslt2Processor, __destruct)
{
XsltProcessor_object *obj = (XsltProcessor_object
*)zend_object_store_get_object(
getThis() TSRMLS_CC);
SaxonProcessor_object *obj2 = (SaxonProcessor_object
*)zend_object_store_get_object(
getThis() TSRMLS_CC);
XsltProcessor *xsltProcessor = obj->xsltProcessor;
SaxonProcessor * saxonProc= obj2->saxonProc;
delete xsltProcessor;
delete saxonProc
}
PHP_METHOD(Xslt2Processor, xsltSaveResultToFile)
{
XsltProcessor *xsltProcessor;
char * sourcefile;
char * stylesheet;
char * outputfile;
int len1, len2, len3;
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sss",
&sourcefile, &len1, &stylesheet, &len2, &outputfile, &len3) == FAILURE) {
RETURN_NULL();
}
XsltProcessor_object *obj = (XsltProcessor_object
*)zend_object_store_get_object(
getThis() TSRMLS_CC);
xsltProcessor = obj->xsltProcessor;
if (xsltProcessor != NULL) {
xsltProcessor->xsltSaveResultToFile(sourcefile, stylesheet,
outputfile);
if(xsltProcessor->exceptionOccurred()) {
//throw exception
}
}
}
PHP_METHOD(Xslt2Processor, xsltApplyStylesheet)
{
XsltProcessor *xsltProcessor;
char * sourcefile;
char * stylesheet;
int len1, len2, myint;
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ss",
&sourcefile, &len1, &stylesheet, &len2) == FAILURE) {
RETURN_NULL();
}
XsltProcessor_object *obj = (XsltProcessor_object
*)zend_object_store_get_object(
getThis() TSRMLS_CC);
xsltProcessor = obj->xsltProcessor;
if (xsltProcessor != NULL) {
const char * result =
xsltProcessor->xsltApplyStylesheet(sourcefile, stylesheet);
xsltProcessor->checkException();
if(result != `NULL` ) {
char *str = estrdup(result);
RETURN_STRING(str, 0);
}
}
RETURN_NULL();
}
zend_function_entry XsltProcessor_methods[] = {
PHP_ME(Xslt2Processor, __construct, NULL, ZEND_ACC_PUBLIC |
ZEND_ACC_CTOR)
PHP_ME(Xslt2Processor, __destruct, NULL, ZEND_ACC_PUBLIC |
ZEND_ACC_DTOR)
PHP_ME(Xslt2Processor, xsltSaveResultToFile, NULL,
ZEND_ACC_PUBLIC)
PHP_ME(Xslt2Processor, xsltApplyStylesheet, NULL,
ZEND_ACC_PUBLIC)
PHP_ME(Xslt2Processor, xsltApplyStylesheet1, NULL,
ZEND_ACC_PUBLIC)
PHP_ME(Xslt2Processor, setSourceValue, NULL, ZEND_ACC_PUBLIC)
PHP_ME(Xslt2Processor, parseString, NULL, ZEND_ACC_PUBLIC)
PHP_ME(Xslt2Processor, setParameter, NULL, ZEND_ACC_PUBLIC)
PHP_ME(Xslt2Processor, setProperty, NULL, ZEND_ACC_PUBLIC)
PHP_ME(Xslt2Processor, clear, NULL, ZEND_ACC_PUBLIC)
PHP_ME(Xslt2Processor, exceptionClear, NULL, ZEND_ACC_PUBLIC)
PHP_ME(Xslt2Processor, close, NULL, ZEND_ACC_PUBLIC)
PHP_ME(Xslt2Processor, getErrorCode, NULL, ZEND_ACC_PUBLIC)
PHP_ME(Xslt2Processor, getErrorMessage, NULL, ZEND_ACC_PUBLIC)
PHP_ME(Xslt2Processor, getExceptionCount, NULL, ZEND_ACC_PUBLIC)
PHP_ME(Xslt2Processor, version, NULL, ZEND_ACC_PUBLIC)
{NULL, NULL, NULL}
};
PHP_MINIT_FUNCTION(saxon)
{
// ts_allocate_id(&saxon_globals_id, sizeof(XsltProcessor_object),
(ts_allocate_ctor)XsltProcessor_object,(ts_allocate_dtor)XsltProcessor_object);
zend_class_entry ce;
INIT_CLASS_ENTRY(ce, "Xslt2Processor", XsltProcessor_methods);
xsltProcessor_ce = zend_register_internal_class(&ce TSRMLS_CC);
xsltProcessor_ce->create_object = XsltProcessor_create_handler;
memcpy(&XsltProcessor_object_handlers,
zend_get_std_object_handlers(), sizeof(zend_object_handlers));
XsltProcessor_object_handlers.clone_obj = NULL;
INIT_CLASS_ENTRY(ce, "XQueryProcessor", XQueryProcessor_methods);
xqueryProcessor_ce = zend_register_internal_class(&ce TSRMLS_CC);
xqueryProcessor_ce->create_object = XQueryProcessor_create_handler;
memcpy(&XQueryProcessor_object_handlers,
zend_get_std_object_handlers(), sizeof(zend_object_handlers));
XQueryProcessor_object_handlers.clone_obj = NULL;
INIT_CLASS_ENTRY(ce, "XdmValue", xdmValue_methods);
xdmValue_ce = zend_register_internal_class(&ce TSRMLS_CC);
xdmValue_ce->create_object = xdmValue_create_handler;
memcpy(&xdmValue_object_handlers,
zend_get_std_object_handlers(), sizeof(zend_object_handlers));
xdmValue_object_handlers.clone_obj = NULL;
// ZEND_INIT_MODULE_GLOBALS(saxon, php_saxon_init_globals,NULL);
// REGISTER_INI_ENTRIES();
return SUCCESS;
}
// function implementation
PHP_MINFO_FUNCTION(saxon)
{
php_info_print_table_start();
php_info_print_table_header(2, "Saxon/C", "enabled");
php_info_print_table_row(2, "Saxon/C EXT version", "0.2");
php_info_print_table_row(2, "Saxon-HEJ", "9.5.1.3");
php_info_print_table_row(2, "Excelsior JET (MP1)", "9.0");
php_info_print_table_end();
DISPLAY_INI_ENTRIES();
}
PHP_MSHUTDOWN_FUNCTION(saxon){
php_error(E_WARNING,"entered MSHUTDOWN");
UNREGISTER_INI_ENTRIES();
return SUCCESS;
}
PHP_RSHUTDOWN_FUNCTION(saxon){
php_error(E_WARNING,"entered RSHUTDOWN");
efree(xsltProcessor_ce);
return SUCCESS;
}
PHP_RINIT_FUNCTION(saxon){
php_error(E_WARNING,"entered RINT");
/* string str = ""+((SAXON_G(counter)));
const char * result = str.c_str();
php_error(E_WARNING, result);
SAXON_G(counter) = 0;*/
return SUCCESS;
}
zend_module_entry saxon_module_entry = {
#if ZEND_MODULE_API_NO >= 20010901
STANDARD_MODULE_HEADER,
#endif
PHP_SAXON_EXTNAME,
NULL/saxon_static_functions/, /* Functions /
PHP_MINIT(saxon), / MINIT /
PHP_MSHUTDOWN(saxon), / MSHUTDOWN /
/PHP_RINIT(saxon)/NULL, / RINIT /
PHP_RSHUTDOWN(saxon), / RSHUTDOWN /
PHP_MINFO(saxon), / MINFO */
#if ZEND_MODULE_API_NO >= 20010901
PHP_SAXON_EXTVER,
#endif
STANDARD_MODULE_PROPERTIES
};
//#ifdef COMPILE_DL_SAXON
extern "C" {
ZEND_GET_MODULE(saxon)
}
//#endif
===================
kind regards,
O'Neil Delpratt
Software Developer, Saxonica Limited
Email: oneil@saxonica.com mailto:oneil@saxonica.com
Tel: +44 118 946 5894
Web: http://www.saxonica.com
Saxonica Community Site: http://dev.saxonica.com Saxonica Bug tracking
System: https://saxonica.plan.io/
Hi Johannes,
Thanks for your quick response. Please find code below:
I have commented out my attempt to do some thread safety.
I have cut out some PHP_METHODS to ease the readability.
If you need to build the php extension yourself and require the full
project C/C++ code, including JET JVM library then please let me know
and I will send you it directly.
Thanks and kind regards,
--
O'Neil Delpratt
Software Developer, Saxonica Limited
Email: oneil@saxonica.com mailto:oneil@saxonica.com
Tel: +44 118 946 5894
Web: http://www.saxonica.com
Saxonica Community Site: http://dev.saxonica.com Saxonica Bug tracking
System: https://saxonica.plan.io/