Skip to content
Snippets Groups Projects
threads.c 26.7 KiB
Newer Older
  • Learn to ignore specific revisions
  •  * threads.c: set of generic threading related routines
    
     *
     * See Copyright for the status of this software.
     *
     * Gary Pennington <Gary.Pennington@uk.sun.com>
     * daniel@veillard.com
     */
    
    
    #include <stdlib.h>
    
    
    #include <libxml/threads.h>
    #include <libxml/globals.h>
    
    #ifdef HAVE_PTHREAD_H
    #include <pthread.h>
    
    #elif defined HAVE_WIN32_THREADS
    
    #define WIN32_LEAN_AND_MEAN
    
    #ifndef HAVE_COMPILER_TLS
    
    #ifdef HAVE_BEOS_THREADS
    #include <OS.h>
    #include <TLS.h>
    #endif
    
    
    #if defined(__GNUC__) && (__GNUC__ * 100 + __GNUC_MINOR__ >= 303) && \
        defined(__GLIBC__) && defined(__linux__)
    
    
    
    #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__ */
    
    
    /*
     * 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;
    
        CRITICAL_SECTION cs;
    
    #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;
    
        xmlMutexPtr lock;
        thread_id tid;
        int32 count;
    
    /*
     * This module still has some internal static data.
     *   - xmlLibraryLock a global lock
     *   - globalkey used for per-thread data
     */
    
    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;
    
    #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 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;
    
    static xmlRMutexPtr xmlLibraryLock = NULL;
    
    
     *
     * 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
    
            pthread_mutex_init(&tok->lock, NULL);
    
        InitializeCriticalSection(&tok->cs);
    
        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)
    {
    
            pthread_mutex_destroy(&tok->lock);
    
        DeleteCriticalSection(&tok->cs);
    
    #endif
        free(tok);
    }
    
    /**
     * xmlMutexLock:
     * @tok:  the simple mutex
     *
     * xmlMutexLock() is used to lock a libxml2 token.
     */
    void
    
        EnterCriticalSection(&tok->cs);
    
        if (acquire_sem(tok->sem) != B_NO_ERROR) {
    
            xmlGenericError(xmlGenericErrorContext,
    
                            "xmlMutexLock():BeOS:Couldn't acquire semaphore\n");
    
    #endif
    
    }
    
    /**
     * xmlMutexUnlock:
     * @tok:  the simple mutex
     *
     * xmlMutexUnlock() is used to unlock a libxml2 token.
     */
    void
    
            pthread_mutex_unlock(&tok->lock);
    
        LeaveCriticalSection(&tok->cs);
    
        if (tok->tid == find_thread(NULL)) {
            tok->tid = -1;
            release_sem(tok->sem);
        }
    
     *
     * 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
    
            pthread_mutex_init(&tok->lock, NULL);
            tok->held = 0;
            tok->waiters = 0;
            pthread_cond_init(&tok->cv, NULL);
    
        InitializeCriticalSection(&tok->cs);
    
        if ((tok->lock = xmlNewMutex()) == NULL) {
            free(tok);
            return NULL;
        }
        tok->count = 0;
    
     * @tok:  the reentrant mutex
     *
     * xmlRFreeMutex() is used to reclaim resources associated with a
     * reentrant mutex.
     */
    void
    
    xmlFreeRMutex(xmlRMutexPtr tok ATTRIBUTE_UNUSED)
    
            pthread_mutex_destroy(&tok->lock);
    
            pthread_cond_destroy(&tok->cv);
    
        DeleteCriticalSection(&tok->cs);
    
    #endif
        free(tok);
    }
    
    /**
     * xmlRMutexLock:
     * @tok:  the reentrant mutex
     *
     * xmlRMutexLock() is used to lock a libxml2 token_r.
     */
    void
    
    xmlRMutexLock(xmlRMutexPtr tok)
    
        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);
    
        EnterCriticalSection(&tok->cs);
    
        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 (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);
    
        LeaveCriticalSection(&tok->cs);
    
        if (tok->lock->tid == find_thread(NULL)) {
            tok->count--;
            if (tok->count == 0) {
                xmlMutexUnlock(tok->lock);
            }
            return;
        }
    
    /**
     * 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)
    
    
        /* 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);
    
        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;
    
    /************************************************************************
     *									*
     *			Per thread global state handling		*
     *									*
     ************************************************************************/
    
    
    #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);
        }
    
    William M. Brack's avatar
    William M. Brack committed
        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;
    
    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 {
    
        struct _xmlGlobalStateCleanupHelperParams *prev;
        struct _xmlGlobalStateCleanupHelperParams *next;
    
    static xmlGlobalStateCleanupHelperParams *cleanup_helpers_head = NULL;
    
    static CRITICAL_SECTION cleanup_helpers_cs;
    
    #endif /* LIBXMLSTATIC && !LIBXML_STATIC_FOR_DLL */
    #endif /* HAVE_COMPILER_TLS */
    
    /**
     * xmlGlobalStateCleanup:
     * @data: unused parameter
     *
     * Used for Beos only
     */
    
    void
    xmlGlobalStateCleanup(void *data)
    
        void *globalval = tls_get(globalkey);
    
        if (globalval != NULL)
            xmlFreeGlobalState(globalval);
    
    /**
     * 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;
    
    
        pthread_once(&once_control, xmlOnceInit);
    
    
             pthread_getspecific(globalkey)) == NULL) {
    
            xmlGlobalState *tsd = xmlNewGlobalState();
    
    	if (tsd == NULL)
    	    return(NULL);
    
    
            pthread_setspecific(globalkey, tsd);
            return (tsd);
    
    #if defined(HAVE_COMPILER_TLS)
        if (!tlstate_inited) {
    
            tlstate_inited = 1;
            xmlInitializeGlobalState(&tlstate);
    
        }
        return &tlstate;
    #else /* HAVE_COMPILER_TLS */
        xmlGlobalState *globalval;
    
        xmlGlobalStateCleanupHelperParams *p;
    
    #if defined(LIBXML_STATIC) && !defined(LIBXML_STATIC_FOR_DLL)
    
        globalval = (xmlGlobalState *) TlsGetValue(globalkey);
    
        p = (xmlGlobalStateCleanupHelperParams *) TlsGetValue(globalkey);
        globalval = (xmlGlobalState *) (p ? p->memory : NULL);
    
            xmlGlobalState *tsd = xmlNewGlobalState();
    
            if (tsd == NULL)
    	    return(NULL);
            p = (xmlGlobalStateCleanupHelperParams *)
                malloc(sizeof(xmlGlobalStateCleanupHelperParams));
    	if (p == NULL) {
                xmlGenericError(xmlGenericErrorContext,
                                "xmlGetGlobalState: out of memory\n");
    
    #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);
    
            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);
    
        }
        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);
    
    #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
    
        id = pthread_self();
        /* horrible but preserves compat, see warning above */
        memcpy(&ret, &id, sizeof(ret));
        return (ret);
    
        return GetCurrentThreadId();
    
     * 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)
    {
    
        if (libxml_is_threaded == -1)
            xmlInitThreads();
        if (libxml_is_threaded == 0)
    
        pthread_once(&once_control, xmlOnceInit);
    
    #ifdef DEBUG_THREADS
        xmlGenericError(xmlGenericErrorContext, "xmlIsMainThread()\n");
    #endif
    #ifdef HAVE_PTHREAD_H
    
        return (pthread_equal(mainthread,pthread_self()));
    
        return (mainthread == GetCurrentThreadId());
    
        return (mainthread == find_thread(NULL));
    
     * 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:
     *
    
     * DEPRECATED: This function will be made private. Call xmlInitParser to
     * initialize the library.
     *
    
     * xmlInitThreads() is used to to initialize all the thread related
     * data of the libxml2 library.
     */
    void
    xmlInitThreads(void)
    {
    
    #ifdef XML_PTHREAD_WEAK
    
        if (libxml_is_threaded == -1) {
            if ((pthread_once != NULL) &&
    
                (pthread_getspecific != NULL) &&
                (pthread_setspecific != NULL) &&
                (pthread_key_create != 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_self != NULL) &&
                (pthread_cond_signal != NULL)) {
    
    /* fprintf(stderr, "Running multithreaded\n"); */
    
    /* fprintf(stderr, "Running without multithread\n"); */
    
    #endif /* XML_PTHREAD_WEAK */
    
     * DEPRECATED: This function will be made private. 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.
     *
    
     * 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 !
    
    #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)
    #if !defined(HAVE_COMPILER_TLS)
    
        if (globalkey != TLS_OUT_OF_INDEXES) {
    
    #if !defined(LIBXML_STATIC) || defined(LIBXML_STATIC_FOR_DLL)
    
            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;
    
    #if !defined(LIBXML_STATIC) || defined(LIBXML_STATIC_FOR_DLL)
    
        DeleteCriticalSection(&cleanup_helpers_cs);
    
        run_once.done = 0;
        run_once.control = 0;
    
    /**
     * 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
    
        (void) pthread_key_create(&globalkey, xmlFreeGlobalState);
    
        __xmlInitializeDict();
    
    #elif defined(HAVE_WIN32_THREADS)
    
            if (InterlockedIncrement(&run_once.control) == 1) {
    
    #if !defined(HAVE_COMPILER_TLS)
    
    #if !defined(LIBXML_STATIC) || defined(LIBXML_STATIC_FOR_DLL)
                InitializeCriticalSection(&cleanup_helpers_cs);
    #endif
    
                mainthread = GetCurrentThreadId();
    
    	    __xmlInitializeDict();
    
                /* 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);
    
     * 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.
    
    #ifdef HAVE_PTHREAD_H
    #elif defined(HAVE_WIN32_THREADS) && !defined(HAVE_COMPILER_TLS) && (!defined(LIBXML_STATIC) || defined(LIBXML_STATIC_FOR_DLL))