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