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