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