1/**
2 * threads.c: set of generic threading related routines
3 *
4 * See Copyright for the status of this software.
5 *
6 * Gary Pennington <Gary.Pennington@uk.sun.com>
7 * daniel@veillard.com
8 */
9
10#define IN_LIBXML
11#include "libxml.h"
12
13#include <string.h>
14
15#include <libxml/threads.h>
16#include <libxml/globals.h>
17
18#ifdef HAVE_SYS_TYPES_H
19#include <sys/types.h>
20#endif
21#ifdef HAVE_UNISTD_H
22#include <unistd.h>
23#endif
24#ifdef HAVE_STDLIB_H
25#include <stdlib.h>
26#endif
27#ifdef HAVE_PTHREAD_H
28#include <pthread.h>
29#elif defined HAVE_WIN32_THREADS
30#define WIN32_LEAN_AND_MEAN
31#include <windows.h>
32#ifndef HAVE_COMPILER_TLS
33#include <process.h>
34#endif
35#endif
36
37#ifdef HAVE_BEOS_THREADS
38#include <OS.h>
39#include <TLS.h>
40#endif
41
42#if defined(SOLARIS)
43#include <note.h>
44#endif
45
46/* #define DEBUG_THREADS */
47
48#ifdef HAVE_PTHREAD_H
49
50#if defined(__GNUC__) && (__GNUC__ * 100 + __GNUC_MINOR__ >= 303) && \
51    defined(__GLIBC__) && defined(__linux__)
52
53static int libxml_is_threaded = -1;
54
55#define XML_PTHREAD_WEAK
56
57#pragma weak pthread_once
58#pragma weak pthread_getspecific
59#pragma weak pthread_setspecific
60#pragma weak pthread_key_create
61#pragma weak pthread_key_delete
62#pragma weak pthread_mutex_init
63#pragma weak pthread_mutex_destroy
64#pragma weak pthread_mutex_lock
65#pragma weak pthread_mutex_unlock
66#pragma weak pthread_cond_init
67#pragma weak pthread_cond_destroy
68#pragma weak pthread_cond_wait
69#pragma weak pthread_equal
70#pragma weak pthread_self
71#pragma weak pthread_key_create
72#pragma weak pthread_key_delete
73#pragma weak pthread_cond_signal
74
75#else /* __GNUC__, __GLIBC__, __linux__ */
76
77static int libxml_is_threaded = 1;
78
79#endif /* __GNUC__, __GLIBC__, __linux__ */
80
81#endif /* HAVE_PTHREAD_H */
82
83/*
84 * TODO: this module still uses malloc/free and not xmlMalloc/xmlFree
85 *       to avoid some crazyness since xmlMalloc/xmlFree may actually
86 *       be hosted on allocated blocks needing them for the allocation ...
87 */
88
89/*
90 * xmlMutex are a simple mutual exception locks
91 */
92struct _xmlMutex {
93#ifdef HAVE_PTHREAD_H
94    pthread_mutex_t lock;
95#elif defined HAVE_WIN32_THREADS
96    HANDLE mutex;
97#elif defined HAVE_BEOS_THREADS
98    sem_id sem;
99    thread_id tid;
100#else
101    int empty;
102#endif
103};
104
105/*
106 * xmlRMutex are reentrant mutual exception locks
107 */
108struct _xmlRMutex {
109#ifdef HAVE_PTHREAD_H
110    pthread_mutex_t lock;
111    unsigned int held;
112    unsigned int waiters;
113    pthread_t tid;
114    pthread_cond_t cv;
115#elif defined HAVE_WIN32_THREADS
116    CRITICAL_SECTION cs;
117    unsigned int count;
118#elif defined HAVE_BEOS_THREADS
119    xmlMutexPtr lock;
120    thread_id tid;
121    int32 count;
122#else
123    int empty;
124#endif
125};
126
127/*
128 * This module still has some internal static data.
129 *   - xmlLibraryLock a global lock
130 *   - globalkey used for per-thread data
131 */
132
133#ifdef HAVE_PTHREAD_H
134static pthread_key_t globalkey;
135static pthread_t mainthread;
136static pthread_once_t once_control = PTHREAD_ONCE_INIT;
137static pthread_once_t once_control_init = PTHREAD_ONCE_INIT;
138static pthread_mutex_t global_init_lock = PTHREAD_MUTEX_INITIALIZER;
139#elif defined HAVE_WIN32_THREADS
140#if defined(HAVE_COMPILER_TLS)
141static __declspec(thread) xmlGlobalState tlstate;
142static __declspec(thread) int tlstate_inited = 0;
143#else /* HAVE_COMPILER_TLS */
144static DWORD globalkey = TLS_OUT_OF_INDEXES;
145#endif /* HAVE_COMPILER_TLS */
146static DWORD mainthread;
147static struct {
148    DWORD done;
149    LONG control;
150} run_once = { 0, 0};
151static volatile LPCRITICAL_SECTION global_init_lock = NULL;
152
153/* endif HAVE_WIN32_THREADS */
154#elif defined HAVE_BEOS_THREADS
155int32 globalkey = 0;
156thread_id mainthread = 0;
157int32 run_once_init = 0;
158static int32 global_init_lock = -1;
159static vint32 global_init_count = 0;
160#endif
161
162static xmlRMutexPtr xmlLibraryLock = NULL;
163
164#ifdef LIBXML_THREAD_ENABLED
165static void xmlOnceInit(void);
166#endif
167
168/**
169 * xmlNewMutex:
170 *
171 * xmlNewMutex() is used to allocate a libxml2 token struct for use in
172 * synchronizing access to data.
173 *
174 * Returns a new simple mutex pointer or NULL in case of error
175 */
176xmlMutexPtr
177xmlNewMutex(void)
178{
179    xmlMutexPtr tok;
180
181    if ((tok = malloc(sizeof(xmlMutex))) == NULL)
182        return (NULL);
183#ifdef HAVE_PTHREAD_H
184    if (libxml_is_threaded != 0)
185        pthread_mutex_init(&tok->lock, NULL);
186#elif defined HAVE_WIN32_THREADS
187    tok->mutex = CreateMutex(NULL, FALSE, NULL);
188#elif defined HAVE_BEOS_THREADS
189    if ((tok->sem = create_sem(1, "xmlMutex")) < B_OK) {
190        free(tok);
191        return NULL;
192    }
193    tok->tid = -1;
194#endif
195    return (tok);
196}
197
198/**
199 * xmlFreeMutex:
200 * @tok:  the simple mutex
201 *
202 * xmlFreeMutex() is used to reclaim resources associated with a libxml2 token
203 * struct.
204 */
205void
206xmlFreeMutex(xmlMutexPtr tok)
207{
208    if (tok == NULL)
209        return;
210
211#ifdef HAVE_PTHREAD_H
212    if (libxml_is_threaded != 0)
213        pthread_mutex_destroy(&tok->lock);
214#elif defined HAVE_WIN32_THREADS
215    CloseHandle(tok->mutex);
216#elif defined HAVE_BEOS_THREADS
217    delete_sem(tok->sem);
218#endif
219    free(tok);
220}
221
222/**
223 * xmlMutexLock:
224 * @tok:  the simple mutex
225 *
226 * xmlMutexLock() is used to lock a libxml2 token.
227 */
228void
229xmlMutexLock(xmlMutexPtr tok)
230{
231    if (tok == NULL)
232        return;
233#ifdef HAVE_PTHREAD_H
234    if (libxml_is_threaded != 0)
235        pthread_mutex_lock(&tok->lock);
236#elif defined HAVE_WIN32_THREADS
237    WaitForSingleObject(tok->mutex, INFINITE);
238#elif defined HAVE_BEOS_THREADS
239    if (acquire_sem(tok->sem) != B_NO_ERROR) {
240#ifdef DEBUG_THREADS
241        xmlGenericError(xmlGenericErrorContext,
242                        "xmlMutexLock():BeOS:Couldn't aquire semaphore\n");
243#endif
244    }
245    tok->tid = find_thread(NULL);
246#endif
247
248}
249
250/**
251 * xmlMutexUnlock:
252 * @tok:  the simple mutex
253 *
254 * xmlMutexUnlock() is used to unlock a libxml2 token.
255 */
256void
257xmlMutexUnlock(xmlMutexPtr tok)
258{
259    if (tok == NULL)
260        return;
261#ifdef HAVE_PTHREAD_H
262    if (libxml_is_threaded != 0)
263        pthread_mutex_unlock(&tok->lock);
264#elif defined HAVE_WIN32_THREADS
265    ReleaseMutex(tok->mutex);
266#elif defined HAVE_BEOS_THREADS
267    if (tok->tid == find_thread(NULL)) {
268        tok->tid = -1;
269        release_sem(tok->sem);
270    }
271#endif
272}
273
274/**
275 * xmlNewRMutex:
276 *
277 * xmlRNewMutex() is used to allocate a reentrant mutex for use in
278 * synchronizing access to data. token_r is a re-entrant lock and thus useful
279 * for synchronizing access to data structures that may be manipulated in a
280 * recursive fashion.
281 *
282 * Returns the new reentrant mutex pointer or NULL in case of error
283 */
284xmlRMutexPtr
285xmlNewRMutex(void)
286{
287    xmlRMutexPtr tok;
288
289    if ((tok = malloc(sizeof(xmlRMutex))) == NULL)
290        return (NULL);
291#ifdef HAVE_PTHREAD_H
292    if (libxml_is_threaded != 0) {
293        pthread_mutex_init(&tok->lock, NULL);
294        tok->held = 0;
295        tok->waiters = 0;
296        pthread_cond_init(&tok->cv, NULL);
297    }
298#elif defined HAVE_WIN32_THREADS
299    InitializeCriticalSection(&tok->cs);
300    tok->count = 0;
301#elif defined HAVE_BEOS_THREADS
302    if ((tok->lock = xmlNewMutex()) == NULL) {
303        free(tok);
304        return NULL;
305    }
306    tok->count = 0;
307#endif
308    return (tok);
309}
310
311/**
312 * xmlFreeRMutex:
313 * @tok:  the reentrant mutex
314 *
315 * xmlRFreeMutex() is used to reclaim resources associated with a
316 * reentrant mutex.
317 */
318void
319xmlFreeRMutex(xmlRMutexPtr tok ATTRIBUTE_UNUSED)
320{
321    if (tok == NULL)
322        return;
323#ifdef HAVE_PTHREAD_H
324    if (libxml_is_threaded != 0) {
325        pthread_mutex_destroy(&tok->lock);
326        pthread_cond_destroy(&tok->cv);
327    }
328#elif defined HAVE_WIN32_THREADS
329    DeleteCriticalSection(&tok->cs);
330#elif defined HAVE_BEOS_THREADS
331    xmlFreeMutex(tok->lock);
332#endif
333    free(tok);
334}
335
336/**
337 * xmlRMutexLock:
338 * @tok:  the reentrant mutex
339 *
340 * xmlRMutexLock() is used to lock a libxml2 token_r.
341 */
342void
343xmlRMutexLock(xmlRMutexPtr tok)
344{
345    if (tok == NULL)
346        return;
347#ifdef HAVE_PTHREAD_H
348    if (libxml_is_threaded == 0)
349        return;
350
351    pthread_mutex_lock(&tok->lock);
352    if (tok->held) {
353        if (pthread_equal(tok->tid, pthread_self())) {
354            tok->held++;
355            pthread_mutex_unlock(&tok->lock);
356            return;
357        } else {
358            tok->waiters++;
359            while (tok->held)
360                pthread_cond_wait(&tok->cv, &tok->lock);
361            tok->waiters--;
362        }
363    }
364    tok->tid = pthread_self();
365    tok->held = 1;
366    pthread_mutex_unlock(&tok->lock);
367#elif defined HAVE_WIN32_THREADS
368    EnterCriticalSection(&tok->cs);
369    tok->count++;
370#elif defined HAVE_BEOS_THREADS
371    if (tok->lock->tid == find_thread(NULL)) {
372        tok->count++;
373        return;
374    } else {
375        xmlMutexLock(tok->lock);
376        tok->count = 1;
377    }
378#endif
379}
380
381/**
382 * xmlRMutexUnlock:
383 * @tok:  the reentrant mutex
384 *
385 * xmlRMutexUnlock() is used to unlock a libxml2 token_r.
386 */
387void
388xmlRMutexUnlock(xmlRMutexPtr tok ATTRIBUTE_UNUSED)
389{
390    if (tok == NULL)
391        return;
392#ifdef HAVE_PTHREAD_H
393    if (libxml_is_threaded == 0)
394        return;
395
396    pthread_mutex_lock(&tok->lock);
397    tok->held--;
398    if (tok->held == 0) {
399        if (tok->waiters)
400            pthread_cond_signal(&tok->cv);
401        memset(&tok->tid, 0, sizeof(tok->tid));
402    }
403    pthread_mutex_unlock(&tok->lock);
404#elif defined HAVE_WIN32_THREADS
405    if (tok->count > 0) {
406	tok->count--;
407        LeaveCriticalSection(&tok->cs);
408    }
409#elif defined HAVE_BEOS_THREADS
410    if (tok->lock->tid == find_thread(NULL)) {
411        tok->count--;
412        if (tok->count == 0) {
413            xmlMutexUnlock(tok->lock);
414        }
415        return;
416    }
417#endif
418}
419
420/**
421 * xmlGlobalInitMutexLock
422 *
423 * Makes sure that the global initialization mutex is initialized and
424 * locks it.
425 */
426void
427__xmlGlobalInitMutexLock(void)
428{
429    /* Make sure the global init lock is initialized and then lock it. */
430#ifdef HAVE_PTHREAD_H
431    /* The mutex is statically initialized, so we just lock it. */
432#ifdef XML_PTHREAD_WEAK
433    if (pthread_mutex_lock == NULL)
434        return;
435#endif /* XML_PTHREAD_WEAK */
436    pthread_mutex_lock(&global_init_lock);
437#elif defined HAVE_WIN32_THREADS
438    LPCRITICAL_SECTION cs;
439
440    /* Create a new critical section */
441    if (global_init_lock == NULL) {
442        cs = malloc(sizeof(CRITICAL_SECTION));
443        if (cs == NULL) {
444            xmlGenericError(xmlGenericErrorContext,
445                            "xmlGlobalInitMutexLock: out of memory\n");
446            return;
447        }
448        InitializeCriticalSection(cs);
449
450        /* Swap it into the global_init_lock */
451#ifdef InterlockedCompareExchangePointer
452        InterlockedCompareExchangePointer((void **) &global_init_lock,
453                                          cs, NULL);
454#else /* Use older void* version */
455        InterlockedCompareExchange((void **) &global_init_lock,
456                                   (void *) cs, NULL);
457#endif /* InterlockedCompareExchangePointer */
458
459        /* If another thread successfully recorded its critical
460         * section in the global_init_lock then discard the one
461         * allocated by this thread. */
462        if (global_init_lock != cs) {
463            DeleteCriticalSection(cs);
464            free(cs);
465        }
466    }
467
468    /* Lock the chosen critical section */
469    EnterCriticalSection(global_init_lock);
470#elif defined HAVE_BEOS_THREADS
471    int32 sem;
472
473    /* Allocate a new semaphore */
474    sem = create_sem(1, "xmlGlobalinitMutex");
475
476    while (global_init_lock == -1) {
477        if (atomic_add(&global_init_count, 1) == 0) {
478            global_init_lock = sem;
479        } else {
480            snooze(1);
481            atomic_add(&global_init_count, -1);
482        }
483    }
484
485    /* If another thread successfully recorded its critical
486     * section in the global_init_lock then discard the one
487     * allocated by this thread. */
488    if (global_init_lock != sem)
489        delete_sem(sem);
490
491    /* Acquire the chosen semaphore */
492    if (acquire_sem(global_init_lock) != B_NO_ERROR) {
493#ifdef DEBUG_THREADS
494        xmlGenericError(xmlGenericErrorContext,
495                        "xmlGlobalInitMutexLock():BeOS:Couldn't acquire semaphore\n");
496#endif
497    }
498#endif
499}
500
501void
502__xmlGlobalInitMutexUnlock(void)
503{
504#ifdef HAVE_PTHREAD_H
505#ifdef XML_PTHREAD_WEAK
506    if (pthread_mutex_unlock == NULL)
507        return;
508#endif /* XML_PTHREAD_WEAK */
509    pthread_mutex_unlock(&global_init_lock);
510#elif defined HAVE_WIN32_THREADS
511    if (global_init_lock != NULL) {
512	LeaveCriticalSection(global_init_lock);
513    }
514#elif defined HAVE_BEOS_THREADS
515    release_sem(global_init_lock);
516#endif
517}
518
519/**
520 * xmlGlobalInitMutexDestroy
521 *
522 * Makes sure that the global initialization mutex is destroyed before
523 * application termination.
524 */
525void
526__xmlGlobalInitMutexDestroy(void)
527{
528#ifdef HAVE_PTHREAD_H
529#elif defined HAVE_WIN32_THREADS
530    if (global_init_lock != NULL) {
531        DeleteCriticalSection(global_init_lock);
532        free(global_init_lock);
533        global_init_lock = NULL;
534    }
535#endif
536}
537
538/************************************************************************
539 *									*
540 *			Per thread global state handling		*
541 *									*
542 ************************************************************************/
543
544#ifdef LIBXML_THREAD_ENABLED
545#ifdef xmlLastError
546#undef xmlLastError
547#endif
548
549/**
550 * xmlFreeGlobalState:
551 * @state:  a thread global state
552 *
553 * xmlFreeGlobalState() is called when a thread terminates with a non-NULL
554 * global state. It is is used here to reclaim memory resources.
555 */
556static void
557xmlFreeGlobalState(void *state)
558{
559    xmlGlobalState *gs = (xmlGlobalState *) state;
560
561    /* free any memory allocated in the thread's xmlLastError */
562    xmlResetError(&(gs->xmlLastError));
563    free(state);
564}
565
566/**
567 * xmlNewGlobalState:
568 *
569 * xmlNewGlobalState() allocates a global state. This structure is used to
570 * hold all data for use by a thread when supporting backwards compatibility
571 * of libxml2 to pre-thread-safe behaviour.
572 *
573 * Returns the newly allocated xmlGlobalStatePtr or NULL in case of error
574 */
575static xmlGlobalStatePtr
576xmlNewGlobalState(void)
577{
578    xmlGlobalState *gs;
579
580    gs = malloc(sizeof(xmlGlobalState));
581    if (gs == NULL) {
582	xmlGenericError(xmlGenericErrorContext,
583			"xmlGetGlobalState: out of memory\n");
584        return (NULL);
585    }
586
587    memset(gs, 0, sizeof(xmlGlobalState));
588    xmlInitializeGlobalState(gs);
589    return (gs);
590}
591#endif /* LIBXML_THREAD_ENABLED */
592
593#ifdef HAVE_PTHREAD_H
594#elif defined HAVE_WIN32_THREADS
595#if !defined(HAVE_COMPILER_TLS)
596#if defined(LIBXML_STATIC) && !defined(LIBXML_STATIC_FOR_DLL)
597typedef struct _xmlGlobalStateCleanupHelperParams {
598    HANDLE thread;
599    void *memory;
600} xmlGlobalStateCleanupHelperParams;
601
602static void XMLCDECL
603xmlGlobalStateCleanupHelper(void *p)
604{
605    xmlGlobalStateCleanupHelperParams *params =
606        (xmlGlobalStateCleanupHelperParams *) p;
607    WaitForSingleObject(params->thread, INFINITE);
608    CloseHandle(params->thread);
609    xmlFreeGlobalState(params->memory);
610    free(params);
611    _endthread();
612}
613#else /* LIBXML_STATIC && !LIBXML_STATIC_FOR_DLL */
614
615typedef struct _xmlGlobalStateCleanupHelperParams {
616    void *memory;
617    struct _xmlGlobalStateCleanupHelperParams *prev;
618    struct _xmlGlobalStateCleanupHelperParams *next;
619} xmlGlobalStateCleanupHelperParams;
620
621static xmlGlobalStateCleanupHelperParams *cleanup_helpers_head = NULL;
622static CRITICAL_SECTION cleanup_helpers_cs;
623
624#endif /* LIBXMLSTATIC && !LIBXML_STATIC_FOR_DLL */
625#endif /* HAVE_COMPILER_TLS */
626#endif /* HAVE_WIN32_THREADS */
627
628#if defined HAVE_BEOS_THREADS
629
630/**
631 * xmlGlobalStateCleanup:
632 * @data: unused parameter
633 *
634 * Used for Beos only
635 */
636void
637xmlGlobalStateCleanup(void *data)
638{
639    void *globalval = tls_get(globalkey);
640
641    if (globalval != NULL)
642        xmlFreeGlobalState(globalval);
643}
644#endif
645
646/**
647 * xmlGetGlobalState:
648 *
649 * xmlGetGlobalState() is called to retrieve the global state for a thread.
650 *
651 * Returns the thread global state or NULL in case of error
652 */
653xmlGlobalStatePtr
654xmlGetGlobalState(void)
655{
656#ifdef HAVE_PTHREAD_H
657    xmlGlobalState *globalval;
658
659    if (libxml_is_threaded == 0)
660        return (NULL);
661
662    pthread_once(&once_control, xmlOnceInit);
663
664    if ((globalval = (xmlGlobalState *)
665         pthread_getspecific(globalkey)) == NULL) {
666        xmlGlobalState *tsd = xmlNewGlobalState();
667	if (tsd == NULL)
668	    return(NULL);
669
670        pthread_setspecific(globalkey, tsd);
671        return (tsd);
672    }
673    return (globalval);
674#elif defined HAVE_WIN32_THREADS
675#if defined(HAVE_COMPILER_TLS)
676    if (!tlstate_inited) {
677        tlstate_inited = 1;
678        xmlInitializeGlobalState(&tlstate);
679    }
680    return &tlstate;
681#else /* HAVE_COMPILER_TLS */
682    xmlGlobalState *globalval;
683    xmlGlobalStateCleanupHelperParams *p;
684
685    xmlOnceInit();
686#if defined(LIBXML_STATIC) && !defined(LIBXML_STATIC_FOR_DLL)
687    globalval = (xmlGlobalState *) TlsGetValue(globalkey);
688#else
689    p = (xmlGlobalStateCleanupHelperParams *) TlsGetValue(globalkey);
690    globalval = (xmlGlobalState *) (p ? p->memory : NULL);
691#endif
692    if (globalval == NULL) {
693        xmlGlobalState *tsd = xmlNewGlobalState();
694
695        if (tsd == NULL)
696	    return(NULL);
697        p = (xmlGlobalStateCleanupHelperParams *)
698            malloc(sizeof(xmlGlobalStateCleanupHelperParams));
699	if (p == NULL) {
700            xmlGenericError(xmlGenericErrorContext,
701                            "xmlGetGlobalState: out of memory\n");
702            xmlFreeGlobalState(tsd);
703	    return(NULL);
704	}
705        p->memory = tsd;
706#if defined(LIBXML_STATIC) && !defined(LIBXML_STATIC_FOR_DLL)
707        DuplicateHandle(GetCurrentProcess(), GetCurrentThread(),
708                        GetCurrentProcess(), &p->thread, 0, TRUE,
709                        DUPLICATE_SAME_ACCESS);
710        TlsSetValue(globalkey, tsd);
711        _beginthread(xmlGlobalStateCleanupHelper, 0, p);
712#else
713        EnterCriticalSection(&cleanup_helpers_cs);
714        if (cleanup_helpers_head != NULL) {
715            cleanup_helpers_head->prev = p;
716        }
717        p->next = cleanup_helpers_head;
718        p->prev = NULL;
719        cleanup_helpers_head = p;
720        TlsSetValue(globalkey, p);
721        LeaveCriticalSection(&cleanup_helpers_cs);
722#endif
723
724        return (tsd);
725    }
726    return (globalval);
727#endif /* HAVE_COMPILER_TLS */
728#elif defined HAVE_BEOS_THREADS
729    xmlGlobalState *globalval;
730
731    xmlOnceInit();
732
733    if ((globalval = (xmlGlobalState *) tls_get(globalkey)) == NULL) {
734        xmlGlobalState *tsd = xmlNewGlobalState();
735	if (tsd == NULL)
736	    return (NULL);
737
738        tls_set(globalkey, tsd);
739        on_exit_thread(xmlGlobalStateCleanup, NULL);
740        return (tsd);
741    }
742    return (globalval);
743#else
744    return (NULL);
745#endif
746}
747
748/************************************************************************
749 *									*
750 *			Library wide thread interfaces			*
751 *									*
752 ************************************************************************/
753
754/**
755 * xmlGetThreadId:
756 *
757 * xmlGetThreadId() find the current thread ID number
758 * Note that this is likely to be broken on some platforms using pthreads
759 * as the specification doesn't mandate pthread_t to be an integer type
760 *
761 * Returns the current thread ID number
762 */
763int
764xmlGetThreadId(void)
765{
766#ifdef HAVE_PTHREAD_H
767    pthread_t id;
768    int ret;
769
770    if (libxml_is_threaded == 0)
771        return (0);
772    id = pthread_self();
773    /* horrible but preserves compat, see warning above */
774    memcpy(&ret, &id, sizeof(ret));
775    return (ret);
776#elif defined HAVE_WIN32_THREADS
777    return GetCurrentThreadId();
778#elif defined HAVE_BEOS_THREADS
779    return find_thread(NULL);
780#else
781    return ((int) 0);
782#endif
783}
784
785/**
786 * xmlIsMainThread:
787 *
788 * xmlIsMainThread() check whether the current thread is the main thread.
789 *
790 * Returns 1 if the current thread is the main thread, 0 otherwise
791 */
792int
793xmlIsMainThread(void)
794{
795#ifdef HAVE_PTHREAD_H
796    if (libxml_is_threaded == -1)
797        xmlInitThreads();
798    if (libxml_is_threaded == 0)
799        return (1);
800    pthread_once(&once_control, xmlOnceInit);
801#elif defined HAVE_WIN32_THREADS
802    xmlOnceInit();
803#elif defined HAVE_BEOS_THREADS
804    xmlOnceInit();
805#endif
806
807#ifdef DEBUG_THREADS
808    xmlGenericError(xmlGenericErrorContext, "xmlIsMainThread()\n");
809#endif
810#ifdef HAVE_PTHREAD_H
811    return (pthread_equal(mainthread,pthread_self()));
812#elif defined HAVE_WIN32_THREADS
813    return (mainthread == GetCurrentThreadId());
814#elif defined HAVE_BEOS_THREADS
815    return (mainthread == find_thread(NULL));
816#else
817    return (1);
818#endif
819}
820
821/**
822 * xmlLockLibrary:
823 *
824 * xmlLockLibrary() is used to take out a re-entrant lock on the libxml2
825 * library.
826 */
827void
828xmlLockLibrary(void)
829{
830#ifdef DEBUG_THREADS
831    xmlGenericError(xmlGenericErrorContext, "xmlLockLibrary()\n");
832#endif
833    xmlRMutexLock(xmlLibraryLock);
834}
835
836/**
837 * xmlUnlockLibrary:
838 *
839 * xmlUnlockLibrary() is used to release a re-entrant lock on the libxml2
840 * library.
841 */
842void
843xmlUnlockLibrary(void)
844{
845#ifdef DEBUG_THREADS
846    xmlGenericError(xmlGenericErrorContext, "xmlUnlockLibrary()\n");
847#endif
848    xmlRMutexUnlock(xmlLibraryLock);
849}
850
851/**
852 * xmlInitThreads:
853 *
854 * xmlInitThreads() is used to to initialize all the thread related
855 * data of the libxml2 library.
856 */
857void
858xmlInitThreads(void)
859{
860#ifdef HAVE_PTHREAD_H
861#ifdef XML_PTHREAD_WEAK
862    if (libxml_is_threaded == -1) {
863        if ((pthread_once != NULL) &&
864            (pthread_getspecific != NULL) &&
865            (pthread_setspecific != NULL) &&
866            (pthread_key_create != NULL) &&
867            (pthread_key_delete != NULL) &&
868            (pthread_mutex_init != NULL) &&
869            (pthread_mutex_destroy != NULL) &&
870            (pthread_mutex_lock != NULL) &&
871            (pthread_mutex_unlock != NULL) &&
872            (pthread_cond_init != NULL) &&
873            (pthread_cond_destroy != NULL) &&
874            (pthread_cond_wait != NULL) &&
875            (pthread_equal != NULL) &&
876            (pthread_self != NULL) &&
877            (pthread_cond_signal != NULL)) {
878            libxml_is_threaded = 1;
879
880/* fprintf(stderr, "Running multithreaded\n"); */
881        } else {
882
883/* fprintf(stderr, "Running without multithread\n"); */
884            libxml_is_threaded = 0;
885        }
886    }
887#endif /* XML_PTHREAD_WEAK */
888#elif defined(HAVE_WIN32_THREADS) && !defined(HAVE_COMPILER_TLS) && (!defined(LIBXML_STATIC) || defined(LIBXML_STATIC_FOR_DLL))
889    InitializeCriticalSection(&cleanup_helpers_cs);
890#endif
891}
892
893/**
894 * xmlCleanupThreads:
895 *
896 * xmlCleanupThreads() is used to to cleanup all the thread related
897 * data of the libxml2 library once processing has ended.
898 *
899 * WARNING: if your application is multithreaded or has plugin support
900 *          calling this may crash the application if another thread or
901 *          a plugin is still using libxml2. It's sometimes very hard to
902 *          guess if libxml2 is in use in the application, some libraries
903 *          or plugins may use it without notice. In case of doubt abstain
904 *          from calling this function or do it just before calling exit()
905 *          to avoid leak reports from valgrind !
906 */
907void
908xmlCleanupThreads(void)
909{
910#ifdef DEBUG_THREADS
911    xmlGenericError(xmlGenericErrorContext, "xmlCleanupThreads()\n");
912#endif
913#ifdef HAVE_PTHREAD_H
914    if (libxml_is_threaded != 0)
915        pthread_key_delete(globalkey);
916    once_control = once_control_init;
917#elif defined(HAVE_WIN32_THREADS) && !defined(HAVE_COMPILER_TLS) && (!defined(LIBXML_STATIC) || defined(LIBXML_STATIC_FOR_DLL))
918    if (globalkey != TLS_OUT_OF_INDEXES) {
919        xmlGlobalStateCleanupHelperParams *p;
920
921        EnterCriticalSection(&cleanup_helpers_cs);
922        p = cleanup_helpers_head;
923        while (p != NULL) {
924            xmlGlobalStateCleanupHelperParams *temp = p;
925
926            p = p->next;
927            xmlFreeGlobalState(temp->memory);
928            free(temp);
929        }
930        cleanup_helpers_head = 0;
931        LeaveCriticalSection(&cleanup_helpers_cs);
932        TlsFree(globalkey);
933        globalkey = TLS_OUT_OF_INDEXES;
934    }
935    DeleteCriticalSection(&cleanup_helpers_cs);
936#endif
937}
938
939#ifdef LIBXML_THREAD_ENABLED
940
941/**
942 * xmlOnceInit
943 *
944 * xmlOnceInit() is used to initialize the value of mainthread for use
945 * in other routines. This function should only be called using
946 * pthread_once() in association with the once_control variable to ensure
947 * that the function is only called once. See man pthread_once for more
948 * details.
949 */
950static void
951xmlOnceInit(void)
952{
953#ifdef HAVE_PTHREAD_H
954    (void) pthread_key_create(&globalkey, xmlFreeGlobalState);
955    mainthread = pthread_self();
956    __xmlInitializeDict();
957#elif defined(HAVE_WIN32_THREADS)
958    if (!run_once.done) {
959        if (InterlockedIncrement(&run_once.control) == 1) {
960#if !defined(HAVE_COMPILER_TLS)
961            globalkey = TlsAlloc();
962#endif
963            mainthread = GetCurrentThreadId();
964	    __xmlInitializeDict();
965            run_once.done = 1;
966        } else {
967            /* Another thread is working; give up our slice and
968             * wait until they're done. */
969            while (!run_once.done)
970                Sleep(0);
971        }
972    }
973#elif defined HAVE_BEOS_THREADS
974    if (atomic_add(&run_once_init, 1) == 0) {
975        globalkey = tls_allocate();
976        tls_set(globalkey, NULL);
977        mainthread = find_thread(NULL);
978	__xmlInitializeDict();
979    } else
980        atomic_add(&run_once_init, -1);
981#endif
982}
983#endif
984
985/**
986 * DllMain:
987 * @hinstDLL: handle to DLL instance
988 * @fdwReason: Reason code for entry
989 * @lpvReserved: generic pointer (depends upon reason code)
990 *
991 * Entry point for Windows library. It is being used to free thread-specific
992 * storage.
993 *
994 * Returns TRUE always
995 */
996#ifdef HAVE_PTHREAD_H
997#elif defined(HAVE_WIN32_THREADS) && !defined(HAVE_COMPILER_TLS) && (!defined(LIBXML_STATIC) || defined(LIBXML_STATIC_FOR_DLL))
998#if defined(LIBXML_STATIC_FOR_DLL)
999int XMLCALL
1000xmlDllMain(ATTRIBUTE_UNUSED void *hinstDLL, unsigned long fdwReason,
1001           ATTRIBUTE_UNUSED void *lpvReserved)
1002#else
1003/* declare to avoid "no previous prototype for 'DllMain'" warning */
1004/* Note that we do NOT want to include this function declaration in
1005   a public header because it's meant to be called by Windows itself,
1006   not a program that uses this library.  This also has to be exported. */
1007
1008XMLPUBFUN BOOL WINAPI
1009DllMain (HINSTANCE hinstDLL,
1010         DWORD     fdwReason,
1011         LPVOID    lpvReserved);
1012
1013BOOL WINAPI
1014DllMain(ATTRIBUTE_UNUSED HINSTANCE hinstDLL, DWORD fdwReason,
1015        ATTRIBUTE_UNUSED LPVOID lpvReserved)
1016#endif
1017{
1018    switch (fdwReason) {
1019        case DLL_THREAD_DETACH:
1020            if (globalkey != TLS_OUT_OF_INDEXES) {
1021                xmlGlobalState *globalval = NULL;
1022                xmlGlobalStateCleanupHelperParams *p =
1023                    (xmlGlobalStateCleanupHelperParams *)
1024                    TlsGetValue(globalkey);
1025                globalval = (xmlGlobalState *) (p ? p->memory : NULL);
1026                if (globalval) {
1027                    xmlFreeGlobalState(globalval);
1028                    TlsSetValue(globalkey, NULL);
1029                }
1030                if (p) {
1031                    EnterCriticalSection(&cleanup_helpers_cs);
1032                    if (p == cleanup_helpers_head)
1033                        cleanup_helpers_head = p->next;
1034                    else
1035                        p->prev->next = p->next;
1036                    if (p->next != NULL)
1037                        p->next->prev = p->prev;
1038                    LeaveCriticalSection(&cleanup_helpers_cs);
1039                    free(p);
1040                }
1041            }
1042            break;
1043    }
1044    return TRUE;
1045}
1046#endif
1047#define bottom_threads
1048#include "elfgcchack.h"
1049