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
 }