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