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 
53 static 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 
77 static 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 craziness 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  */
92 struct _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  */
108 struct _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
134 static pthread_key_t globalkey;
135 static pthread_t mainthread;
136 static pthread_once_t once_control = PTHREAD_ONCE_INIT;
137 static pthread_once_t once_control_init = PTHREAD_ONCE_INIT;
138 static pthread_mutex_t global_init_lock = PTHREAD_MUTEX_INITIALIZER;
139 #elif defined HAVE_WIN32_THREADS
140 #if defined(HAVE_COMPILER_TLS)
141 static __declspec(thread) xmlGlobalState tlstate;
142 static __declspec(thread) int tlstate_inited = 0;
143 #else /* HAVE_COMPILER_TLS */
144 static DWORD globalkey = TLS_OUT_OF_INDEXES;
145 #endif /* HAVE_COMPILER_TLS */
146 static DWORD mainthread;
147 static struct {
148     DWORD done;
149     LONG control;
150 } run_once = { 0, 0};
151 static volatile LPCRITICAL_SECTION global_init_lock = NULL;
152 
153 /* endif HAVE_WIN32_THREADS */
154 #elif defined HAVE_BEOS_THREADS
155 int32 globalkey = 0;
156 thread_id mainthread = 0;
157 int32 run_once_init = 0;
158 static int32 global_init_lock = -1;
159 static vint32 global_init_count = 0;
160 #endif
161 
162 static xmlRMutexPtr xmlLibraryLock = NULL;
163 
164 #ifdef LIBXML_THREAD_ENABLED
165 static 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  */
176 xmlMutexPtr
xmlNewMutex(void)177 xmlNewMutex(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  */
205 void
xmlFreeMutex(xmlMutexPtr tok)206 xmlFreeMutex(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  */
228 void
xmlMutexLock(xmlMutexPtr tok)229 xmlMutexLock(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 acquire 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  */
256 void
xmlMutexUnlock(xmlMutexPtr tok)257 xmlMutexUnlock(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  */
284 xmlRMutexPtr
xmlNewRMutex(void)285 xmlNewRMutex(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  */
318 void
xmlFreeRMutex(xmlRMutexPtr tok ATTRIBUTE_UNUSED)319 xmlFreeRMutex(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  */
342 void
xmlRMutexLock(xmlRMutexPtr tok)343 xmlRMutexLock(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  */
387 void
xmlRMutexUnlock(xmlRMutexPtr tok ATTRIBUTE_UNUSED)388 xmlRMutexUnlock(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  */
426 void
__xmlGlobalInitMutexLock(void)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 
501 void
__xmlGlobalInitMutexUnlock(void)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  */
525 void
__xmlGlobalInitMutexDestroy(void)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  */
556 static void
xmlFreeGlobalState(void * state)557 xmlFreeGlobalState(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  */
575 static xmlGlobalStatePtr
xmlNewGlobalState(void)576 xmlNewGlobalState(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)
597 typedef struct _xmlGlobalStateCleanupHelperParams {
598     HANDLE thread;
599     void *memory;
600 } xmlGlobalStateCleanupHelperParams;
601 
602 static void XMLCDECL
xmlGlobalStateCleanupHelper(void * p)603 xmlGlobalStateCleanupHelper(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 
615 typedef struct _xmlGlobalStateCleanupHelperParams {
616     void *memory;
617     struct _xmlGlobalStateCleanupHelperParams *prev;
618     struct _xmlGlobalStateCleanupHelperParams *next;
619 } xmlGlobalStateCleanupHelperParams;
620 
621 static xmlGlobalStateCleanupHelperParams *cleanup_helpers_head = NULL;
622 static 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  */
636 void
xmlGlobalStateCleanup(void * data)637 xmlGlobalStateCleanup(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  */
653 xmlGlobalStatePtr
xmlGetGlobalState(void)654 xmlGetGlobalState(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  */
763 int
xmlGetThreadId(void)764 xmlGetThreadId(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  */
792 int
xmlIsMainThread(void)793 xmlIsMainThread(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  */
827 void
xmlLockLibrary(void)828 xmlLockLibrary(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  */
842 void
xmlUnlockLibrary(void)843 xmlUnlockLibrary(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  */
857 void
xmlInitThreads(void)858 xmlInitThreads(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 #endif
889 }
890 
891 /**
892  * xmlCleanupThreads:
893  *
894  * xmlCleanupThreads() is used to to cleanup all the thread related
895  * data of the libxml2 library once processing has ended.
896  *
897  * WARNING: if your application is multithreaded or has plugin support
898  *          calling this may crash the application if another thread or
899  *          a plugin is still using libxml2. It's sometimes very hard to
900  *          guess if libxml2 is in use in the application, some libraries
901  *          or plugins may use it without notice. In case of doubt abstain
902  *          from calling this function or do it just before calling exit()
903  *          to avoid leak reports from valgrind !
904  */
905 void
xmlCleanupThreads(void)906 xmlCleanupThreads(void)
907 {
908 #ifdef DEBUG_THREADS
909     xmlGenericError(xmlGenericErrorContext, "xmlCleanupThreads()\n");
910 #endif
911 #ifdef HAVE_PTHREAD_H
912     if (libxml_is_threaded != 0)
913         pthread_key_delete(globalkey);
914     once_control = once_control_init;
915 #elif defined(HAVE_WIN32_THREADS) && !defined(HAVE_COMPILER_TLS) && (!defined(LIBXML_STATIC) || defined(LIBXML_STATIC_FOR_DLL))
916     if (globalkey != TLS_OUT_OF_INDEXES) {
917         xmlGlobalStateCleanupHelperParams *p;
918 
919         EnterCriticalSection(&cleanup_helpers_cs);
920         p = cleanup_helpers_head;
921         while (p != NULL) {
922             xmlGlobalStateCleanupHelperParams *temp = p;
923 
924             p = p->next;
925             xmlFreeGlobalState(temp->memory);
926             free(temp);
927         }
928         cleanup_helpers_head = 0;
929         LeaveCriticalSection(&cleanup_helpers_cs);
930         TlsFree(globalkey);
931         globalkey = TLS_OUT_OF_INDEXES;
932     }
933     DeleteCriticalSection(&cleanup_helpers_cs);
934 #endif
935 }
936 
937 #ifdef LIBXML_THREAD_ENABLED
938 
939 /**
940  * xmlOnceInit
941  *
942  * xmlOnceInit() is used to initialize the value of mainthread for use
943  * in other routines. This function should only be called using
944  * pthread_once() in association with the once_control variable to ensure
945  * that the function is only called once. See man pthread_once for more
946  * details.
947  */
948 static void
xmlOnceInit(void)949 xmlOnceInit(void)
950 {
951 #ifdef HAVE_PTHREAD_H
952     (void) pthread_key_create(&globalkey, xmlFreeGlobalState);
953     mainthread = pthread_self();
954     __xmlInitializeDict();
955 #elif defined(HAVE_WIN32_THREADS)
956     if (!run_once.done) {
957         if (InterlockedIncrement(&run_once.control) == 1) {
958 #if !defined(HAVE_COMPILER_TLS)
959 #if !defined(LIBXML_STATIC) || defined(LIBXML_STATIC_FOR_DLL)
960             InitializeCriticalSection(&cleanup_helpers_cs);
961 #endif
962             globalkey = TlsAlloc();
963 #endif
964             mainthread = GetCurrentThreadId();
965 	    __xmlInitializeDict();
966             run_once.done = 1;
967         } else {
968             /* Another thread is working; give up our slice and
969              * wait until they're done. */
970             while (!run_once.done)
971                 Sleep(0);
972         }
973     }
974 #elif defined HAVE_BEOS_THREADS
975     if (atomic_add(&run_once_init, 1) == 0) {
976         globalkey = tls_allocate();
977         tls_set(globalkey, NULL);
978         mainthread = find_thread(NULL);
979 	__xmlInitializeDict();
980     } else
981         atomic_add(&run_once_init, -1);
982 #endif
983 }
984 #endif
985 
986 /**
987  * DllMain:
988  * @hinstDLL: handle to DLL instance
989  * @fdwReason: Reason code for entry
990  * @lpvReserved: generic pointer (depends upon reason code)
991  *
992  * Entry point for Windows library. It is being used to free thread-specific
993  * storage.
994  *
995  * Returns TRUE always
996  */
997 #ifdef HAVE_PTHREAD_H
998 #elif defined(HAVE_WIN32_THREADS) && !defined(HAVE_COMPILER_TLS) && (!defined(LIBXML_STATIC) || defined(LIBXML_STATIC_FOR_DLL))
999 #if defined(LIBXML_STATIC_FOR_DLL)
1000 int XMLCALL
xmlDllMain(ATTRIBUTE_UNUSED void * hinstDLL,unsigned long fdwReason,ATTRIBUTE_UNUSED void * lpvReserved)1001 xmlDllMain(ATTRIBUTE_UNUSED void *hinstDLL, unsigned long fdwReason,
1002            ATTRIBUTE_UNUSED void *lpvReserved)
1003 #else
1004 /* declare to avoid "no previous prototype for 'DllMain'" warning */
1005 /* Note that we do NOT want to include this function declaration in
1006    a public header because it's meant to be called by Windows itself,
1007    not a program that uses this library.  This also has to be exported. */
1008 
1009 XMLPUBFUN BOOL WINAPI
1010 DllMain (HINSTANCE hinstDLL,
1011          DWORD     fdwReason,
1012          LPVOID    lpvReserved);
1013 
1014 BOOL WINAPI
1015 DllMain(ATTRIBUTE_UNUSED HINSTANCE hinstDLL, DWORD fdwReason,
1016         ATTRIBUTE_UNUSED LPVOID lpvReserved)
1017 #endif
1018 {
1019     switch (fdwReason) {
1020         case DLL_THREAD_DETACH:
1021             if (globalkey != TLS_OUT_OF_INDEXES) {
1022                 xmlGlobalState *globalval = NULL;
1023                 xmlGlobalStateCleanupHelperParams *p =
1024                     (xmlGlobalStateCleanupHelperParams *)
1025                     TlsGetValue(globalkey);
1026                 globalval = (xmlGlobalState *) (p ? p->memory : NULL);
1027                 if (globalval) {
1028                     xmlFreeGlobalState(globalval);
1029                     TlsSetValue(globalkey, NULL);
1030                 }
1031                 if (p) {
1032                     EnterCriticalSection(&cleanup_helpers_cs);
1033                     if (p == cleanup_helpers_head)
1034                         cleanup_helpers_head = p->next;
1035                     else
1036                         p->prev->next = p->next;
1037                     if (p->next != NULL)
1038                         p->next->prev = p->prev;
1039                     LeaveCriticalSection(&cleanup_helpers_cs);
1040                     free(p);
1041                 }
1042             }
1043             break;
1044     }
1045     return TRUE;
1046 }
1047 #endif
1048 #define bottom_threads
1049 #include "elfgcchack.h"
1050