Re: [xml] [patch] make libxml2 slightly more thread-friendly
- From: Ted Phelps <phelps mantara com>
- To: veillard redhat com
- Cc: xml gnome org
- Subject: Re: [xml] [patch] make libxml2 slightly more thread-friendly
- Date: Mon, 12 Feb 2007 10:18:03 -0600
Hi Daniel,
As always, thank you for your speedy reply.
Daniel Veillard writes:
Okay, fixing the problem sounds fine of course, but I have a couple of
issues with the patch:
- all thread implementation specific code in libxml2 is contained in
threads.c module, that patch breal that containment, I would really
prefer if this code was kept in threads.c
I thought you might. I initially chose not to because I didn't want to
add new functions to the shared object, but I'm happy to do so if you
are. Or do you have some mechanism to prevent functions from being
exported to the aplication?
- it looks to me that this code would break if libxml2 is configured
without thread support (configure --without-threads)
Oops. You are correct. That should now be fixed.
- it uses the keyword 'volatile' which is not used anywhere else in
libxml2, I would prefer to avoid it if possible to maximize portability
(plus its semantic is IMHO rather fuzzy)
I included the volatile keyword for the Windows code because the
declaration of InterlockedCompareExchangePointer indicates that the
first parameter should be of type 'PVOID volatile *'. I believe that we
can safely leave this out and I have removed it from the patch below.
For the BeOS implementation, I have changed 'volatile' to vint32,
following the definition of atomic_add:
http://www.beunited.org/bebook/The%20Support%20Kit/Functions.html
You may want to consider changing run_once_init to be a vint32 too.
I'm not sure how to best achieve those improvements, as this code
really mix the xmlInitParser with really thread-specific code.
I have shifted the thread-related code into threads.c, leaving only
calls to lock and unlock a global mutex.
Also we had trouble in the past with the xmlInitGlobals()/
xmlInitThreads()/xmlInitMemory() ordering of initializers, the
interraction are relatively complex to debug I hope the new code
doesn't have side effect, that seems true as it uses direct memory
allocations, it's just something to keep in mind.
I believe this patch should be immmune to such interactions.
Ted, do you think you could try to rewrite the patch to address the 3
issues listed before, this may require separing the xmlInitParser into
one routine when thread are not compiled in, and one in threads.c when
thread support is compiled in (but that's just a suggestion).
No worries. Here's a second attempt. Please have a look and let me
know if you'd like me to try a different approach or make additional
changes.
Thanks again,
-Ted
diff -Naurp libxml2-2.6.27.orig/include/libxml/threads.h libxml2-2.6.27/include/libxml/threads.h
--- libxml2-2.6.27.orig/include/libxml/threads.h 2005-09-04 21:57:21.000000000 +0100
+++ libxml2-2.6.27/include/libxml/threads.h 2007-02-12 15:26:12.000000000 +0000
@@ -54,6 +54,11 @@ XMLPUBFUN void XMLCALL
XMLPUBFUN void XMLCALL
xmlFreeRMutex (xmlRMutexPtr tok);
+XMLPUBFUN void XMLCALL
+ xmlGlobalInitMutexLock(void);
+XMLPUBFUN void XMLCALL
+ xmlGlobalInitMutexUnlock(void);
+
/*
* Library wide APIs.
*/
diff -Naurp libxml2-2.6.27.orig/parser.c libxml2-2.6.27/parser.c
--- libxml2-2.6.27.orig/parser.c 2006-10-17 15:28:27.000000000 +0100
+++ libxml2-2.6.27/parser.c 2007-02-12 15:41:38.000000000 +0000
@@ -12839,26 +12839,34 @@ xmlInitParser(void) {
if (xmlParserInitialized != 0)
return;
- if ((xmlGenericError == xmlGenericErrorDefaultFunc) ||
- (xmlGenericError == NULL))
- initGenericErrorDefaultFunc(NULL);
- xmlInitGlobals();
- xmlInitThreads();
- xmlInitMemory();
- xmlInitCharEncodingHandlers();
- xmlDefaultSAXHandlerInit();
- xmlRegisterDefaultInputCallbacks();
+#ifdef LIBXML_THREAD_ENABLED
+ xmlGlobalInitMutexLock();
+ if (xmlParserInitialized == 0) {
+#endif
+ if ((xmlGenericError == xmlGenericErrorDefaultFunc) ||
+ (xmlGenericError == NULL))
+ initGenericErrorDefaultFunc(NULL);
+ xmlInitGlobals();
+ xmlInitThreads();
+ xmlInitMemory();
+ xmlInitCharEncodingHandlers();
+ xmlDefaultSAXHandlerInit();
+ xmlRegisterDefaultInputCallbacks();
#ifdef LIBXML_OUTPUT_ENABLED
- xmlRegisterDefaultOutputCallbacks();
+ xmlRegisterDefaultOutputCallbacks();
#endif /* LIBXML_OUTPUT_ENABLED */
#ifdef LIBXML_HTML_ENABLED
- htmlInitAutoClose();
- htmlDefaultSAXHandlerInit();
+ htmlInitAutoClose();
+ htmlDefaultSAXHandlerInit();
#endif
#ifdef LIBXML_XPATH_ENABLED
- xmlXPathInit();
+ xmlXPathInit();
+#endif
+ xmlParserInitialized = 1;
+#ifdef LIBXML_THREAD_ENABLED
+ }
+ xmlGlobalInitMutexUnlock();
#endif
- xmlParserInitialized = 1;
}
/**
diff -Naurp libxml2-2.6.27.orig/threads.c libxml2-2.6.27/threads.c
--- libxml2-2.6.27.orig/threads.c 2006-06-26 13:16:03.000000000 +0100
+++ libxml2-2.6.27/threads.c 2007-02-12 16:02:49.000000000 +0000
@@ -139,6 +139,7 @@ struct _xmlRMutex {
static pthread_key_t globalkey;
static pthread_t mainthread;
static pthread_once_t once_control = PTHREAD_ONCE_INIT;
+static pthrad_mutex_t global_init_lock = PTHREAD_MUTEX_INITIALIZER;
#elif defined HAVE_WIN32_THREADS
#if defined(HAVE_COMPILER_TLS)
static __declspec(thread) xmlGlobalState tlstate;
@@ -152,11 +153,14 @@ static struct
DWORD done;
DWORD control;
} run_once = { 0, 0 };
+static LPCRITICAL_SECTION global_init_lock = NULL;
/* endif HAVE_WIN32_THREADS */
#elif defined HAVE_BEOS_THREADS
int32 globalkey = 0;
thread_id mainthread = 0;
int32 run_once_init = 0;
+static int32 global_init_lock = -1;
+static int32 global_init_count = 0;
#endif
static xmlRMutexPtr xmlLibraryLock = NULL;
@@ -413,6 +417,85 @@ xmlRMutexUnlock(xmlRMutexPtr tok ATTRIBU
#endif
}
+/**
+ * xmlGlobalInitMutexLock
+ *
+ * Makes sure that the global initialization mutex is initialized and
+ * locks it.
+ */
+void
+xmlGlobalInitMutexLock(void)
+{
+ /* Make sure the global init lock is initialized and then lock it. */
+#ifdef HAVE_PTHREAD_H
+ int err;
+
+ /* The mutex is statically initialized, so we just lock it. */
+ pthread_mutex_lock(&global_init_lock);
+#elif defined HAVE_WIN32_THREADS
+ LPCRITICAL_SECTION cs;
+
+ /* Create a new critical section */
+ if (global_init_lock == NULL) {
+ cs = malloc(sizeof(CRITICAL_SECTION));
+ InitializeCriticalSection(cs);
+
+ /* Swap it into the global_init_lock */
+ InterlockedCompareExchangePointer(&global_init_lock, cs, NULL);
+
+ /* If another thread successfully recorded its critical
+ * section in the global_init_lock then discard the one
+ * allocated by this thread. */
+ if (global_init_lock != cs) {
+ free(cs);
+ }
+ }
+
+ /* Lock the chosen critical section */
+ EnterCriticalSection(global_init_lock);
+#elif defined HAVE_BEOS_THREADS
+ int32 sem;
+
+ /* Allocate a new semaphore */
+ sem = create_sem(1, "xmlGlobalinitMutex");
+
+ while (global_init_lock == -1) {
+ if (atomic_add(&global_init_count, 1) == 0) {
+ global_init_lock = sem;
+ } else {
+ snooze(1);
+ atomic_add(&global_init_count, -1);
+ }
+ }
+
+ /* If another thread successfully recorded its critical
+ * section in the global_init_lock then discard the one
+ * allocated by this thread. */
+ if (global_init_lock != sem)
+ delete_sem(sem);
+
+ /* Acquire the chosen semaphore */
+ if (acquire_sem(global_init_lock) != B_NO_ERROR) {
+#ifdef DEBUG_THREADS
+ xmlGenericError(xmlGenericErrorContext, "xmlGlobalInitMutexLock():BeOS:Couldn't acquire semaphore\n");
+ exit();
+#endif
+ }
+#endif
+}
+
+void
+xmlGlobalInitMutexUnlock(void)
+{
+#ifdef HAVE_PTHREAD_H
+ pthread_mutex_unlock(&global_init_lock);
+#elif defined HAVE_WIN32_THREADS
+ LeaveCriticalSection(global_init_lock);
+#elif defined HAVE_BEOS_THREADS
+ release_sem(global_init_lock);
+#endif
+}
+
/************************************************************************
* *
* Per thread global state handling *
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]