applied Serguei Narojnyi's patch to add native thread support on the Win32
* threads.c: applied Serguei Narojnyi's patch to add native
thread support on the Win32 platform
* testThreadsWin32.c Makefile.am: added the test program also
from Serguei, Win32 specific
* include/win32config.h include/libxml/xmlwin32version.h.in:
added patch from Igor for the Windows thread specific defines.
Daniel
diff --git a/ChangeLog b/ChangeLog
index 8f4d19e..d08503d 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,12 @@
+Sun Jan 13 14:23:21 CET 2002 Daniel Veillard <daniel@veillard.com>
+
+ * threads.c: applied Serguei Narojnyi's patch to add native
+ thread support on the Win32 platform
+ * testThreadsWin32.c Makefile.am: added the test program also
+ from Serguei, Win32 specific
+ * include/win32config.h include/libxml/xmlwin32version.h.in:
+ added patch from Igor for the Windows thread specific defines.
+
Wed Jan 9 12:50:39 CET 2002 Daniel Veillard <daniel@veillard.com>
* entities.c: Anthony Jones pointed a bug in xmlCopyEntity()
diff --git a/Makefile.am b/Makefile.am
index 6f06df1..6314acc 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -569,7 +569,8 @@
example/Makefile.am example/gjobread.c example/gjobs.xml \
$(man_MANS) libxml-2.0.pc.in \
trionan.c trionan.h strio.c strio.h trio.c trio.h \
- triop.h triodef.h libxml.h
+ triop.h triodef.h libxml.h \
+ testThreadsWin32.c
pkgconfigdir = $(libdir)/pkgconfig
pkgconfig_DATA = libxml-2.0.pc
diff --git a/doc/Makefile.am b/doc/Makefile.am
index e5557ff..95e03d9 100644
--- a/doc/Makefile.am
+++ b/doc/Makefile.am
@@ -35,7 +35,7 @@
gtkdoc-mkdb --module=libxml --source-dir=$(DOC_SOURCE_DIR)
html:
- if ! test -d html ; then mkdir html ; fi
+ if test -n -d html ; then mkdir html ; fi
-cd html && gtkdoc-mkhtml libxml ../$(DOC_MAIN_SGML_FILE)
clean-local:
diff --git a/include/libxml/xmlwin32version.h b/include/libxml/xmlwin32version.h
index df11fed..5ae4ee8 100644
--- a/include/libxml/xmlwin32version.h
+++ b/include/libxml/xmlwin32version.h
@@ -68,6 +68,15 @@
#endif
/**
+ * LIBXML_THREADS_ENABLED:
+ *
+ * Whether the thread support is configured in
+ */
+#if 0
+#define LIBXML_THREAD_ENABLED
+#endif
+
+/**
* LIBXML_FTP_ENABLED:
*
* Whether the FTP support is configured in
diff --git a/include/libxml/xmlwin32version.h.in b/include/libxml/xmlwin32version.h.in
index 41d5b14..614667f 100644
--- a/include/libxml/xmlwin32version.h.in
+++ b/include/libxml/xmlwin32version.h.in
@@ -68,6 +68,15 @@
#endif
/**
+ * LIBXML_THREADS_ENABLED:
+ *
+ * Whether the thread support is configured in
+ */
+#if 0
+#define LIBXML_THREAD_ENABLED
+#endif
+
+/**
* LIBXML_FTP_ENABLED:
*
* Whether the FTP support is configured in
diff --git a/include/win32config.h b/include/win32config.h
index 7b09d8a..3e56217 100644
--- a/include/win32config.h
+++ b/include/win32config.h
@@ -144,5 +144,12 @@
#define ATTRIBUTE_UNUSED
#endif
+/* Define this if you want to use Windows native thread implementation.
+ Undefine it if you wish to use another, pthreads for example. Note
+ that this alone does not enable threads, just specifies the
+ threading model. Threading support is activated in xmlversion.h by
+ defining LIBXML_THREAD_ENABLE. */
+#define HAVE_WIN32_THREADS
+
#endif /* __LIBXML_WIN32_CONFIG__ */
diff --git a/testThreadsWin32.c b/testThreadsWin32.c
new file mode 100644
index 0000000..111e007
--- /dev/null
+++ b/testThreadsWin32.c
@@ -0,0 +1,146 @@
+#include <stdlib.h>
+#include <stdio.h>
+#include "libxml.h"
+
+#if defined(LIBXML_THREAD_ENABLED) && defined(LIBXML_CATALOG_ENABLED)
+#include <libxml/globals.h>
+#include <libxml/threads.h>
+#include <libxml/parser.h>
+#include <libxml/catalog.h>
+#include <windows.h>
+#include <string.h>
+#include <assert.h>
+
+#define MAX_ARGC 20
+#define TEST_REPEAT_COUNT 500
+
+static HANDLE tid[MAX_ARGC];
+
+static const char *catalog = "test/threads/complex.xml";
+static char *testfiles[] = {
+ "test/threads/abc.xml",
+ "test/threads/acb.xml",
+ "test/threads/bac.xml",
+ "test/threads/bca.xml",
+ "test/threads/cab.xml",
+ "test/threads/cba.xml",
+ "test/threads/invalid.xml",
+};
+
+const char *Okay = "OK";
+const char *Failed = "Failed";
+
+#ifndef xmlDoValidityCheckingDefaultValue
+#error xmlDoValidityCheckingDefaultValue is not a macro
+#endif
+#ifndef xmlGenericErrorContext
+#error xmlGenericErrorContext is not a macro
+#endif
+
+static DWORD WINAPI
+thread_specific_data(void *private_data)
+{
+ xmlDocPtr myDoc;
+ const char *filename = (const char *) private_data;
+ int okay = 1;
+
+ if (!strcmp(filename, "test/threads/invalid.xml")) {
+ xmlDoValidityCheckingDefaultValue = 0;
+ xmlGenericErrorContext = stdout;
+ } else {
+ xmlDoValidityCheckingDefaultValue = 1;
+ xmlGenericErrorContext = stderr;
+ }
+ myDoc = xmlParseFile(filename);
+ if (myDoc) {
+ xmlFreeDoc(myDoc);
+ } else {
+ printf("parse failed\n");
+ okay = 0;
+ }
+ if (!strcmp(filename, "test/threads/invalid.xml")) {
+ if (xmlDoValidityCheckingDefaultValue != 0) {
+ printf("ValidityCheckingDefaultValue override failed\n");
+ okay = 0;
+ }
+ if (xmlGenericErrorContext != stdout) {
+ printf("xmlGenericErrorContext override failed\n");
+ okay = 0;
+ }
+ } else {
+ if (xmlDoValidityCheckingDefaultValue != 1) {
+ printf("ValidityCheckingDefaultValue override failed\n");
+ okay = 0;
+ }
+ if (xmlGenericErrorContext != stderr) {
+ printf("xmlGenericErrorContext override failed\n");
+ okay = 0;
+ }
+ }
+ if (okay == 0)
+ return ((DWORD) Failed);
+ return ((DWORD) Okay);
+}
+
+int
+main()
+{
+ unsigned int i, repeat;
+ unsigned int num_threads = sizeof(testfiles) / sizeof(testfiles[0]);
+ DWORD results[MAX_ARGC];
+ BOOL ret;
+
+ xmlInitParser();
+ for (repeat = 0;repeat < TEST_REPEAT_COUNT;repeat++)
+ {
+ xmlLoadCatalog(catalog);
+
+ for (i = 0; i < num_threads; i++)
+ {
+ results[i] = 0;
+ tid[i] = (HANDLE) -1;
+ }
+
+ for (i = 0; i < num_threads; i++)
+ {
+ DWORD useless;
+ tid[i] = CreateThread (NULL, 0, thread_specific_data, testfiles[i], 0, &useless);
+ if (tid[i] == NULL)
+ {
+ perror("CreateThread");
+ exit(1);
+ }
+ }
+
+ if (WaitForMultipleObjects (num_threads, tid, TRUE, INFINITE) == WAIT_FAILED) perror ("WaitForMultipleObjects failed");
+
+ for (i = 0; i < num_threads; i++)
+ {
+ ret = GetExitCodeThread (tid[i], &results[i]);
+ if (ret == 0)
+ {
+ perror("GetExitCodeThread");
+ exit(1);
+ }
+ CloseHandle (tid[i]);
+ }
+
+ xmlCatalogCleanup();
+ for (i = 0; i < num_threads; i++)
+ if (results[i] != (DWORD) Okay) printf("Thread %d handling %s failed\n", i, testfiles[i]);
+ }
+
+ xmlCleanupParser();
+ xmlMemoryDump();
+
+ return (0);
+}
+
+#else /* !LIBXML_THREADS_ENABLED */
+int
+main()
+{
+ fprintf(stderr, "libxml was not compiled with thread or catalog support\n");
+ return (0);
+}
+#endif
diff --git a/threads.c b/threads.c
index be3b131..dc69f47 100644
--- a/threads.c
+++ b/threads.c
@@ -26,6 +26,12 @@
#ifdef HAVE_PTHREAD_H
#include <pthread.h>
#endif
+#ifdef HAVE_WIN32_THREADS
+#include <windows.h>
+#ifndef _MSC_VER
+#include <process.h>
+#endif
+#endif
#if defined(SOLARIS)
#include <note.h>
@@ -45,6 +51,8 @@
struct _xmlMutex {
#ifdef HAVE_PTHREAD_H
pthread_mutex_t lock;
+#elif defined HAVE_WIN32_THREADS
+ HANDLE mutex;
#else
int empty;
#endif
@@ -60,6 +68,9 @@
unsigned int waiters;
pthread_t tid;
pthread_cond_t cv;
+#elif defined HAVE_WIN32_THREADS
+ CRITICAL_SECTION cs;
+ unsigned int count;
#else
int empty;
#endif
@@ -74,7 +85,16 @@
static pthread_key_t globalkey;
static pthread_t mainthread;
static pthread_once_t once_control = PTHREAD_ONCE_INIT;
-#endif
+#elif defined HAVE_WIN32_THREADS
+#ifdef _MSC_VER
+static __declspec (thread) xmlGlobalState tlstate;
+static __declspec (thread) int tlstate_inited = 0;
+#else
+static DWORD globalkey;
+#endif /* _MSC_VER */
+static DWORD mainthread;
+static int run_once_init = 1;
+#endif /* HAVE_WIN32_THREADS */
static xmlRMutexPtr xmlLibraryLock = NULL;
static void xmlOnceInit(void);
@@ -95,6 +115,8 @@
return (NULL);
#ifdef HAVE_PTHREAD_H
pthread_mutex_init(&tok->lock, NULL);
+#elif defined HAVE_WIN32_THREADS
+ tok->mutex = CreateMutex (NULL, FALSE, NULL);
#endif
return (tok);
}
@@ -111,6 +133,8 @@
{
#ifdef HAVE_PTHREAD_H
pthread_mutex_destroy(&tok->lock);
+#elif defined HAVE_WIN32_THREADS
+ CloseHandle (tok->mutex);
#endif
free(tok);
}
@@ -126,6 +150,8 @@
{
#ifdef HAVE_PTHREAD_H
pthread_mutex_lock(&tok->lock);
+#elif defined HAVE_WIN32_THREADS
+ WaitForSingleObject (tok->mutex, INFINITE);
#endif
}
@@ -141,6 +167,8 @@
{
#ifdef HAVE_PTHREAD_H
pthread_mutex_unlock(&tok->lock);
+#elif defined HAVE_WIN32_THREADS
+ ReleaseMutex (tok->mutex);
#endif
}
@@ -165,6 +193,9 @@
pthread_mutex_init(&tok->lock, NULL);
tok->held = 0;
tok->waiters = 0;
+#elif defined HAVE_WIN32_THREADS
+ InitializeCriticalSection (&tok->cs);
+ tok->count = 0;
#endif
return (tok);
}
@@ -181,6 +212,8 @@
{
#ifdef HAVE_PTHREAD_H
pthread_mutex_destroy(&tok->lock);
+#elif defined HAVE_WIN32_THREADS
+ DeleteCriticalSection (&tok->cs);
#endif
free(tok);
}
@@ -211,6 +244,9 @@
tok->tid = pthread_self();
tok->held = 1;
pthread_mutex_unlock(&tok->lock);
+#elif defined HAVE_WIN32_THREADS
+ EnterCriticalSection (&tok->cs);
+ ++tok->count;
#endif
}
@@ -232,6 +268,8 @@
tok->tid = 0;
}
pthread_mutex_unlock(&tok->lock);
+#elif defined HAVE_WIN32_THREADS
+ if (!--tok->count) LeaveCriticalSection (&tok->cs);
#endif
}
@@ -242,6 +280,7 @@
************************************************************************/
#ifdef LIBXML_THREAD_ENABLED
+#ifndef _MSC_VER
/**
* xmlFreeGlobalState:
* @state: a thread global state
@@ -277,6 +316,7 @@
xmlInitializeGlobalState(gs);
return (gs);
}
+#endif /* _MSC_VER */
#endif /* LIBXML_THREAD_ENABLED */
@@ -287,6 +327,27 @@
*
* Returns the thread global state or NULL in case of error
*/
+
+#ifdef HAVE_WIN32_THREADS
+#ifndef _MSC_VER
+typedef struct _xmlGlobalStateCleanupHelperParams
+{
+ HANDLE thread;
+ void *memory;
+} xmlGlobalStateCleanupHelperParams;
+
+void __cdecl xmlGlobalStateCleanupHelper (void *p)
+{
+ xmlGlobalStateCleanupHelperParams *params = (xmlGlobalStateCleanupHelperParams *) p;
+ WaitForSingleObject (params->thread, INFINITE);
+ CloseHandle (params->thread);
+ xmlFreeGlobalState (params->memory);
+ free (params);
+ _endthread ();
+}
+#endif /* _MSC_VER */
+#endif /* HAVE_WIN32_THREADS */
+
xmlGlobalStatePtr
xmlGetGlobalState(void)
{
@@ -303,12 +364,40 @@
return (tsd);
}
return (globalval);
+#elif defined HAVE_WIN32_THREADS
+#ifdef _MSC_VER
+ if (!tlstate_inited)
+ {
+ tlstate_inited = 1;
+ xmlInitializeGlobalState (&tlstate);
+ }
+
+ return &tlstate;
+#else /* !_MSC_VER */
+ xmlGlobalState *globalval;
+
+ if (run_once_init) { run_once_init = 0; xmlOnceInit (); }
+
+ if ((globalval = (xmlGlobalState *) TlsGetValue (globalkey)) == NULL)
+ {
+ xmlGlobalState *tsd = xmlNewGlobalState();
+ xmlGlobalStateCleanupHelperParams *p = (xmlGlobalStateCleanupHelperParams *) malloc (sizeof (xmlGlobalStateCleanupHelperParams));
+
+ p->memory = tsd;
+ DuplicateHandle (GetCurrentProcess (), GetCurrentThread (), GetCurrentProcess (), &p->thread, 0, TRUE, DUPLICATE_SAME_ACCESS);
+
+ TlsSetValue (globalkey, tsd);
+ _beginthread (xmlGlobalStateCleanupHelper, 0, p);
+
+ return (tsd);
+ }
+ return (globalval);
+#endif /* _MSC_VER */
#else
return(NULL);
#endif
}
-
/************************************************************************
* *
* Library wide thread interfaces *
@@ -327,6 +416,8 @@
{
#ifdef HAVE_PTHREAD_H
return((int) pthread_self());
+#elif defined HAVE_WIN32_THREADS
+ return GetCurrentThreadId ();
#else
return((int) 0);
#endif
@@ -344,6 +435,8 @@
{
#ifdef HAVE_PTHREAD_H
pthread_once(&once_control, xmlOnceInit);
+#elif defined HAVE_WIN32_THREADS
+ if (run_once_init) { run_once_init = 0; xmlOnceInit (); }
#endif
#ifdef DEBUG_THREADS
@@ -351,6 +444,8 @@
#endif
#ifdef HAVE_PTHREAD_H
return(mainthread == pthread_self());
+#elif defined HAVE_WIN32_THREADS
+ return (mainthread == GetCurrentThreadId ());
#else
return(1);
#endif
@@ -428,5 +523,10 @@
#ifdef HAVE_PTHREAD_H
(void) pthread_key_create(&globalkey, xmlFreeGlobalState);
mainthread = pthread_self();
+#elif defined HAVE_WIN32_THREADS
+#ifndef _MSC_VER
+ globalkey = TlsAlloc ();
+#endif /* _MSC_VER */
+ mainthread = GetCurrentThreadId ();
#endif
}