[xml] [patch] make libxml2 slightly more thread-friendly




As documented here at the following URL, applications which use libxml2
are required to 'call xmlInitParser() in the "main" thread before using
any of the libxml2 API'.

    http://xmlsoft.org/threads.html

That's fine for applications, but less helpful for modules which are
loaded into larger application (think apache or the X11 server).  In the
latter case, each module which uses libxml2 will need to call
xmlInitParser() since it won't be able to determine whether or not
another module has done so.  If the larger application happens to run
these modules in separate threads then the test at the beginning of
xmlParserInit is insufficient to ensure that the initialization is
performed exactly once.  Under some circumstances it's possible for a
thread to hang waiting for a mutex to be unlocked which will never be
unlocked (see http://bugzilla.gnome.org/show_bug.cgi?id=406099).

The following patch (also available as an attachment in the above bug
report) adds some extra checking to xmlInitParser() to avoid these
problems with zero extra overhead for the non-initialization case.
Please consider adding it to libxml2.  Of course I'm happy to rework it
in whatever way the maintainers see fit.

Thanks,
-Ted


Content-Type: text/x-c
Content-Disposition: inline; filename=parser.c.diff

--- parser.c.orig       2006-10-17 15:28:27.000000000 +0100
+++ parser.c    2007-02-12 12:12:52.000000000 +0000
@@ -79,6 +79,27 @@
 #ifdef HAVE_ZLIB_H
 #include <zlib.h>
 #endif
+#ifdef HAVE_PTHREAD_H
+#include <pthread.h>
+#endif
+#ifdef HAVE_WIN32_THREADS
+#include <windows.h>
+#ifndef HAVE_COMPILER_TLS
+#include <process.h>
+#endif
+#endif
+
+#ifdef HAVE_BEOS_THREADS
+#include <OS.h>
+#include <TLS.h>
+#endif
+
+#if defined(HAVE_PTHREAD_H)
+extern int pthread_mutex_lock ()
+          __attribute((weak));
+extern int pthread_mutex_unlock ()
+          __attribute((weak));
+#endif /* HAVE_PTHREAD_H */
 
 /**
  * xmlParserMaxDepth:
@@ -89,6 +110,15 @@
  */
 unsigned int xmlParserMaxDepth = 1024;
 
+#if defined(HAVE_PTHREAD_H)
+static pthread_mutex_t xmlGlobalInitLock = PTHREAD_MUTEX_INITIALIZER;
+#elif defined(HAVE_WIN32_THREADS)
+static volatile LPCRITICAL_SECTION xmlGlobalInitLock = NULL;
+#elif defined(HAVE_BEOS_THREADS)
+static volatile int32 xmlGlobalInitLock = 0;
+static volatile xmlGlobalInitCounter = 0;
+#endif
+
 #define SAX2 1
 
 #define XML_PARSER_BIG_BUFFER_SIZE 300
@@ -12836,29 +12866,82 @@ static int xmlParserInitialized = 0;
 
 void
 xmlInitParser(void) {
+#if defined(HAVE_WIN32_THREADS)
+    LPCRITICAL_SECTION cs;
+#elif defined(HAVE_BEOS_THREADS)
+    int32 sem;
+#endif
+
     if (xmlParserInitialized != 0)
        return;
 
-    if ((xmlGenericError == xmlGenericErrorDefaultFunc) ||
-       (xmlGenericError == NULL))
-       initGenericErrorDefaultFunc(NULL);
-    xmlInitGlobals();
-    xmlInitThreads();
-    xmlInitMemory();
-    xmlInitCharEncodingHandlers();
-    xmlDefaultSAXHandlerInit();
-    xmlRegisterDefaultInputCallbacks();
+#if defined(HAVE_PTHREAD_H)
+    if (pthread_mutex_lock != NULL && pthread_mutex_unlock != NULL) {
+       pthread_mutex_lock(&xmlGlobalInitLock);
+    }
+#elif defined(HAVE_WIN32_THREADS)
+    /* Allocate a critical section in a thread-safe way */
+    cs = malloc(sizeof(CRITICAL_SECTION));
+    InitializeCriticalSection(cs);
+    InterlockedCompareExchangePointer(&xmlGlobalInitLock, cs, NULL);
+    if (xmlGlobalInitLock != cs) {
+       DeleteCriticalSection(cs);
+       free(cs);
+    }
+
+    /* Lock it */
+    EnterCriticalSection(xmlGlobalInitLock);
+#elif defined(HAVE_BEOS_THREADS)
+    /* Allocate a semaphore in a thread-safe way */
+    sem = create_sem(1, "xmlGlobalInitLock");
+    while (xmlGlobalInitLock == 0) {
+       if (atomic_add(&xmlGlobalInitCounter, 1) == 0) {
+           xmlGlobalInitLock = sem;
+       } else {
+           snooze(1);
+           atomic_add(&xmlGlobalInitCounter, -1);
+       }
+    }
+
+    if (sem != xmlGlobalInitLock)
+       delete_sem(sem);
+
+    /* Lock the semaphore */
+    acquire_sem(xmlGlobalInitLock);
+#endif
+
+    if (!xmlParserInitialized) {
+       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;
+    }
+
+#if defined(HAVE_PTHREAD_H)
+    if (pthread_mutex_lock != NULL && pthread_mutex_unlock != NULL) {
+       pthread_mutex_unlock(&xmlGlobalInitLock);
+    }
+#elif defined(HAVE_WIN32_THREADS)
+    LeaveCriticalSection(xmlGlobalInitLock);
+#elif defined(HAVE_BEOS_THREADS)
+    release_sem(xmlGlobalInitLock);
 #endif
-    xmlParserInitialized = 1;
 }
 
 /**



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]