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