/**1* threads.c: set of generic threading related routines2*3* See Copyright for the status of this software.4*5* Gary Pennington <[email protected]>6* [email protected]7*/89#define IN_LIBXML10#include "libxml.h"1112#include <string.h>13#include <stdlib.h>1415#include <libxml/threads.h>16#include <libxml/parser.h>17#ifdef LIBXML_CATALOG_ENABLED18#include <libxml/catalog.h>19#endif20#ifdef LIBXML_SCHEMAS_ENABLED21#include <libxml/xmlschemastypes.h>22#include <libxml/relaxng.h>23#endif2425#if defined(SOLARIS)26#include <note.h>27#endif2829#include "private/dict.h"30#include "private/enc.h"31#include "private/globals.h"32#include "private/memory.h"33#include "private/threads.h"34#include "private/xpath.h"3536#if defined(HAVE_POSIX_THREADS) && \37defined(__GLIBC__) && \38__GLIBC__ * 100 + __GLIBC_MINOR__ >= 2343940/*41* The modern way available since glibc 2.32.42*43* The check above is for glibc 2.34 which merged the pthread symbols into44* libc. Since we still allow linking without pthread symbols (see below),45* this only works if pthread symbols are guaranteed to be available.46*/4748#include <sys/single_threaded.h>4950#define XML_IS_THREADED() (!__libc_single_threaded)51#define XML_IS_NEVER_THREADED() 05253#elif defined(HAVE_POSIX_THREADS) && \54defined(__GLIBC__) && \55defined(__GNUC__)5657/*58* The traditional way to check for single-threaded applications with59* glibc was to check whether the separate libpthread library is60* linked in. This works by not linking libxml2 with libpthread (see61* BASE_THREAD_LIBS in configure.ac and Makefile.am) and declaring62* pthread functions as weak symbols.63*64* In glibc 2.34, the pthread symbols were moved from libpthread to libc,65* so this doesn't work anymore.66*67* At some point, this legacy code and the BASE_THREAD_LIBS hack in68* configure.ac can probably be removed.69*/7071#pragma weak pthread_mutex_init72#pragma weak pthread_mutex_destroy73#pragma weak pthread_mutex_lock74#pragma weak pthread_mutex_unlock75#pragma weak pthread_cond_init76#pragma weak pthread_cond_destroy77#pragma weak pthread_cond_wait78#pragma weak pthread_equal79#pragma weak pthread_self80#pragma weak pthread_cond_signal8182#define XML_PTHREAD_WEAK83#define XML_IS_THREADED() libxml_is_threaded84#define XML_IS_NEVER_THREADED() (!libxml_is_threaded)8586static int libxml_is_threaded = -1;8788#else /* other POSIX platforms */8990#define XML_IS_THREADED() 191#define XML_IS_NEVER_THREADED() 09293#endif9495/*96* TODO: this module still uses malloc/free and not xmlMalloc/xmlFree97* to avoid some craziness since xmlMalloc/xmlFree may actually98* be hosted on allocated blocks needing them for the allocation ...99*/100101/*102* xmlRMutex are reentrant mutual exception locks103*/104struct _xmlRMutex {105#ifdef HAVE_POSIX_THREADS106pthread_mutex_t lock;107unsigned int held;108unsigned int waiters;109pthread_t tid;110pthread_cond_t cv;111#elif defined HAVE_WIN32_THREADS112CRITICAL_SECTION cs;113#else114int empty;115#endif116};117118static xmlRMutexPtr xmlLibraryLock = NULL;119120/**121* xmlInitMutex:122* @mutex: the mutex123*124* Initialize a mutex.125*/126void127xmlInitMutex(xmlMutexPtr mutex)128{129#ifdef HAVE_POSIX_THREADS130if (XML_IS_NEVER_THREADED() == 0)131pthread_mutex_init(&mutex->lock, NULL);132#elif defined HAVE_WIN32_THREADS133InitializeCriticalSection(&mutex->cs);134#else135(void) mutex;136#endif137}138139/**140* xmlNewMutex:141*142* xmlNewMutex() is used to allocate a libxml2 token struct for use in143* synchronizing access to data.144*145* Returns a new simple mutex pointer or NULL in case of error146*/147xmlMutexPtr148xmlNewMutex(void)149{150xmlMutexPtr tok;151152if ((tok = malloc(sizeof(xmlMutex))) == NULL)153return (NULL);154xmlInitMutex(tok);155return (tok);156}157158/**159* xmlCleanupMutex:160* @mutex: the simple mutex161*162* Reclaim resources associated with a mutex.163*/164void165xmlCleanupMutex(xmlMutexPtr mutex)166{167#ifdef HAVE_POSIX_THREADS168if (XML_IS_NEVER_THREADED() == 0)169pthread_mutex_destroy(&mutex->lock);170#elif defined HAVE_WIN32_THREADS171DeleteCriticalSection(&mutex->cs);172#else173(void) mutex;174#endif175}176177/**178* xmlFreeMutex:179* @tok: the simple mutex180*181* Free a mutex.182*/183void184xmlFreeMutex(xmlMutexPtr tok)185{186if (tok == NULL)187return;188189xmlCleanupMutex(tok);190free(tok);191}192193/**194* xmlMutexLock:195* @tok: the simple mutex196*197* xmlMutexLock() is used to lock a libxml2 token.198*/199void200xmlMutexLock(xmlMutexPtr tok)201{202if (tok == NULL)203return;204#ifdef HAVE_POSIX_THREADS205/*206* This assumes that __libc_single_threaded won't change while the207* lock is held.208*/209if (XML_IS_THREADED() != 0)210pthread_mutex_lock(&tok->lock);211#elif defined HAVE_WIN32_THREADS212EnterCriticalSection(&tok->cs);213#endif214215}216217/**218* xmlMutexUnlock:219* @tok: the simple mutex220*221* xmlMutexUnlock() is used to unlock a libxml2 token.222*/223void224xmlMutexUnlock(xmlMutexPtr tok)225{226if (tok == NULL)227return;228#ifdef HAVE_POSIX_THREADS229if (XML_IS_THREADED() != 0)230pthread_mutex_unlock(&tok->lock);231#elif defined HAVE_WIN32_THREADS232LeaveCriticalSection(&tok->cs);233#endif234}235236/**237* xmlNewRMutex:238*239* xmlRNewMutex() is used to allocate a reentrant mutex for use in240* synchronizing access to data. token_r is a re-entrant lock and thus useful241* for synchronizing access to data structures that may be manipulated in a242* recursive fashion.243*244* Returns the new reentrant mutex pointer or NULL in case of error245*/246xmlRMutexPtr247xmlNewRMutex(void)248{249xmlRMutexPtr tok;250251if ((tok = malloc(sizeof(xmlRMutex))) == NULL)252return (NULL);253#ifdef HAVE_POSIX_THREADS254if (XML_IS_NEVER_THREADED() == 0) {255pthread_mutex_init(&tok->lock, NULL);256tok->held = 0;257tok->waiters = 0;258pthread_cond_init(&tok->cv, NULL);259}260#elif defined HAVE_WIN32_THREADS261InitializeCriticalSection(&tok->cs);262#endif263return (tok);264}265266/**267* xmlFreeRMutex:268* @tok: the reentrant mutex269*270* xmlRFreeMutex() is used to reclaim resources associated with a271* reentrant mutex.272*/273void274xmlFreeRMutex(xmlRMutexPtr tok ATTRIBUTE_UNUSED)275{276if (tok == NULL)277return;278#ifdef HAVE_POSIX_THREADS279if (XML_IS_NEVER_THREADED() == 0) {280pthread_mutex_destroy(&tok->lock);281pthread_cond_destroy(&tok->cv);282}283#elif defined HAVE_WIN32_THREADS284DeleteCriticalSection(&tok->cs);285#endif286free(tok);287}288289/**290* xmlRMutexLock:291* @tok: the reentrant mutex292*293* xmlRMutexLock() is used to lock a libxml2 token_r.294*/295void296xmlRMutexLock(xmlRMutexPtr tok)297{298if (tok == NULL)299return;300#ifdef HAVE_POSIX_THREADS301if (XML_IS_THREADED() == 0)302return;303304pthread_mutex_lock(&tok->lock);305if (tok->held) {306if (pthread_equal(tok->tid, pthread_self())) {307tok->held++;308pthread_mutex_unlock(&tok->lock);309return;310} else {311tok->waiters++;312while (tok->held)313pthread_cond_wait(&tok->cv, &tok->lock);314tok->waiters--;315}316}317tok->tid = pthread_self();318tok->held = 1;319pthread_mutex_unlock(&tok->lock);320#elif defined HAVE_WIN32_THREADS321EnterCriticalSection(&tok->cs);322#endif323}324325/**326* xmlRMutexUnlock:327* @tok: the reentrant mutex328*329* xmlRMutexUnlock() is used to unlock a libxml2 token_r.330*/331void332xmlRMutexUnlock(xmlRMutexPtr tok ATTRIBUTE_UNUSED)333{334if (tok == NULL)335return;336#ifdef HAVE_POSIX_THREADS337if (XML_IS_THREADED() == 0)338return;339340pthread_mutex_lock(&tok->lock);341tok->held--;342if (tok->held == 0) {343if (tok->waiters)344pthread_cond_signal(&tok->cv);345memset(&tok->tid, 0, sizeof(tok->tid));346}347pthread_mutex_unlock(&tok->lock);348#elif defined HAVE_WIN32_THREADS349LeaveCriticalSection(&tok->cs);350#endif351}352353/************************************************************************354* *355* Library wide thread interfaces *356* *357************************************************************************/358359/**360* xmlGetThreadId:361*362* DEPRECATED: Internal function, do not use.363*364* xmlGetThreadId() find the current thread ID number365* Note that this is likely to be broken on some platforms using pthreads366* as the specification doesn't mandate pthread_t to be an integer type367*368* Returns the current thread ID number369*/370int371xmlGetThreadId(void)372{373#ifdef HAVE_POSIX_THREADS374pthread_t id;375int ret;376377if (XML_IS_THREADED() == 0)378return (0);379id = pthread_self();380/* horrible but preserves compat, see warning above */381memcpy(&ret, &id, sizeof(ret));382return (ret);383#elif defined HAVE_WIN32_THREADS384return GetCurrentThreadId();385#else386return ((int) 0);387#endif388}389390/**391* xmlLockLibrary:392*393* xmlLockLibrary() is used to take out a re-entrant lock on the libxml2394* library.395*/396void397xmlLockLibrary(void)398{399xmlRMutexLock(xmlLibraryLock);400}401402/**403* xmlUnlockLibrary:404*405* xmlUnlockLibrary() is used to release a re-entrant lock on the libxml2406* library.407*/408void409xmlUnlockLibrary(void)410{411xmlRMutexUnlock(xmlLibraryLock);412}413414/**415* xmlInitThreads:416*417* DEPRECATED: Alias for xmlInitParser.418*/419void420xmlInitThreads(void)421{422xmlInitParser();423}424425/**426* xmlCleanupThreads:427*428* DEPRECATED: This function is a no-op. Call xmlCleanupParser429* to free global state but see the warnings there. xmlCleanupParser430* should be only called once at program exit. In most cases, you don't431* have call cleanup functions at all.432*/433void434xmlCleanupThreads(void)435{436}437438/************************************************************************439* *440* Library wide initialization *441* *442************************************************************************/443444static int xmlParserInitialized = 0;445static int xmlParserInnerInitialized = 0;446447448#ifdef HAVE_POSIX_THREADS449static pthread_mutex_t global_init_lock = PTHREAD_MUTEX_INITIALIZER;450#elif defined HAVE_WIN32_THREADS451static volatile LPCRITICAL_SECTION global_init_lock = NULL;452#endif453454/**455* xmlGlobalInitMutexLock456*457* Makes sure that the global initialization mutex is initialized and458* locks it.459*/460static void461xmlGlobalInitMutexLock(void) {462#ifdef HAVE_POSIX_THREADS463464#ifdef XML_PTHREAD_WEAK465/*466* This is somewhat unreliable since libpthread could be loaded467* later with dlopen() and threads could be created. But it's468* long-standing behavior and hard to work around.469*/470if (libxml_is_threaded == -1)471libxml_is_threaded =472(pthread_mutex_init != NULL) &&473(pthread_mutex_destroy != NULL) &&474(pthread_mutex_lock != NULL) &&475(pthread_mutex_unlock != NULL) &&476(pthread_cond_init != NULL) &&477(pthread_cond_destroy != NULL) &&478(pthread_cond_wait != NULL) &&479/*480* pthread_equal can be inline, resuting in -Waddress warnings.481* Let's assume it's available if all the other functions are.482*/483/* (pthread_equal != NULL) && */484(pthread_self != NULL) &&485(pthread_cond_signal != NULL);486#endif487488/* The mutex is statically initialized, so we just lock it. */489if (XML_IS_THREADED() != 0)490pthread_mutex_lock(&global_init_lock);491492#elif defined HAVE_WIN32_THREADS493494LPCRITICAL_SECTION cs;495496/* Create a new critical section */497if (global_init_lock == NULL) {498cs = malloc(sizeof(CRITICAL_SECTION));499if (cs == NULL) {500xmlGenericError(xmlGenericErrorContext,501"xmlGlobalInitMutexLock: out of memory\n");502return;503}504InitializeCriticalSection(cs);505506/* Swap it into the global_init_lock */507#ifdef InterlockedCompareExchangePointer508InterlockedCompareExchangePointer((void **) &global_init_lock,509cs, NULL);510#else /* Use older void* version */511InterlockedCompareExchange((void **) &global_init_lock,512(void *) cs, NULL);513#endif /* InterlockedCompareExchangePointer */514515/* If another thread successfully recorded its critical516* section in the global_init_lock then discard the one517* allocated by this thread. */518if (global_init_lock != cs) {519DeleteCriticalSection(cs);520free(cs);521}522}523524/* Lock the chosen critical section */525EnterCriticalSection(global_init_lock);526527#endif528}529530static void531xmlGlobalInitMutexUnlock(void) {532#ifdef HAVE_POSIX_THREADS533if (XML_IS_THREADED() != 0)534pthread_mutex_unlock(&global_init_lock);535#elif defined HAVE_WIN32_THREADS536if (global_init_lock != NULL)537LeaveCriticalSection(global_init_lock);538#endif539}540541/**542* xmlGlobalInitMutexDestroy543*544* Makes sure that the global initialization mutex is destroyed before545* application termination.546*/547static void548xmlGlobalInitMutexDestroy(void) {549#ifdef HAVE_POSIX_THREADS550#elif defined HAVE_WIN32_THREADS551if (global_init_lock != NULL) {552DeleteCriticalSection(global_init_lock);553free(global_init_lock);554global_init_lock = NULL;555}556#endif557}558559/**560* xmlInitParser:561*562* Initialization function for the XML parser.563*564* Call once from the main thread before using the library in565* multithreaded programs.566*/567void568xmlInitParser(void) {569/*570* Note that the initialization code must not make memory allocations.571*/572if (xmlParserInitialized != 0)573return;574575xmlGlobalInitMutexLock();576577if (xmlParserInnerInitialized == 0) {578#if defined(_WIN32) && \579!defined(LIBXML_THREAD_ALLOC_ENABLED) && \580(!defined(LIBXML_STATIC) || defined(LIBXML_STATIC_FOR_DLL))581if (xmlFree == free)582atexit(xmlCleanupParser);583#endif584585xmlInitMemoryInternal(); /* Should come second */586xmlInitGlobalsInternal();587xmlInitRandom();588xmlInitDictInternal();589xmlInitEncodingInternal();590#if defined(LIBXML_XPATH_ENABLED) || defined(LIBXML_SCHEMAS_ENABLED)591xmlInitXPathInternal();592#endif593594xmlRegisterDefaultInputCallbacks();595#ifdef LIBXML_OUTPUT_ENABLED596xmlRegisterDefaultOutputCallbacks();597#endif /* LIBXML_OUTPUT_ENABLED */598599xmlParserInnerInitialized = 1;600}601602xmlGlobalInitMutexUnlock();603604xmlParserInitialized = 1;605}606607/**608* xmlCleanupParser:609*610* This function name is somewhat misleading. It does not clean up611* parser state, it cleans up memory allocated by the library itself.612* It is a cleanup function for the XML library. It tries to reclaim all613* related global memory allocated for the library processing.614* It doesn't deallocate any document related memory. One should615* call xmlCleanupParser() only when the process has finished using616* the library and all XML/HTML documents built with it.617* See also xmlInitParser() which has the opposite function of preparing618* the library for operations.619*620* WARNING: if your application is multithreaded or has plugin support621* calling this may crash the application if another thread or622* a plugin is still using libxml2. It's sometimes very hard to623* guess if libxml2 is in use in the application, some libraries624* or plugins may use it without notice. In case of doubt abstain625* from calling this function or do it just before calling exit()626* to avoid leak reports from valgrind !627*/628void629xmlCleanupParser(void) {630if (!xmlParserInitialized)631return;632633/* These functions can call xmlFree. */634635xmlCleanupCharEncodingHandlers();636#ifdef LIBXML_CATALOG_ENABLED637xmlCatalogCleanup();638#endif639#ifdef LIBXML_SCHEMAS_ENABLED640xmlSchemaCleanupTypes();641xmlRelaxNGCleanupTypes();642#endif643644/* These functions should never call xmlFree. */645646xmlCleanupInputCallbacks();647#ifdef LIBXML_OUTPUT_ENABLED648xmlCleanupOutputCallbacks();649#endif650651xmlCleanupDictInternal();652xmlCleanupRandom();653xmlCleanupGlobalsInternal();654/*655* Must come last. On Windows, xmlCleanupGlobalsInternal can call656* xmlFree which uses xmlMemMutex in debug mode.657*/658xmlCleanupMemoryInternal();659660xmlGlobalInitMutexDestroy();661662xmlParserInitialized = 0;663xmlParserInnerInitialized = 0;664}665666#if defined(HAVE_ATTRIBUTE_DESTRUCTOR) && \667!defined(LIBXML_THREAD_ALLOC_ENABLED) && \668!defined(LIBXML_STATIC) && \669!defined(_WIN32)670static void671ATTRIBUTE_DESTRUCTOR672xmlDestructor(void) {673/*674* Calling custom deallocation functions in a destructor can cause675* problems, for example with Nokogiri.676*/677if (xmlFree == free)678xmlCleanupParser();679}680#endif681682683