2 * threads.c: set of generic threading related routines
4 * See Copyright for the status of this software.
6 * Gary Pennington <Gary.Pennington@uk.sun.com>
16 #include <libxml/threads.h>
17 #include <libxml/globals.h>
23 #include "private/dict.h"
24 #include "private/threads.h"
26 /* #define DEBUG_THREADS */
28 #if defined(HAVE_POSIX_THREADS) && \
29 defined(__GLIBC__) && \
30 __GLIBC__ * 100 + __GLIBC_MINOR__ >= 234
33 * The modern way available since glibc 2.32.
35 * The check above is for glibc 2.34 which merged the pthread symbols into
36 * libc. Since we still allow linking without pthread symbols (see below),
37 * this only works if pthread symbols are guaranteed to be available.
40 #include <sys/single_threaded.h>
42 #define XML_IS_THREADED() (!__libc_single_threaded)
43 #define XML_IS_NEVER_THREADED() 0
45 #elif defined(HAVE_POSIX_THREADS) && \
46 defined(__GLIBC__) && \
50 * The traditional way to check for single-threaded applications with
51 * glibc was to check whether the separate libpthread library is
52 * linked in. This works by not linking libxml2 with libpthread (see
53 * BASE_THREAD_LIBS in configure.ac and Makefile.am) and declaring
54 * pthread functions as weak symbols.
56 * In glibc 2.34, the pthread symbols were moved from libpthread to libc,
57 * so this doesn't work anymore.
59 * At some point, this legacy code and the BASE_THREAD_LIBS hack in
60 * configure.ac can probably be removed.
63 #pragma weak pthread_getspecific
64 #pragma weak pthread_setspecific
65 #pragma weak pthread_key_create
66 #pragma weak pthread_key_delete
67 #pragma weak pthread_mutex_init
68 #pragma weak pthread_mutex_destroy
69 #pragma weak pthread_mutex_lock
70 #pragma weak pthread_mutex_unlock
71 #pragma weak pthread_cond_init
72 #pragma weak pthread_cond_destroy
73 #pragma weak pthread_cond_wait
74 #pragma weak pthread_equal
75 #pragma weak pthread_self
76 #pragma weak pthread_key_create
77 #pragma weak pthread_key_delete
78 #pragma weak pthread_cond_signal
80 #define XML_PTHREAD_WEAK
81 #define XML_IS_THREADED() libxml_is_threaded
82 #define XML_IS_NEVER_THREADED() (!libxml_is_threaded)
84 static int libxml_is_threaded
= -1;
86 #else /* other POSIX platforms */
88 #define XML_IS_THREADED() 1
89 #define XML_IS_NEVER_THREADED() 0
94 * TODO: this module still uses malloc/free and not xmlMalloc/xmlFree
95 * to avoid some craziness since xmlMalloc/xmlFree may actually
96 * be hosted on allocated blocks needing them for the allocation ...
100 * xmlRMutex are reentrant mutual exception locks
103 #ifdef HAVE_POSIX_THREADS
104 pthread_mutex_t lock
;
106 unsigned int waiters
;
109 #elif defined HAVE_WIN32_THREADS
117 * This module still has some internal static data.
118 * - xmlLibraryLock a global lock
119 * - globalkey used for per-thread data
122 #ifdef HAVE_POSIX_THREADS
123 static pthread_key_t globalkey
;
124 static pthread_t mainthread
;
125 static pthread_mutex_t global_init_lock
= PTHREAD_MUTEX_INITIALIZER
;
126 #elif defined HAVE_WIN32_THREADS
127 #if defined(HAVE_COMPILER_TLS)
128 static __declspec(thread
) xmlGlobalState tlstate
;
129 static __declspec(thread
) int tlstate_inited
= 0;
130 #else /* HAVE_COMPILER_TLS */
131 static DWORD globalkey
= TLS_OUT_OF_INDEXES
;
132 #endif /* HAVE_COMPILER_TLS */
133 static DWORD mainthread
;
134 static volatile LPCRITICAL_SECTION global_init_lock
= NULL
;
137 static xmlRMutexPtr xmlLibraryLock
= NULL
;
143 * Initialize a mutex.
146 xmlInitMutex(xmlMutexPtr mutex
)
148 #ifdef HAVE_POSIX_THREADS
149 if (XML_IS_NEVER_THREADED() == 0)
150 pthread_mutex_init(&mutex
->lock
, NULL
);
151 #elif defined HAVE_WIN32_THREADS
152 InitializeCriticalSection(&mutex
->cs
);
161 * xmlNewMutex() is used to allocate a libxml2 token struct for use in
162 * synchronizing access to data.
164 * Returns a new simple mutex pointer or NULL in case of error
171 if ((tok
= malloc(sizeof(xmlMutex
))) == NULL
)
179 * @mutex: the simple mutex
181 * Reclaim resources associated with a mutex.
184 xmlCleanupMutex(xmlMutexPtr mutex
)
186 #ifdef HAVE_POSIX_THREADS
187 if (XML_IS_NEVER_THREADED() == 0)
188 pthread_mutex_destroy(&mutex
->lock
);
189 #elif defined HAVE_WIN32_THREADS
190 DeleteCriticalSection(&mutex
->cs
);
198 * @tok: the simple mutex
203 xmlFreeMutex(xmlMutexPtr tok
)
208 xmlCleanupMutex(tok
);
214 * @tok: the simple mutex
216 * xmlMutexLock() is used to lock a libxml2 token.
219 xmlMutexLock(xmlMutexPtr tok
)
223 #ifdef HAVE_POSIX_THREADS
225 * This assumes that __libc_single_threaded won't change while the
228 if (XML_IS_THREADED() != 0)
229 pthread_mutex_lock(&tok
->lock
);
230 #elif defined HAVE_WIN32_THREADS
231 EnterCriticalSection(&tok
->cs
);
238 * @tok: the simple mutex
240 * xmlMutexUnlock() is used to unlock a libxml2 token.
243 xmlMutexUnlock(xmlMutexPtr tok
)
247 #ifdef HAVE_POSIX_THREADS
248 if (XML_IS_THREADED() != 0)
249 pthread_mutex_unlock(&tok
->lock
);
250 #elif defined HAVE_WIN32_THREADS
251 LeaveCriticalSection(&tok
->cs
);
258 * xmlRNewMutex() is used to allocate a reentrant mutex for use in
259 * synchronizing access to data. token_r is a re-entrant lock and thus useful
260 * for synchronizing access to data structures that may be manipulated in a
263 * Returns the new reentrant mutex pointer or NULL in case of error
270 if ((tok
= malloc(sizeof(xmlRMutex
))) == NULL
)
272 #ifdef HAVE_POSIX_THREADS
273 if (XML_IS_NEVER_THREADED() == 0) {
274 pthread_mutex_init(&tok
->lock
, NULL
);
277 pthread_cond_init(&tok
->cv
, NULL
);
279 #elif defined HAVE_WIN32_THREADS
280 InitializeCriticalSection(&tok
->cs
);
287 * @tok: the reentrant mutex
289 * xmlRFreeMutex() is used to reclaim resources associated with a
293 xmlFreeRMutex(xmlRMutexPtr tok ATTRIBUTE_UNUSED
)
297 #ifdef HAVE_POSIX_THREADS
298 if (XML_IS_NEVER_THREADED() == 0) {
299 pthread_mutex_destroy(&tok
->lock
);
300 pthread_cond_destroy(&tok
->cv
);
302 #elif defined HAVE_WIN32_THREADS
303 DeleteCriticalSection(&tok
->cs
);
310 * @tok: the reentrant mutex
312 * xmlRMutexLock() is used to lock a libxml2 token_r.
315 xmlRMutexLock(xmlRMutexPtr tok
)
319 #ifdef HAVE_POSIX_THREADS
320 if (XML_IS_THREADED() == 0)
323 pthread_mutex_lock(&tok
->lock
);
325 if (pthread_equal(tok
->tid
, pthread_self())) {
327 pthread_mutex_unlock(&tok
->lock
);
332 pthread_cond_wait(&tok
->cv
, &tok
->lock
);
336 tok
->tid
= pthread_self();
338 pthread_mutex_unlock(&tok
->lock
);
339 #elif defined HAVE_WIN32_THREADS
340 EnterCriticalSection(&tok
->cs
);
346 * @tok: the reentrant mutex
348 * xmlRMutexUnlock() is used to unlock a libxml2 token_r.
351 xmlRMutexUnlock(xmlRMutexPtr tok ATTRIBUTE_UNUSED
)
355 #ifdef HAVE_POSIX_THREADS
356 if (XML_IS_THREADED() == 0)
359 pthread_mutex_lock(&tok
->lock
);
361 if (tok
->held
== 0) {
363 pthread_cond_signal(&tok
->cv
);
364 memset(&tok
->tid
, 0, sizeof(tok
->tid
));
366 pthread_mutex_unlock(&tok
->lock
);
367 #elif defined HAVE_WIN32_THREADS
368 LeaveCriticalSection(&tok
->cs
);
373 * xmlGlobalInitMutexLock
375 * Makes sure that the global initialization mutex is initialized and
379 __xmlGlobalInitMutexLock(void)
381 /* Make sure the global init lock is initialized and then lock it. */
382 #ifdef HAVE_POSIX_THREADS
383 #ifdef XML_PTHREAD_WEAK
384 if (pthread_mutex_lock
== NULL
)
387 if (XML_IS_THREADED() == 0)
390 /* The mutex is statically initialized, so we just lock it. */
391 pthread_mutex_lock(&global_init_lock
);
392 #elif defined HAVE_WIN32_THREADS
393 LPCRITICAL_SECTION cs
;
395 /* Create a new critical section */
396 if (global_init_lock
== NULL
) {
397 cs
= malloc(sizeof(CRITICAL_SECTION
));
399 xmlGenericError(xmlGenericErrorContext
,
400 "xmlGlobalInitMutexLock: out of memory\n");
403 InitializeCriticalSection(cs
);
405 /* Swap it into the global_init_lock */
406 #ifdef InterlockedCompareExchangePointer
407 InterlockedCompareExchangePointer((void **) &global_init_lock
,
409 #else /* Use older void* version */
410 InterlockedCompareExchange((void **) &global_init_lock
,
412 #endif /* InterlockedCompareExchangePointer */
414 /* If another thread successfully recorded its critical
415 * section in the global_init_lock then discard the one
416 * allocated by this thread. */
417 if (global_init_lock
!= cs
) {
418 DeleteCriticalSection(cs
);
423 /* Lock the chosen critical section */
424 EnterCriticalSection(global_init_lock
);
429 __xmlGlobalInitMutexUnlock(void)
431 #ifdef HAVE_POSIX_THREADS
432 #ifdef XML_PTHREAD_WEAK
433 if (pthread_mutex_lock
== NULL
)
436 if (XML_IS_THREADED() == 0)
439 pthread_mutex_unlock(&global_init_lock
);
440 #elif defined HAVE_WIN32_THREADS
441 if (global_init_lock
!= NULL
) {
442 LeaveCriticalSection(global_init_lock
);
448 * xmlGlobalInitMutexDestroy
450 * Makes sure that the global initialization mutex is destroyed before
451 * application termination.
454 __xmlGlobalInitMutexDestroy(void)
456 #ifdef HAVE_POSIX_THREADS
457 #elif defined HAVE_WIN32_THREADS
458 if (global_init_lock
!= NULL
) {
459 DeleteCriticalSection(global_init_lock
);
460 free(global_init_lock
);
461 global_init_lock
= NULL
;
466 /************************************************************************
468 * Per thread global state handling *
470 ************************************************************************/
472 #ifdef LIBXML_THREAD_ENABLED
478 * xmlFreeGlobalState:
479 * @state: a thread global state
481 * xmlFreeGlobalState() is called when a thread terminates with a non-NULL
482 * global state. It is is used here to reclaim memory resources.
485 xmlFreeGlobalState(void *state
)
487 xmlGlobalState
*gs
= (xmlGlobalState
*) state
;
489 /* free any memory allocated in the thread's xmlLastError */
490 xmlResetError(&(gs
->xmlLastError
));
497 * xmlNewGlobalState() allocates a global state. This structure is used to
498 * hold all data for use by a thread when supporting backwards compatibility
499 * of libxml2 to pre-thread-safe behaviour.
501 * Returns the newly allocated xmlGlobalStatePtr or NULL in case of error
503 static xmlGlobalStatePtr
504 xmlNewGlobalState(void)
508 gs
= malloc(sizeof(xmlGlobalState
));
510 xmlGenericError(xmlGenericErrorContext
,
511 "xmlGetGlobalState: out of memory\n");
515 memset(gs
, 0, sizeof(xmlGlobalState
));
516 xmlInitializeGlobalState(gs
);
519 #endif /* LIBXML_THREAD_ENABLED */
521 #ifdef HAVE_POSIX_THREADS
522 #elif defined HAVE_WIN32_THREADS
523 #if !defined(HAVE_COMPILER_TLS)
524 #if defined(LIBXML_STATIC) && !defined(LIBXML_STATIC_FOR_DLL)
525 typedef struct _xmlGlobalStateCleanupHelperParams
{
528 } xmlGlobalStateCleanupHelperParams
;
531 xmlGlobalStateCleanupHelper(void *p
)
533 xmlGlobalStateCleanupHelperParams
*params
=
534 (xmlGlobalStateCleanupHelperParams
*) p
;
535 WaitForSingleObject(params
->thread
, INFINITE
);
536 CloseHandle(params
->thread
);
537 xmlFreeGlobalState(params
->memory
);
541 #else /* LIBXML_STATIC && !LIBXML_STATIC_FOR_DLL */
543 typedef struct _xmlGlobalStateCleanupHelperParams
{
545 struct _xmlGlobalStateCleanupHelperParams
*prev
;
546 struct _xmlGlobalStateCleanupHelperParams
*next
;
547 } xmlGlobalStateCleanupHelperParams
;
549 static xmlGlobalStateCleanupHelperParams
*cleanup_helpers_head
= NULL
;
550 static CRITICAL_SECTION cleanup_helpers_cs
;
552 #endif /* LIBXMLSTATIC && !LIBXML_STATIC_FOR_DLL */
553 #endif /* HAVE_COMPILER_TLS */
554 #endif /* HAVE_WIN32_THREADS */
559 * DEPRECATED: Internal function, do not use.
561 * xmlGetGlobalState() is called to retrieve the global state for a thread.
563 * Returns the thread global state or NULL in case of error
566 xmlGetGlobalState(void)
568 #ifdef HAVE_POSIX_THREADS
569 xmlGlobalState
*globalval
;
571 if (XML_IS_THREADED() == 0)
574 if ((globalval
= (xmlGlobalState
*)
575 pthread_getspecific(globalkey
)) == NULL
) {
576 xmlGlobalState
*tsd
= xmlNewGlobalState();
580 pthread_setspecific(globalkey
, tsd
);
584 #elif defined HAVE_WIN32_THREADS
585 #if defined(HAVE_COMPILER_TLS)
586 if (!tlstate_inited
) {
588 xmlInitializeGlobalState(&tlstate
);
591 #else /* HAVE_COMPILER_TLS */
592 xmlGlobalState
*globalval
;
593 xmlGlobalStateCleanupHelperParams
*p
;
594 #if defined(LIBXML_STATIC) && !defined(LIBXML_STATIC_FOR_DLL)
595 globalval
= (xmlGlobalState
*) TlsGetValue(globalkey
);
597 p
= (xmlGlobalStateCleanupHelperParams
*) TlsGetValue(globalkey
);
598 globalval
= (xmlGlobalState
*) (p
? p
->memory
: NULL
);
600 if (globalval
== NULL
) {
601 xmlGlobalState
*tsd
= xmlNewGlobalState();
605 p
= (xmlGlobalStateCleanupHelperParams
*)
606 malloc(sizeof(xmlGlobalStateCleanupHelperParams
));
608 xmlGenericError(xmlGenericErrorContext
,
609 "xmlGetGlobalState: out of memory\n");
610 xmlFreeGlobalState(tsd
);
614 #if defined(LIBXML_STATIC) && !defined(LIBXML_STATIC_FOR_DLL)
615 DuplicateHandle(GetCurrentProcess(), GetCurrentThread(),
616 GetCurrentProcess(), &p
->thread
, 0, TRUE
,
617 DUPLICATE_SAME_ACCESS
);
618 TlsSetValue(globalkey
, tsd
);
619 _beginthread(xmlGlobalStateCleanupHelper
, 0, p
);
621 EnterCriticalSection(&cleanup_helpers_cs
);
622 if (cleanup_helpers_head
!= NULL
) {
623 cleanup_helpers_head
->prev
= p
;
625 p
->next
= cleanup_helpers_head
;
627 cleanup_helpers_head
= p
;
628 TlsSetValue(globalkey
, p
);
629 LeaveCriticalSection(&cleanup_helpers_cs
);
635 #endif /* HAVE_COMPILER_TLS */
641 /************************************************************************
643 * Library wide thread interfaces *
645 ************************************************************************/
650 * DEPRECATED: Internal function, do not use.
652 * xmlGetThreadId() find the current thread ID number
653 * Note that this is likely to be broken on some platforms using pthreads
654 * as the specification doesn't mandate pthread_t to be an integer type
656 * Returns the current thread ID number
661 #ifdef HAVE_POSIX_THREADS
665 if (XML_IS_THREADED() == 0)
668 /* horrible but preserves compat, see warning above */
669 memcpy(&ret
, &id
, sizeof(ret
));
671 #elif defined HAVE_WIN32_THREADS
672 return GetCurrentThreadId();
681 * DEPRECATED: Internal function, do not use.
683 * xmlIsMainThread() check whether the current thread is the main thread.
685 * Returns 1 if the current thread is the main thread, 0 otherwise
688 xmlIsMainThread(void)
693 xmlGenericError(xmlGenericErrorContext
, "xmlIsMainThread()\n");
695 #ifdef HAVE_POSIX_THREADS
696 if (XML_IS_THREADED() == 0)
698 return (pthread_equal(mainthread
,pthread_self()));
699 #elif defined HAVE_WIN32_THREADS
700 return (mainthread
== GetCurrentThreadId());
709 * xmlLockLibrary() is used to take out a re-entrant lock on the libxml2
716 xmlGenericError(xmlGenericErrorContext
, "xmlLockLibrary()\n");
718 xmlRMutexLock(xmlLibraryLock
);
724 * xmlUnlockLibrary() is used to release a re-entrant lock on the libxml2
728 xmlUnlockLibrary(void)
731 xmlGenericError(xmlGenericErrorContext
, "xmlUnlockLibrary()\n");
733 xmlRMutexUnlock(xmlLibraryLock
);
739 * DEPRECATED: Alias for xmlInitParser.
748 * xmlInitThreadsInternal:
750 * Used to to initialize all the thread related data.
753 xmlInitThreadsInternal(void)
755 #ifdef HAVE_POSIX_THREADS
756 #ifdef XML_PTHREAD_WEAK
758 * This is somewhat unreliable since libpthread could be loaded
759 * later with dlopen() and threads could be created. But it's
760 * long-standing behavior and hard to work around.
762 if (libxml_is_threaded
== -1)
764 (pthread_getspecific
!= NULL
) &&
765 (pthread_setspecific
!= NULL
) &&
766 (pthread_key_create
!= NULL
) &&
767 (pthread_key_delete
!= NULL
) &&
768 (pthread_mutex_init
!= NULL
) &&
769 (pthread_mutex_destroy
!= NULL
) &&
770 (pthread_mutex_lock
!= NULL
) &&
771 (pthread_mutex_unlock
!= NULL
) &&
772 (pthread_cond_init
!= NULL
) &&
773 (pthread_cond_destroy
!= NULL
) &&
774 (pthread_cond_wait
!= NULL
) &&
776 * pthread_equal can be inline, resuting in -Waddress warnings.
777 * Let's assume it's available if all the other functions are.
779 /* (pthread_equal != NULL) && */
780 (pthread_self
!= NULL
) &&
781 (pthread_cond_signal
!= NULL
);
782 if (libxml_is_threaded
== 0)
784 #endif /* XML_PTHREAD_WEAK */
785 pthread_key_create(&globalkey
, xmlFreeGlobalState
);
786 mainthread
= pthread_self();
787 #elif defined(HAVE_WIN32_THREADS)
788 #if !defined(HAVE_COMPILER_TLS)
789 #if !defined(LIBXML_STATIC) || defined(LIBXML_STATIC_FOR_DLL)
790 InitializeCriticalSection(&cleanup_helpers_cs
);
792 globalkey
= TlsAlloc();
794 mainthread
= GetCurrentThreadId();
801 * DEPRECATED: This function is a no-op. Call xmlCleanupParser
802 * to free global state but see the warnings there. xmlCleanupParser
803 * should be only called once at program exit. In most cases, you don't
804 * have call cleanup functions at all.
807 xmlCleanupThreads(void)
812 * xmlCleanupThreadsInternal:
814 * Used to to cleanup all the thread related data.
817 xmlCleanupThreadsInternal(void)
819 #ifdef HAVE_POSIX_THREADS
820 #ifdef XML_PTHREAD_WEAK
821 if (libxml_is_threaded
== 0)
823 #endif /* XML_PTHREAD_WEAK */
824 pthread_key_delete(globalkey
);
825 #elif defined(HAVE_WIN32_THREADS)
826 #if !defined(HAVE_COMPILER_TLS)
827 if (globalkey
!= TLS_OUT_OF_INDEXES
) {
828 #if !defined(LIBXML_STATIC) || defined(LIBXML_STATIC_FOR_DLL)
829 xmlGlobalStateCleanupHelperParams
*p
;
831 EnterCriticalSection(&cleanup_helpers_cs
);
832 p
= cleanup_helpers_head
;
834 xmlGlobalStateCleanupHelperParams
*temp
= p
;
837 xmlFreeGlobalState(temp
->memory
);
840 cleanup_helpers_head
= 0;
841 LeaveCriticalSection(&cleanup_helpers_cs
);
844 globalkey
= TLS_OUT_OF_INDEXES
;
846 #if !defined(LIBXML_STATIC) || defined(LIBXML_STATIC_FOR_DLL)
847 DeleteCriticalSection(&cleanup_helpers_cs
);
855 * @hinstDLL: handle to DLL instance
856 * @fdwReason: Reason code for entry
857 * @lpvReserved: generic pointer (depends upon reason code)
859 * Entry point for Windows library. It is being used to free thread-specific
862 * Returns TRUE always
864 #ifdef HAVE_POSIX_THREADS
865 #elif defined(HAVE_WIN32_THREADS) && !defined(HAVE_COMPILER_TLS) && (!defined(LIBXML_STATIC) || defined(LIBXML_STATIC_FOR_DLL))
866 #if defined(LIBXML_STATIC_FOR_DLL)
868 xmlDllMain(ATTRIBUTE_UNUSED
void *hinstDLL
, unsigned long fdwReason
,
869 ATTRIBUTE_UNUSED
void *lpvReserved
)
871 /* declare to avoid "no previous prototype for 'DllMain'" warning */
872 /* Note that we do NOT want to include this function declaration in
873 a public header because it's meant to be called by Windows itself,
874 not a program that uses this library. This also has to be exported. */
876 XMLPUBFUN BOOL WINAPI
877 DllMain (HINSTANCE hinstDLL
,
882 DllMain(ATTRIBUTE_UNUSED HINSTANCE hinstDLL
, DWORD fdwReason
,
883 ATTRIBUTE_UNUSED LPVOID lpvReserved
)
887 case DLL_THREAD_DETACH
:
888 if (globalkey
!= TLS_OUT_OF_INDEXES
) {
889 xmlGlobalState
*globalval
= NULL
;
890 xmlGlobalStateCleanupHelperParams
*p
=
891 (xmlGlobalStateCleanupHelperParams
*)
892 TlsGetValue(globalkey
);
893 globalval
= (xmlGlobalState
*) (p
? p
->memory
: NULL
);
895 xmlFreeGlobalState(globalval
);
896 TlsSetValue(globalkey
, NULL
);
899 EnterCriticalSection(&cleanup_helpers_cs
);
900 if (p
== cleanup_helpers_head
)
901 cleanup_helpers_head
= p
->next
;
903 p
->prev
->next
= p
->next
;
905 p
->next
->prev
= p
->prev
;
906 LeaveCriticalSection(&cleanup_helpers_cs
);