From 35a201cc8ef0c3f5b2df88d2e528aabee1048348 Mon Sep 17 00:00:00 2001 From: Wojtek Kosior Date: Fri, 30 Apr 2021 18:47:09 +0200 Subject: Initial/Final commit --- libxml2-2.9.10/threads.c | 1048 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 1048 insertions(+) create mode 100644 libxml2-2.9.10/threads.c (limited to 'libxml2-2.9.10/threads.c') diff --git a/libxml2-2.9.10/threads.c b/libxml2-2.9.10/threads.c new file mode 100644 index 0000000..72df9ba --- /dev/null +++ b/libxml2-2.9.10/threads.c @@ -0,0 +1,1048 @@ +/** + * threads.c: set of generic threading related routines + * + * See Copyright for the status of this software. + * + * Gary Pennington + * daniel@veillard.com + */ + +#define IN_LIBXML +#include "libxml.h" + +#include + +#include +#include + +#ifdef HAVE_SYS_TYPES_H +#include +#endif +#ifdef HAVE_UNISTD_H +#include +#endif +#ifdef HAVE_STDLIB_H +#include +#endif +#ifdef HAVE_PTHREAD_H +#include +#elif defined HAVE_WIN32_THREADS +#define WIN32_LEAN_AND_MEAN +#include +#ifndef HAVE_COMPILER_TLS +#include +#endif +#endif + +#ifdef HAVE_BEOS_THREADS +#include +#include +#endif + +#if defined(SOLARIS) +#include +#endif + +/* #define DEBUG_THREADS */ + +#ifdef HAVE_PTHREAD_H + +#if defined(__GNUC__) && (__GNUC__ * 100 + __GNUC_MINOR__ >= 303) && \ + defined(__GLIBC__) && defined(__linux__) + +static int libxml_is_threaded = -1; + +#define XML_PTHREAD_WEAK + +#pragma weak pthread_once +#pragma weak pthread_getspecific +#pragma weak pthread_setspecific +#pragma weak pthread_key_create +#pragma weak pthread_key_delete +#pragma weak pthread_mutex_init +#pragma weak pthread_mutex_destroy +#pragma weak pthread_mutex_lock +#pragma weak pthread_mutex_unlock +#pragma weak pthread_cond_init +#pragma weak pthread_cond_destroy +#pragma weak pthread_cond_wait +#pragma weak pthread_equal +#pragma weak pthread_self +#pragma weak pthread_key_create +#pragma weak pthread_key_delete +#pragma weak pthread_cond_signal + +#else /* __GNUC__, __GLIBC__, __linux__ */ + +static int libxml_is_threaded = 1; + +#endif /* __GNUC__, __GLIBC__, __linux__ */ + +#endif /* HAVE_PTHREAD_H */ + +/* + * TODO: this module still uses malloc/free and not xmlMalloc/xmlFree + * to avoid some craziness since xmlMalloc/xmlFree may actually + * be hosted on allocated blocks needing them for the allocation ... + */ + +/* + * xmlMutex are a simple mutual exception locks + */ +struct _xmlMutex { +#ifdef HAVE_PTHREAD_H + pthread_mutex_t lock; +#elif defined HAVE_WIN32_THREADS + HANDLE mutex; +#elif defined HAVE_BEOS_THREADS + sem_id sem; + thread_id tid; +#else + int empty; +#endif +}; + +/* + * xmlRMutex are reentrant mutual exception locks + */ +struct _xmlRMutex { +#ifdef HAVE_PTHREAD_H + pthread_mutex_t lock; + unsigned int held; + unsigned int waiters; + pthread_t tid; + pthread_cond_t cv; +#elif defined HAVE_WIN32_THREADS + CRITICAL_SECTION cs; + unsigned int count; +#elif defined HAVE_BEOS_THREADS + xmlMutexPtr lock; + thread_id tid; + int32 count; +#else + int empty; +#endif +}; + +/* + * This module still has some internal static data. + * - xmlLibraryLock a global lock + * - globalkey used for per-thread data + */ + +#ifdef HAVE_PTHREAD_H +static pthread_key_t globalkey; +static pthread_t mainthread; +static pthread_once_t once_control = PTHREAD_ONCE_INIT; +static pthread_once_t once_control_init = PTHREAD_ONCE_INIT; +static pthread_mutex_t global_init_lock = PTHREAD_MUTEX_INITIALIZER; +#elif defined HAVE_WIN32_THREADS +#if defined(HAVE_COMPILER_TLS) +static __declspec(thread) xmlGlobalState tlstate; +static __declspec(thread) int tlstate_inited = 0; +#else /* HAVE_COMPILER_TLS */ +static DWORD globalkey = TLS_OUT_OF_INDEXES; +#endif /* HAVE_COMPILER_TLS */ +static DWORD mainthread; +static struct { + DWORD done; + LONG control; +} run_once = { 0, 0}; +static volatile LPCRITICAL_SECTION global_init_lock = NULL; + +/* endif HAVE_WIN32_THREADS */ +#elif defined HAVE_BEOS_THREADS +int32 globalkey = 0; +thread_id mainthread = 0; +int32 run_once_init = 0; +static int32 global_init_lock = -1; +static vint32 global_init_count = 0; +#endif + +static xmlRMutexPtr xmlLibraryLock = NULL; + +#ifdef LIBXML_THREAD_ENABLED +static void xmlOnceInit(void); +#endif + +/** + * xmlNewMutex: + * + * xmlNewMutex() is used to allocate a libxml2 token struct for use in + * synchronizing access to data. + * + * Returns a new simple mutex pointer or NULL in case of error + */ +xmlMutexPtr +xmlNewMutex(void) +{ + xmlMutexPtr tok; + + if ((tok = malloc(sizeof(xmlMutex))) == NULL) + return (NULL); +#ifdef HAVE_PTHREAD_H + if (libxml_is_threaded != 0) + pthread_mutex_init(&tok->lock, NULL); +#elif defined HAVE_WIN32_THREADS + tok->mutex = CreateMutex(NULL, FALSE, NULL); +#elif defined HAVE_BEOS_THREADS + if ((tok->sem = create_sem(1, "xmlMutex")) < B_OK) { + free(tok); + return NULL; + } + tok->tid = -1; +#endif + return (tok); +} + +/** + * xmlFreeMutex: + * @tok: the simple mutex + * + * xmlFreeMutex() is used to reclaim resources associated with a libxml2 token + * struct. + */ +void +xmlFreeMutex(xmlMutexPtr tok) +{ + if (tok == NULL) + return; + +#ifdef HAVE_PTHREAD_H + if (libxml_is_threaded != 0) + pthread_mutex_destroy(&tok->lock); +#elif defined HAVE_WIN32_THREADS + CloseHandle(tok->mutex); +#elif defined HAVE_BEOS_THREADS + delete_sem(tok->sem); +#endif + free(tok); +} + +/** + * xmlMutexLock: + * @tok: the simple mutex + * + * xmlMutexLock() is used to lock a libxml2 token. + */ +void +xmlMutexLock(xmlMutexPtr tok) +{ + if (tok == NULL) + return; +#ifdef HAVE_PTHREAD_H + if (libxml_is_threaded != 0) + pthread_mutex_lock(&tok->lock); +#elif defined HAVE_WIN32_THREADS + WaitForSingleObject(tok->mutex, INFINITE); +#elif defined HAVE_BEOS_THREADS + if (acquire_sem(tok->sem) != B_NO_ERROR) { +#ifdef DEBUG_THREADS + xmlGenericError(xmlGenericErrorContext, + "xmlMutexLock():BeOS:Couldn't acquire semaphore\n"); +#endif + } + tok->tid = find_thread(NULL); +#endif + +} + +/** + * xmlMutexUnlock: + * @tok: the simple mutex + * + * xmlMutexUnlock() is used to unlock a libxml2 token. + */ +void +xmlMutexUnlock(xmlMutexPtr tok) +{ + if (tok == NULL) + return; +#ifdef HAVE_PTHREAD_H + if (libxml_is_threaded != 0) + pthread_mutex_unlock(&tok->lock); +#elif defined HAVE_WIN32_THREADS + ReleaseMutex(tok->mutex); +#elif defined HAVE_BEOS_THREADS + if (tok->tid == find_thread(NULL)) { + tok->tid = -1; + release_sem(tok->sem); + } +#endif +} + +/** + * xmlNewRMutex: + * + * xmlRNewMutex() is used to allocate a reentrant mutex for use in + * synchronizing access to data. token_r is a re-entrant lock and thus useful + * for synchronizing access to data structures that may be manipulated in a + * recursive fashion. + * + * Returns the new reentrant mutex pointer or NULL in case of error + */ +xmlRMutexPtr +xmlNewRMutex(void) +{ + xmlRMutexPtr tok; + + if ((tok = malloc(sizeof(xmlRMutex))) == NULL) + return (NULL); +#ifdef HAVE_PTHREAD_H + if (libxml_is_threaded != 0) { + pthread_mutex_init(&tok->lock, NULL); + tok->held = 0; + tok->waiters = 0; + pthread_cond_init(&tok->cv, NULL); + } +#elif defined HAVE_WIN32_THREADS + InitializeCriticalSection(&tok->cs); + tok->count = 0; +#elif defined HAVE_BEOS_THREADS + if ((tok->lock = xmlNewMutex()) == NULL) { + free(tok); + return NULL; + } + tok->count = 0; +#endif + return (tok); +} + +/** + * xmlFreeRMutex: + * @tok: the reentrant mutex + * + * xmlRFreeMutex() is used to reclaim resources associated with a + * reentrant mutex. + */ +void +xmlFreeRMutex(xmlRMutexPtr tok ATTRIBUTE_UNUSED) +{ + if (tok == NULL) + return; +#ifdef HAVE_PTHREAD_H + if (libxml_is_threaded != 0) { + pthread_mutex_destroy(&tok->lock); + pthread_cond_destroy(&tok->cv); + } +#elif defined HAVE_WIN32_THREADS + DeleteCriticalSection(&tok->cs); +#elif defined HAVE_BEOS_THREADS + xmlFreeMutex(tok->lock); +#endif + free(tok); +} + +/** + * xmlRMutexLock: + * @tok: the reentrant mutex + * + * xmlRMutexLock() is used to lock a libxml2 token_r. + */ +void +xmlRMutexLock(xmlRMutexPtr tok) +{ + if (tok == NULL) + return; +#ifdef HAVE_PTHREAD_H + if (libxml_is_threaded == 0) + return; + + pthread_mutex_lock(&tok->lock); + if (tok->held) { + if (pthread_equal(tok->tid, pthread_self())) { + tok->held++; + pthread_mutex_unlock(&tok->lock); + return; + } else { + tok->waiters++; + while (tok->held) + pthread_cond_wait(&tok->cv, &tok->lock); + tok->waiters--; + } + } + tok->tid = pthread_self(); + tok->held = 1; + pthread_mutex_unlock(&tok->lock); +#elif defined HAVE_WIN32_THREADS + EnterCriticalSection(&tok->cs); + tok->count++; +#elif defined HAVE_BEOS_THREADS + if (tok->lock->tid == find_thread(NULL)) { + tok->count++; + return; + } else { + xmlMutexLock(tok->lock); + tok->count = 1; + } +#endif +} + +/** + * xmlRMutexUnlock: + * @tok: the reentrant mutex + * + * xmlRMutexUnlock() is used to unlock a libxml2 token_r. + */ +void +xmlRMutexUnlock(xmlRMutexPtr tok ATTRIBUTE_UNUSED) +{ + if (tok == NULL) + return; +#ifdef HAVE_PTHREAD_H + if (libxml_is_threaded == 0) + return; + + pthread_mutex_lock(&tok->lock); + tok->held--; + if (tok->held == 0) { + if (tok->waiters) + pthread_cond_signal(&tok->cv); + memset(&tok->tid, 0, sizeof(tok->tid)); + } + pthread_mutex_unlock(&tok->lock); +#elif defined HAVE_WIN32_THREADS + if (tok->count > 0) { + tok->count--; + LeaveCriticalSection(&tok->cs); + } +#elif defined HAVE_BEOS_THREADS + if (tok->lock->tid == find_thread(NULL)) { + tok->count--; + if (tok->count == 0) { + xmlMutexUnlock(tok->lock); + } + return; + } +#endif +} + +/** + * xmlGlobalInitMutexLock + * + * Makes sure that the global initialization mutex is initialized and + * locks it. + */ +void +__xmlGlobalInitMutexLock(void) +{ + /* Make sure the global init lock is initialized and then lock it. */ +#ifdef HAVE_PTHREAD_H + /* The mutex is statically initialized, so we just lock it. */ +#ifdef XML_PTHREAD_WEAK + if (pthread_mutex_lock == NULL) + return; +#endif /* XML_PTHREAD_WEAK */ + pthread_mutex_lock(&global_init_lock); +#elif defined HAVE_WIN32_THREADS + LPCRITICAL_SECTION cs; + + /* Create a new critical section */ + if (global_init_lock == NULL) { + cs = malloc(sizeof(CRITICAL_SECTION)); + if (cs == NULL) { + xmlGenericError(xmlGenericErrorContext, + "xmlGlobalInitMutexLock: out of memory\n"); + return; + } + InitializeCriticalSection(cs); + + /* Swap it into the global_init_lock */ +#ifdef InterlockedCompareExchangePointer + InterlockedCompareExchangePointer((void **) &global_init_lock, + cs, NULL); +#else /* Use older void* version */ + InterlockedCompareExchange((void **) &global_init_lock, + (void *) cs, NULL); +#endif /* InterlockedCompareExchangePointer */ + + /* If another thread successfully recorded its critical + * section in the global_init_lock then discard the one + * allocated by this thread. */ + if (global_init_lock != cs) { + DeleteCriticalSection(cs); + free(cs); + } + } + + /* Lock the chosen critical section */ + EnterCriticalSection(global_init_lock); +#elif defined HAVE_BEOS_THREADS + int32 sem; + + /* Allocate a new semaphore */ + sem = create_sem(1, "xmlGlobalinitMutex"); + + while (global_init_lock == -1) { + if (atomic_add(&global_init_count, 1) == 0) { + global_init_lock = sem; + } else { + snooze(1); + atomic_add(&global_init_count, -1); + } + } + + /* If another thread successfully recorded its critical + * section in the global_init_lock then discard the one + * allocated by this thread. */ + if (global_init_lock != sem) + delete_sem(sem); + + /* Acquire the chosen semaphore */ + if (acquire_sem(global_init_lock) != B_NO_ERROR) { +#ifdef DEBUG_THREADS + xmlGenericError(xmlGenericErrorContext, + "xmlGlobalInitMutexLock():BeOS:Couldn't acquire semaphore\n"); +#endif + } +#endif +} + +void +__xmlGlobalInitMutexUnlock(void) +{ +#ifdef HAVE_PTHREAD_H +#ifdef XML_PTHREAD_WEAK + if (pthread_mutex_unlock == NULL) + return; +#endif /* XML_PTHREAD_WEAK */ + pthread_mutex_unlock(&global_init_lock); +#elif defined HAVE_WIN32_THREADS + if (global_init_lock != NULL) { + LeaveCriticalSection(global_init_lock); + } +#elif defined HAVE_BEOS_THREADS + release_sem(global_init_lock); +#endif +} + +/** + * xmlGlobalInitMutexDestroy + * + * Makes sure that the global initialization mutex is destroyed before + * application termination. + */ +void +__xmlGlobalInitMutexDestroy(void) +{ +#ifdef HAVE_PTHREAD_H +#elif defined HAVE_WIN32_THREADS + if (global_init_lock != NULL) { + DeleteCriticalSection(global_init_lock); + free(global_init_lock); + global_init_lock = NULL; + } +#endif +} + +/************************************************************************ + * * + * Per thread global state handling * + * * + ************************************************************************/ + +#ifdef LIBXML_THREAD_ENABLED +#ifdef xmlLastError +#undef xmlLastError +#endif + +/** + * xmlFreeGlobalState: + * @state: a thread global state + * + * xmlFreeGlobalState() is called when a thread terminates with a non-NULL + * global state. It is is used here to reclaim memory resources. + */ +static void +xmlFreeGlobalState(void *state) +{ + xmlGlobalState *gs = (xmlGlobalState *) state; + + /* free any memory allocated in the thread's xmlLastError */ + xmlResetError(&(gs->xmlLastError)); + free(state); +} + +/** + * xmlNewGlobalState: + * + * xmlNewGlobalState() allocates a global state. This structure is used to + * hold all data for use by a thread when supporting backwards compatibility + * of libxml2 to pre-thread-safe behaviour. + * + * Returns the newly allocated xmlGlobalStatePtr or NULL in case of error + */ +static xmlGlobalStatePtr +xmlNewGlobalState(void) +{ + xmlGlobalState *gs; + + gs = malloc(sizeof(xmlGlobalState)); + if (gs == NULL) { + xmlGenericError(xmlGenericErrorContext, + "xmlGetGlobalState: out of memory\n"); + return (NULL); + } + + memset(gs, 0, sizeof(xmlGlobalState)); + xmlInitializeGlobalState(gs); + return (gs); +} +#endif /* LIBXML_THREAD_ENABLED */ + +#ifdef HAVE_PTHREAD_H +#elif defined HAVE_WIN32_THREADS +#if !defined(HAVE_COMPILER_TLS) +#if defined(LIBXML_STATIC) && !defined(LIBXML_STATIC_FOR_DLL) +typedef struct _xmlGlobalStateCleanupHelperParams { + HANDLE thread; + void *memory; +} xmlGlobalStateCleanupHelperParams; + +static void XMLCDECL +xmlGlobalStateCleanupHelper(void *p) +{ + xmlGlobalStateCleanupHelperParams *params = + (xmlGlobalStateCleanupHelperParams *) p; + WaitForSingleObject(params->thread, INFINITE); + CloseHandle(params->thread); + xmlFreeGlobalState(params->memory); + free(params); + _endthread(); +} +#else /* LIBXML_STATIC && !LIBXML_STATIC_FOR_DLL */ + +typedef struct _xmlGlobalStateCleanupHelperParams { + void *memory; + struct _xmlGlobalStateCleanupHelperParams *prev; + struct _xmlGlobalStateCleanupHelperParams *next; +} xmlGlobalStateCleanupHelperParams; + +static xmlGlobalStateCleanupHelperParams *cleanup_helpers_head = NULL; +static CRITICAL_SECTION cleanup_helpers_cs; + +#endif /* LIBXMLSTATIC && !LIBXML_STATIC_FOR_DLL */ +#endif /* HAVE_COMPILER_TLS */ +#endif /* HAVE_WIN32_THREADS */ + +#if defined HAVE_BEOS_THREADS + +/** + * xmlGlobalStateCleanup: + * @data: unused parameter + * + * Used for Beos only + */ +void +xmlGlobalStateCleanup(void *data) +{ + void *globalval = tls_get(globalkey); + + if (globalval != NULL) + xmlFreeGlobalState(globalval); +} +#endif + +/** + * xmlGetGlobalState: + * + * xmlGetGlobalState() is called to retrieve the global state for a thread. + * + * Returns the thread global state or NULL in case of error + */ +xmlGlobalStatePtr +xmlGetGlobalState(void) +{ +#ifdef HAVE_PTHREAD_H + xmlGlobalState *globalval; + + if (libxml_is_threaded == 0) + return (NULL); + + pthread_once(&once_control, xmlOnceInit); + + if ((globalval = (xmlGlobalState *) + pthread_getspecific(globalkey)) == NULL) { + xmlGlobalState *tsd = xmlNewGlobalState(); + if (tsd == NULL) + return(NULL); + + pthread_setspecific(globalkey, tsd); + return (tsd); + } + return (globalval); +#elif defined HAVE_WIN32_THREADS +#if defined(HAVE_COMPILER_TLS) + if (!tlstate_inited) { + tlstate_inited = 1; + xmlInitializeGlobalState(&tlstate); + } + return &tlstate; +#else /* HAVE_COMPILER_TLS */ + xmlGlobalState *globalval; + xmlGlobalStateCleanupHelperParams *p; + + xmlOnceInit(); +#if defined(LIBXML_STATIC) && !defined(LIBXML_STATIC_FOR_DLL) + globalval = (xmlGlobalState *) TlsGetValue(globalkey); +#else + p = (xmlGlobalStateCleanupHelperParams *) TlsGetValue(globalkey); + globalval = (xmlGlobalState *) (p ? p->memory : NULL); +#endif + if (globalval == NULL) { + xmlGlobalState *tsd = xmlNewGlobalState(); + + if (tsd == NULL) + return(NULL); + p = (xmlGlobalStateCleanupHelperParams *) + malloc(sizeof(xmlGlobalStateCleanupHelperParams)); + if (p == NULL) { + xmlGenericError(xmlGenericErrorContext, + "xmlGetGlobalState: out of memory\n"); + xmlFreeGlobalState(tsd); + return(NULL); + } + p->memory = tsd; +#if defined(LIBXML_STATIC) && !defined(LIBXML_STATIC_FOR_DLL) + DuplicateHandle(GetCurrentProcess(), GetCurrentThread(), + GetCurrentProcess(), &p->thread, 0, TRUE, + DUPLICATE_SAME_ACCESS); + TlsSetValue(globalkey, tsd); + _beginthread(xmlGlobalStateCleanupHelper, 0, p); +#else + EnterCriticalSection(&cleanup_helpers_cs); + if (cleanup_helpers_head != NULL) { + cleanup_helpers_head->prev = p; + } + p->next = cleanup_helpers_head; + p->prev = NULL; + cleanup_helpers_head = p; + TlsSetValue(globalkey, p); + LeaveCriticalSection(&cleanup_helpers_cs); +#endif + + return (tsd); + } + return (globalval); +#endif /* HAVE_COMPILER_TLS */ +#elif defined HAVE_BEOS_THREADS + xmlGlobalState *globalval; + + xmlOnceInit(); + + if ((globalval = (xmlGlobalState *) tls_get(globalkey)) == NULL) { + xmlGlobalState *tsd = xmlNewGlobalState(); + if (tsd == NULL) + return (NULL); + + tls_set(globalkey, tsd); + on_exit_thread(xmlGlobalStateCleanup, NULL); + return (tsd); + } + return (globalval); +#else + return (NULL); +#endif +} + +/************************************************************************ + * * + * Library wide thread interfaces * + * * + ************************************************************************/ + +/** + * xmlGetThreadId: + * + * xmlGetThreadId() find the current thread ID number + * Note that this is likely to be broken on some platforms using pthreads + * as the specification doesn't mandate pthread_t to be an integer type + * + * Returns the current thread ID number + */ +int +xmlGetThreadId(void) +{ +#ifdef HAVE_PTHREAD_H + pthread_t id; + int ret; + + if (libxml_is_threaded == 0) + return (0); + id = pthread_self(); + /* horrible but preserves compat, see warning above */ + memcpy(&ret, &id, sizeof(ret)); + return (ret); +#elif defined HAVE_WIN32_THREADS + return GetCurrentThreadId(); +#elif defined HAVE_BEOS_THREADS + return find_thread(NULL); +#else + return ((int) 0); +#endif +} + +/** + * xmlIsMainThread: + * + * xmlIsMainThread() check whether the current thread is the main thread. + * + * Returns 1 if the current thread is the main thread, 0 otherwise + */ +int +xmlIsMainThread(void) +{ +#ifdef HAVE_PTHREAD_H + if (libxml_is_threaded == -1) + xmlInitThreads(); + if (libxml_is_threaded == 0) + return (1); + pthread_once(&once_control, xmlOnceInit); +#elif defined HAVE_WIN32_THREADS + xmlOnceInit(); +#elif defined HAVE_BEOS_THREADS + xmlOnceInit(); +#endif + +#ifdef DEBUG_THREADS + xmlGenericError(xmlGenericErrorContext, "xmlIsMainThread()\n"); +#endif +#ifdef HAVE_PTHREAD_H + return (pthread_equal(mainthread,pthread_self())); +#elif defined HAVE_WIN32_THREADS + return (mainthread == GetCurrentThreadId()); +#elif defined HAVE_BEOS_THREADS + return (mainthread == find_thread(NULL)); +#else + return (1); +#endif +} + +/** + * xmlLockLibrary: + * + * xmlLockLibrary() is used to take out a re-entrant lock on the libxml2 + * library. + */ +void +xmlLockLibrary(void) +{ +#ifdef DEBUG_THREADS + xmlGenericError(xmlGenericErrorContext, "xmlLockLibrary()\n"); +#endif + xmlRMutexLock(xmlLibraryLock); +} + +/** + * xmlUnlockLibrary: + * + * xmlUnlockLibrary() is used to release a re-entrant lock on the libxml2 + * library. + */ +void +xmlUnlockLibrary(void) +{ +#ifdef DEBUG_THREADS + xmlGenericError(xmlGenericErrorContext, "xmlUnlockLibrary()\n"); +#endif + xmlRMutexUnlock(xmlLibraryLock); +} + +/** + * xmlInitThreads: + * + * xmlInitThreads() is used to to initialize all the thread related + * data of the libxml2 library. + */ +void +xmlInitThreads(void) +{ +#ifdef HAVE_PTHREAD_H +#ifdef XML_PTHREAD_WEAK + if (libxml_is_threaded == -1) { + if ((pthread_once != NULL) && + (pthread_getspecific != NULL) && + (pthread_setspecific != NULL) && + (pthread_key_create != NULL) && + (pthread_key_delete != NULL) && + (pthread_mutex_init != NULL) && + (pthread_mutex_destroy != NULL) && + (pthread_mutex_lock != NULL) && + (pthread_mutex_unlock != NULL) && + (pthread_cond_init != NULL) && + (pthread_cond_destroy != NULL) && + (pthread_cond_wait != NULL) && + (pthread_equal != NULL) && + (pthread_self != NULL) && + (pthread_cond_signal != NULL)) { + libxml_is_threaded = 1; + +/* fprintf(stderr, "Running multithreaded\n"); */ + } else { + +/* fprintf(stderr, "Running without multithread\n"); */ + libxml_is_threaded = 0; + } + } +#endif /* XML_PTHREAD_WEAK */ +#elif defined(HAVE_WIN32_THREADS) && !defined(HAVE_COMPILER_TLS) && (!defined(LIBXML_STATIC) || defined(LIBXML_STATIC_FOR_DLL)) + InitializeCriticalSection(&cleanup_helpers_cs); +#endif +} + +/** + * xmlCleanupThreads: + * + * xmlCleanupThreads() is used to to cleanup all the thread related + * data of the libxml2 library once processing has ended. + * + * WARNING: if your application is multithreaded or has plugin support + * calling this may crash the application if another thread or + * a plugin is still using libxml2. It's sometimes very hard to + * guess if libxml2 is in use in the application, some libraries + * or plugins may use it without notice. In case of doubt abstain + * from calling this function or do it just before calling exit() + * to avoid leak reports from valgrind ! + */ +void +xmlCleanupThreads(void) +{ +#ifdef DEBUG_THREADS + xmlGenericError(xmlGenericErrorContext, "xmlCleanupThreads()\n"); +#endif +#ifdef HAVE_PTHREAD_H + if (libxml_is_threaded != 0) + pthread_key_delete(globalkey); + once_control = once_control_init; +#elif defined(HAVE_WIN32_THREADS) && !defined(HAVE_COMPILER_TLS) && (!defined(LIBXML_STATIC) || defined(LIBXML_STATIC_FOR_DLL)) + if (globalkey != TLS_OUT_OF_INDEXES) { + xmlGlobalStateCleanupHelperParams *p; + + EnterCriticalSection(&cleanup_helpers_cs); + p = cleanup_helpers_head; + while (p != NULL) { + xmlGlobalStateCleanupHelperParams *temp = p; + + p = p->next; + xmlFreeGlobalState(temp->memory); + free(temp); + } + cleanup_helpers_head = 0; + LeaveCriticalSection(&cleanup_helpers_cs); + TlsFree(globalkey); + globalkey = TLS_OUT_OF_INDEXES; + } + DeleteCriticalSection(&cleanup_helpers_cs); +#endif +} + +#ifdef LIBXML_THREAD_ENABLED + +/** + * xmlOnceInit + * + * xmlOnceInit() is used to initialize the value of mainthread for use + * in other routines. This function should only be called using + * pthread_once() in association with the once_control variable to ensure + * that the function is only called once. See man pthread_once for more + * details. + */ +static void +xmlOnceInit(void) +{ +#ifdef HAVE_PTHREAD_H + (void) pthread_key_create(&globalkey, xmlFreeGlobalState); + mainthread = pthread_self(); + __xmlInitializeDict(); +#elif defined(HAVE_WIN32_THREADS) + if (!run_once.done) { + if (InterlockedIncrement(&run_once.control) == 1) { +#if !defined(HAVE_COMPILER_TLS) + globalkey = TlsAlloc(); +#endif + mainthread = GetCurrentThreadId(); + __xmlInitializeDict(); + run_once.done = 1; + } else { + /* Another thread is working; give up our slice and + * wait until they're done. */ + while (!run_once.done) + Sleep(0); + } + } +#elif defined HAVE_BEOS_THREADS + if (atomic_add(&run_once_init, 1) == 0) { + globalkey = tls_allocate(); + tls_set(globalkey, NULL); + mainthread = find_thread(NULL); + __xmlInitializeDict(); + } else + atomic_add(&run_once_init, -1); +#endif +} +#endif + +/** + * DllMain: + * @hinstDLL: handle to DLL instance + * @fdwReason: Reason code for entry + * @lpvReserved: generic pointer (depends upon reason code) + * + * Entry point for Windows library. It is being used to free thread-specific + * storage. + * + * Returns TRUE always + */ +#ifdef HAVE_PTHREAD_H +#elif defined(HAVE_WIN32_THREADS) && !defined(HAVE_COMPILER_TLS) && (!defined(LIBXML_STATIC) || defined(LIBXML_STATIC_FOR_DLL)) +#if defined(LIBXML_STATIC_FOR_DLL) +int XMLCALL +xmlDllMain(ATTRIBUTE_UNUSED void *hinstDLL, unsigned long fdwReason, + ATTRIBUTE_UNUSED void *lpvReserved) +#else +/* declare to avoid "no previous prototype for 'DllMain'" warning */ +/* Note that we do NOT want to include this function declaration in + a public header because it's meant to be called by Windows itself, + not a program that uses this library. This also has to be exported. */ + +XMLPUBFUN BOOL WINAPI +DllMain (HINSTANCE hinstDLL, + DWORD fdwReason, + LPVOID lpvReserved); + +BOOL WINAPI +DllMain(ATTRIBUTE_UNUSED HINSTANCE hinstDLL, DWORD fdwReason, + ATTRIBUTE_UNUSED LPVOID lpvReserved) +#endif +{ + switch (fdwReason) { + case DLL_THREAD_DETACH: + if (globalkey != TLS_OUT_OF_INDEXES) { + xmlGlobalState *globalval = NULL; + xmlGlobalStateCleanupHelperParams *p = + (xmlGlobalStateCleanupHelperParams *) + TlsGetValue(globalkey); + globalval = (xmlGlobalState *) (p ? p->memory : NULL); + if (globalval) { + xmlFreeGlobalState(globalval); + TlsSetValue(globalkey, NULL); + } + if (p) { + EnterCriticalSection(&cleanup_helpers_cs); + if (p == cleanup_helpers_head) + cleanup_helpers_head = p->next; + else + p->prev->next = p->next; + if (p->next != NULL) + p->next->prev = p->prev; + LeaveCriticalSection(&cleanup_helpers_cs); + free(p); + } + } + break; + } + return TRUE; +} +#endif +#define bottom_threads +#include "elfgcchack.h" -- cgit v1.2.3