Edit

kc3-lang/libxml2/threads.c

Branch :

  • Show log

    Commit

  • Author : Daniel P. BerrangĂ©
    Date : 2025-06-25 15:24:24
    Hash : ac5fcb0e
    Message : relaxng: ensure thread safe global initialization Relying on a plain integer flag, with no synchronization primitives does not give thread-safe initialization. All reads & writes of the xmlSchemaTypesInitialized flag need to be protected by a mutex to ensure suitable memory barriers & thus correct ordering wrt any speculative execution. A separate internal initializer tied to xmlParserInit is used to create the mutex used for synchronization, similarly to how catalog.c works. Signed-off-by: Daniel P. Berrangé <berrange@redhat.com>

  • threads.c
  • /**
     * threads.c: set of generic threading related routines
     *
     * See Copyright for the status of this software.
     *
     * Author: Gary Pennington, Daniel Veillard
     */
    
    #define IN_LIBXML
    #include "libxml.h"
    
    #include <string.h>
    #include <stdarg.h>
    #include <stdlib.h>
    
    #include <libxml/threads.h>
    #include <libxml/parser.h>
    #ifdef LIBXML_CATALOG_ENABLED
    #include <libxml/catalog.h>
    #endif
    #ifdef LIBXML_RELAXNG_ENABLED
    #include <libxml/relaxng.h>
    #endif
    #ifdef LIBXML_SCHEMAS_ENABLED
    #include <libxml/xmlschemastypes.h>
    #endif
    
    #if defined(SOLARIS)
    #include <note.h>
    #endif
    
    #include "private/cata.h"
    #include "private/dict.h"
    #include "private/enc.h"
    #include "private/error.h"
    #include "private/globals.h"
    #include "private/io.h"
    #include "private/memory.h"
    #include "private/threads.h"
    #include "private/xpath.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 ...
     */
    
    static xmlRMutex xmlLibraryLock;
    
    /**
     * Initialize a mutex.
     *
     * @param mutex  the mutex
     */
    void
    xmlInitMutex(xmlMutex *mutex)
    {
    #ifdef HAVE_POSIX_THREADS
        pthread_mutex_init(&mutex->lock, NULL);
    #elif defined HAVE_WIN32_THREADS
        InitializeCriticalSection(&mutex->cs);
    #else
        (void) mutex;
    #endif
    }
    
    /**
     * #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
     */
    xmlMutex *
    xmlNewMutex(void)
    {
        xmlMutexPtr tok;
    
        tok = malloc(sizeof(xmlMutex));
        if (tok == NULL)
            return (NULL);
        xmlInitMutex(tok);
        return (tok);
    }
    
    /**
     * Reclaim resources associated with a mutex.
     *
     * @param mutex  the simple mutex
     */
    void
    xmlCleanupMutex(xmlMutex *mutex)
    {
    #ifdef HAVE_POSIX_THREADS
        pthread_mutex_destroy(&mutex->lock);
    #elif defined HAVE_WIN32_THREADS
        DeleteCriticalSection(&mutex->cs);
    #else
        (void) mutex;
    #endif
    }
    
    /**
     * Free a mutex.
     *
     * @param tok  the simple mutex
     */
    void
    xmlFreeMutex(xmlMutex *tok)
    {
        if (tok == NULL)
            return;
    
        xmlCleanupMutex(tok);
        free(tok);
    }
    
    /**
     * #xmlMutexLock is used to lock a libxml2 token.
     *
     * @param tok  the simple mutex
     */
    void
    xmlMutexLock(xmlMutex *tok)
    {
        if (tok == NULL)
            return;
    #ifdef HAVE_POSIX_THREADS
        /*
         * This assumes that __libc_single_threaded won't change while the
         * lock is held.
         */
        pthread_mutex_lock(&tok->lock);
    #elif defined HAVE_WIN32_THREADS
        EnterCriticalSection(&tok->cs);
    #endif
    
    }
    
    /**
     * #xmlMutexUnlock is used to unlock a libxml2 token.
     *
     * @param tok  the simple mutex
     */
    void
    xmlMutexUnlock(xmlMutex *tok)
    {
        if (tok == NULL)
            return;
    #ifdef HAVE_POSIX_THREADS
        pthread_mutex_unlock(&tok->lock);
    #elif defined HAVE_WIN32_THREADS
        LeaveCriticalSection(&tok->cs);
    #endif
    }
    
    /**
     * Initialize the mutex.
     *
     * @param tok  mutex
     */
    void
    xmlInitRMutex(xmlRMutex *tok) {
        (void) tok;
    
    #ifdef HAVE_POSIX_THREADS
        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);
    #endif
    }
    
    /**
     * 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
     */
    xmlRMutex *
    xmlNewRMutex(void)
    {
        xmlRMutexPtr tok;
    
        tok = malloc(sizeof(xmlRMutex));
        if (tok == NULL)
            return (NULL);
        xmlInitRMutex(tok);
        return (tok);
    }
    
    /**
     * Cleanup the mutex.
     *
     * @param tok  mutex
     */
    void
    xmlCleanupRMutex(xmlRMutex *tok) {
        (void) tok;
    
    #ifdef HAVE_POSIX_THREADS
        pthread_mutex_destroy(&tok->lock);
        pthread_cond_destroy(&tok->cv);
    #elif defined HAVE_WIN32_THREADS
        DeleteCriticalSection(&tok->cs);
    #endif
    }
    
    /**
     * Used to reclaim resources associated with a
     * reentrant mutex.
     *
     * @param tok  the reentrant mutex
     */
    void
    xmlFreeRMutex(xmlRMutex *tok)
    {
        if (tok == NULL)
            return;
        xmlCleanupRMutex(tok);
        free(tok);
    }
    
    /**
     * #xmlRMutexLock is used to lock a libxml2 token_r.
     *
     * @param tok  the reentrant mutex
     */
    void
    xmlRMutexLock(xmlRMutex *tok)
    {
        if (tok == NULL)
            return;
    #ifdef HAVE_POSIX_THREADS
        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);
    #endif
    }
    
    /**
     * #xmlRMutexUnlock is used to unlock a libxml2 token_r.
     *
     * @param tok  the reentrant mutex
     */
    void
    xmlRMutexUnlock(xmlRMutex *tok ATTRIBUTE_UNUSED)
    {
        if (tok == NULL)
            return;
    #ifdef HAVE_POSIX_THREADS
        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
        LeaveCriticalSection(&tok->cs);
    #endif
    }
    
    /************************************************************************
     *									*
     *			Library wide thread interfaces			*
     *									*
     ************************************************************************/
    
    /**
     * #xmlLockLibrary is used to take out a re-entrant lock on the libxml2
     * library.
     */
    void
    xmlLockLibrary(void)
    {
        xmlRMutexLock(&xmlLibraryLock);
    }
    
    /**
     * #xmlUnlockLibrary is used to release a re-entrant lock on the libxml2
     * library.
     */
    void
    xmlUnlockLibrary(void)
    {
        xmlRMutexUnlock(&xmlLibraryLock);
    }
    
    /**
     * @deprecated Alias for #xmlInitParser.
     */
    void
    xmlInitThreads(void)
    {
        xmlInitParser();
    }
    
    /**
     * @deprecated This function is a no-op. Call #xmlCleanupParser
     * to free global state but see the warnings there. #xmlCleanupParser
     * should be only called once at program exit. In most cases, you don't
     * have call cleanup functions at all.
     */
    void
    xmlCleanupThreads(void)
    {
    }
    
    static void
    xmlInitThreadsInternal(void) {
        xmlInitRMutex(&xmlLibraryLock);
    }
    
    static void
    xmlCleanupThreadsInternal(void) {
        xmlCleanupRMutex(&xmlLibraryLock);
    }
    
    /************************************************************************
     *									*
     *			Library wide initialization			*
     *									*
     ************************************************************************/
    
    static int xmlParserInitialized = 0;
    
    #ifdef HAVE_POSIX_THREADS
    static pthread_once_t onceControl = PTHREAD_ONCE_INIT;
    #elif defined HAVE_WIN32_THREADS
    static INIT_ONCE onceControl = INIT_ONCE_STATIC_INIT;
    #else
    static int onceControl = 0;
    #endif
    
    static void
    xmlInitParserInternal(void) {
        /*
         * Note that the initialization code must not make memory allocations.
         */
        xmlInitRandom(); /* Required by xmlInitGlobalsInternal */
        xmlInitMemoryInternal();
        xmlInitThreadsInternal();
        xmlInitGlobalsInternal();
        xmlInitDictInternal();
        xmlInitEncodingInternal();
    #if defined(LIBXML_XPATH_ENABLED)
        xmlInitXPathInternal();
    #endif
        xmlInitIOCallbacks();
    #ifdef LIBXML_CATALOG_ENABLED
        xmlInitCatalogInternal();
    #endif
    #ifdef LIBXML_SCHEMAS_ENABLED
        xmlInitSchemasTypesInternal();
    #endif
    #ifdef LIBXML_RELAXNG_ENABLED
        xmlInitRelaxNGInternal();
    #endif
    
        xmlParserInitialized = 1;
    }
    
    #if defined(HAVE_WIN32_THREADS)
    static BOOL WINAPI
    xmlInitParserWinWrapper(INIT_ONCE *initOnce ATTRIBUTE_UNUSED,
                            void *parameter ATTRIBUTE_UNUSED,
                            void **context ATTRIBUTE_UNUSED) {
        xmlInitParserInternal();
        return(TRUE);
    }
    #endif
    
    /**
     * Initialization function for the XML parser.
     *
     * For older versions, it's recommended to call this function once
     * from the main thread before using the library in multithreaded
     * programs.
     *
     * Since 2.14.0, there's no distinction between threads. It should
     * be unnecessary to call this function.
     */
    void
    xmlInitParser(void) {
    #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;
        }
    #endif
    }
    
    /**
     * Free global memory allocations.
     *
     * This function is named somewhat misleadingly. It does not clean up
     * parser state but frees global memory allocated by various components
     * of the library.
     *
     * Since 2.9.11, cleanup is performed automatically on most platforms
     * and there's no need at all for manual cleanup. This includes all
     * compilers and platforms that support GCC-style destructor attributes
     * as well as Windows DLLs.
     *
     * This function should only be used to avoid false positives from
     * memory leak checkers if automatic cleanup isn't possible, for
     * example with static builds on MSVC.
     *
     * WARNING: xmlCleanupParser is not thread-safe. If this function is
     * called and any threads that could make calls into libxml2 are
     * still running, memory corruption is likely to occur.
     *
     * No library calls must be made (from any thread) after calling this
     * function. In general, *this function should only be called right
     * before the whole process exits.* Calling this function too early
     * will lead to memory corruption.
     */
    void
    xmlCleanupParser(void) {
        /*
         * Unfortunately, some users call this function to fix memory
         * leaks on unload with versions before 2.9.11. This can result
         * in the library being reinitialized, so this use case must
         * be supported.
         */
        if (!xmlParserInitialized)
            return;
    
        xmlCleanupCharEncodingHandlers();
    #ifdef LIBXML_CATALOG_ENABLED
        xmlCatalogCleanup();
        xmlCleanupCatalogInternal();
    #endif
    #ifdef LIBXML_SCHEMAS_ENABLED
        xmlSchemaCleanupTypes();
    #endif
    #ifdef LIBXML_RELAXNG_ENABLED
        xmlRelaxNGCleanupTypes();
    #endif
    
    #ifdef LIBXML_SCHEMAS_ENABLED
        /* Must be after xmlRelaxNGCleanupTypes */
        xmlCleanupSchemasTypesInternal();
    #endif
    #ifdef LIBXML_RELAXNG_ENABLED
        xmlCleanupRelaxNGInternal();
    #endif
    
        xmlCleanupDictInternal();
        xmlCleanupRandom();
        xmlCleanupGlobalsInternal();
        xmlCleanupThreadsInternal();
    
        /*
         * Must come after all cleanup functions that call xmlFree which
         * uses xmlMemMutex in debug mode.
         */
        xmlCleanupMemoryInternal();
    
        xmlParserInitialized = 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) && \
        !defined(LIBXML_THREAD_ALLOC_ENABLED) && \
        !defined(LIBXML_STATIC) && \
        !defined(_WIN32)
    static void
    ATTRIBUTE_DESTRUCTOR
    xmlDestructor(void) {
        /*
         * Calling custom deallocation functions in a destructor can cause
         * problems, for example with Nokogiri.
         */
        if (xmlFree == free)
            xmlCleanupParser();
    }
    #endif