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