1
0
mirror of https://gitlab.gnome.org/GNOME/libxml2.git synced 2024-10-26 12:25:09 +03:00

threads: Use pthread_once and InitOnceExecuteOnce

Static initialization flags aren't thread-safe. Also avoids an
allocation on Windows.

TODO: Breaks xmllint on Windows.
This commit is contained in:
Nick Wellnhofer 2024-07-15 23:50:08 +02:00
parent be250b798f
commit 5f3f66c683
3 changed files with 80 additions and 139 deletions

View File

@ -3227,6 +3227,7 @@ xmlCatalogCleanup(void) {
void
xmlCleanupCatalogInternal(void) {
xmlFreeRMutex(xmlCatalogMutex);
xmlCatalogMutex = NULL;
}
/**

View File

@ -90,8 +90,6 @@ XML_GLOBALS_TREE
#undef XML_OP
};
static int parserInitialized;
/*
* Mutex to protect "ForNewThreads" variables
*/
@ -606,8 +604,6 @@ void xmlCleanupGlobalsInternal(void) {
}
#endif
#endif
parserInitialized = 0;
}
/**
@ -636,10 +632,10 @@ xmlGetGlobalState(void)
static int
xmlIsMainThreadInternal(void) {
if (parserInitialized == 0) {
xmlInitParser();
parserInitialized = 1;
}
/*
* Make sure that mainthread is initialized.
*/
xmlInitParser();
#ifdef HAVE_POSIX_THREADS
return (pthread_equal(mainthread, pthread_self()));

206
threads.c
View File

@ -374,90 +374,52 @@ xmlCleanupThreads(void)
************************************************************************/
static int xmlParserInitialized = 0;
static int xmlParserInnerInitialized = 0;
#ifdef HAVE_POSIX_THREADS
static pthread_mutex_t global_init_lock = PTHREAD_MUTEX_INITIALIZER;
static pthread_once_t onceControl = PTHREAD_ONCE_INIT;
#elif defined HAVE_WIN32_THREADS
static volatile LPCRITICAL_SECTION global_init_lock = NULL;
static INIT_ONCE onceControl = INIT_ONCE_STATIC_INIT;
#else
static int onceControl = 0;
#endif
/**
* xmlGlobalInitMutexLock
*
* Makes sure that the global initialization mutex is initialized and
* locks it.
*/
static void
xmlGlobalInitMutexLock(void) {
#ifdef HAVE_POSIX_THREADS
/* The mutex is statically initialized, so we just lock it. */
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)
xmlAbort("libxml2: xmlInitParser: out of memory\n");
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);
#endif
}
static void
xmlGlobalInitMutexUnlock(void) {
#ifdef HAVE_POSIX_THREADS
pthread_mutex_unlock(&global_init_lock);
#elif defined HAVE_WIN32_THREADS
if (global_init_lock != NULL)
LeaveCriticalSection(global_init_lock);
xmlInitParserInternal(void) {
#if defined(_WIN32) && \
!defined(LIBXML_THREAD_ALLOC_ENABLED) && \
(!defined(LIBXML_STATIC) || defined(LIBXML_STATIC_FOR_DLL))
if (xmlFree == free)
atexit(xmlCleanupParser);
#endif
/*
* Note that the initialization code must not make memory allocations.
*/
xmlInitRandom(); /* Required by xmlInitGlobalsInternal */
xmlInitMemoryInternal();
xmlInitGlobalsInternal();
xmlInitDictInternal();
xmlInitEncodingInternal();
#if defined(LIBXML_XPATH_ENABLED)
xmlInitXPathInternal();
#endif
xmlInitIOCallbacks();
#ifdef LIBXML_CATALOG_ENABLED
xmlInitCatalogInternal();
#endif
xmlParserInitialized = 1;
}
/**
* xmlGlobalInitMutexDestroy
*
* Makes sure that the global initialization mutex is destroyed before
* application termination.
*/
static void
xmlGlobalInitMutexDestroy(void) {
#ifdef HAVE_POSIX_THREADS
#elif defined HAVE_WIN32_THREADS
if (global_init_lock != NULL) {
DeleteCriticalSection(global_init_lock);
free(global_init_lock);
global_init_lock = NULL;
}
#endif
#if defined(HAVE_WIN32_THREADS)
static BOOL
xmlInitParserWinWrapper(INIT_ONCE *initOnce ATTRIBUTE_UNUSED,
void *parameter ATTRIBUTE_UNUSED,
void **context ATTRIBUTE_UNUSED) {
xmlInitParserInternal();
return(TRUE);
}
#endif
/**
* xmlInitParser:
@ -469,63 +431,32 @@ xmlGlobalInitMutexDestroy(void) {
*/
void
xmlInitParser(void) {
/*
* Note that the initialization code must not make memory allocations.
*/
if (xmlParserInitialized != 0)
return;
xmlGlobalInitMutexLock();
if (xmlParserInnerInitialized == 0) {
#if defined(_WIN32) && \
!defined(LIBXML_THREAD_ALLOC_ENABLED) && \
(!defined(LIBXML_STATIC) || defined(LIBXML_STATIC_FOR_DLL))
if (xmlFree == free)
atexit(xmlCleanupParser);
#endif
xmlInitRandom(); /* Required by xmlInitGlobalsInternal */
xmlInitMemoryInternal();
xmlInitGlobalsInternal();
xmlInitDictInternal();
xmlInitEncodingInternal();
#if defined(LIBXML_XPATH_ENABLED)
xmlInitXPathInternal();
#endif
xmlInitIOCallbacks();
#ifdef LIBXML_CATALOG_ENABLED
xmlInitCatalogInternal();
#endif
xmlParserInnerInitialized = 1;
#ifdef HAVE_POSIX_THREADS
pthread_once(&onceControl, xmlInitParserInternal);
#elif defined(HAVE_WIN32_THREADS)
InitOnceExecuteOnce(&onceControl, xmlInitParserWinWrapper, NULL, NULL);
#else
if (onceControl == 0) {
xmlInitParserInternal();
onceControl = 1;
}
xmlGlobalInitMutexUnlock();
xmlParserInitialized = 1;
#endif
}
/**
* xmlCleanupParser:
*
* This function name is somewhat misleading. It does not clean up
* parser state, it cleans up memory allocated by the library itself.
* It is a cleanup function for the XML library. It tries to reclaim all
* related global memory allocated for the library processing.
* It doesn't deallocate any document related memory. One should
* call xmlCleanupParser() only when the process has finished using
* the library and all XML/HTML documents built with it.
* See also xmlInitParser() which has the opposite function of preparing
* the library for operations.
* This function is named somewhat misleadingly. It does not clean up
* parser state but global memory allocated by the library itself. This
* function is mainly useful to avoid false positives from memory leak
* checkers and SHOULD ONLY BE CALLED RIGHT BEFORE THE WHOLE PROCESS
* EXITS.
*
* 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 !
* WARNING: If a process is multithreaded or uses other shared or
* dynamic libraries, calling this function may cause crashes if
* another thread or library is still using libxml2. It can be very
* hard to guess if libxml2 is in use by a process. In case of doubt
* abstain from calling this function.
*/
void
xmlCleanupParser(void) {
@ -533,7 +464,6 @@ xmlCleanupParser(void) {
return;
/* These functions can call xmlFree. */
xmlCleanupCharEncodingHandlers();
#ifdef LIBXML_CATALOG_ENABLED
xmlCatalogCleanup();
@ -545,20 +475,34 @@ xmlCleanupParser(void) {
#endif
/* These functions should never call xmlFree. */
xmlCleanupDictInternal();
xmlCleanupRandom();
xmlCleanupGlobalsInternal();
/*
* Must come last. On Windows, xmlCleanupGlobalsInternal can call
* xmlFree which uses xmlMemMutex in debug mode.
* Must come last. xmlCleanupGlobalsInternal can call xmlFree which
* uses xmlMemMutex in debug mode.
*/
xmlCleanupMemoryInternal();
xmlGlobalInitMutexDestroy();
xmlParserInitialized = 0;
xmlParserInnerInitialized = 0;
/*
* This is a bit sketchy but should make reinitialization work.
*/
#ifdef HAVE_POSIX_THREADS
{
pthread_once_t tmp = PTHREAD_ONCE_INIT;
memcpy(&onceControl, &tmp, sizeof(tmp));
}
#elif defined(HAVE_WIN32_THREADS)
{
INIT_ONCE tmp = INIT_ONCE_STATIC_INIT;
memcpy(&onceControl, &tmp, sizeof(tmp));
}
#else
onceControl = 0;
#endif
}
#if defined(HAVE_FUNC_ATTRIBUTE_DESTRUCTOR) && \