Edit

kc3-lang/libxml2/threads.c

Branch :

  • Show log

    Commit

  • Author : Daniel Veillard
    Date : 2003-09-29 13:20:24
    Hash : a9cce9cd
    Message : Okay this is scary but it is just adding a configure option to disable * HTMLtree.c SAX2.c c14n.c catalog.c configure.in debugXML.c encoding.c entities.c nanoftp.c nanohttp.c parser.c relaxng.c testAutomata.c testC14N.c testHTML.c testRegexp.c testRelax.c testSchemas.c testXPath.c threads.c tree.c valid.c xmlIO.c xmlcatalog.c xmllint.c xmlmemory.c xmlreader.c xmlschemas.c example/gjobread.c include/libxml/HTMLtree.h include/libxml/c14n.h include/libxml/catalog.h include/libxml/debugXML.h include/libxml/entities.h include/libxml/nanohttp.h include/libxml/relaxng.h include/libxml/tree.h include/libxml/valid.h include/libxml/xmlIO.h include/libxml/xmlschemas.h include/libxml/xmlversion.h.in include/libxml/xpathInternals.h python/libxml.c: Okay this is scary but it is just adding a configure option to disable output, this touches most of the files. Daniel

  • threads.c
  • /**
     * 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
     */
    
    #define IN_LIBXML
    #include "libxml.h"
    
    #include <string.h>
    
    #include <libxml/threads.h>
    #include <libxml/globals.h>
    
    #ifdef HAVE_SYS_TYPES_H
    #include <sys/types.h>
    #endif
    #ifdef HAVE_UNISTD_H
    #include <unistd.h>
    #endif
    #ifdef HAVE_STDLIB_H
    #include <stdlib.h>
    #endif
    #ifdef HAVE_PTHREAD_H
    #include <pthread.h>
    #endif
    
    #ifdef HAVE_WIN32_THREADS
    #include <windows.h>
    #ifndef HAVE_COMPILER_TLS
    #include <process.h>
    #endif
    #endif
    
    #if defined(SOLARIS)
    #include <note.h>
    #endif
    
    /* #define DEBUG_THREADS */
    
    /*
     * TODO: this module still uses malloc/free and not xmlMalloc/xmlFree
     *       to avoid some crazyness 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;
    #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;
    #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;
    #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 int run_once_init = 1;
    #endif /* HAVE_WIN32_THREADS */
    
    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
        pthread_mutex_init(&tok->lock, NULL);
    #elif defined HAVE_WIN32_THREADS
        tok->mutex = CreateMutex(NULL, FALSE, NULL);
    #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
        pthread_mutex_destroy(&tok->lock);
    #elif defined HAVE_WIN32_THREADS
        CloseHandle(tok->mutex);
    #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
        pthread_mutex_lock(&tok->lock);
    #elif defined HAVE_WIN32_THREADS
        WaitForSingleObject(tok->mutex, INFINITE);
    #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
        pthread_mutex_unlock(&tok->lock);
    #elif defined HAVE_WIN32_THREADS
        ReleaseMutex(tok->mutex);
    #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
        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;
    #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)
    {
    #ifdef HAVE_PTHREAD_H
        pthread_mutex_destroy(&tok->lock);
    #elif defined HAVE_WIN32_THREADS
        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 (tok == NULL)
            return;
    #ifdef HAVE_PTHREAD_H
        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;
    #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
        pthread_mutex_lock(&tok->lock);
        tok->held--;
        if (tok->held == 0) {
            if (tok->waiters)
                pthread_cond_signal(&tok->cv);
            tok->tid = 0;
        }
        pthread_mutex_unlock(&tok->lock);
    #elif defined HAVE_WIN32_THREADS
        if (!--tok->count) 
    	LeaveCriticalSection(&tok->cs);
    #endif
    }
    
    /************************************************************************
     *									*
     *			Per thread global state handling		*
     *									*
     ************************************************************************/
    
    #ifdef LIBXML_THREAD_ENABLED
    /**
     * 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)
    {
        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)
    	return(NULL);
    
        memset(gs, 0, sizeof(xmlGlobalState));
        xmlInitializeGlobalState(gs);
        return (gs);
    }
    #endif /* LIBXML_THREAD_ENABLED */
    
    
    #ifdef HAVE_WIN32_THREADS
    #if !defined(HAVE_COMPILER_TLS) && defined(LIBXML_STATIC)
    typedef struct _xmlGlobalStateCleanupHelperParams
    {
        HANDLE thread;
        void *memory;
    } xmlGlobalStateCleanupHelperParams;
    
    static void xmlGlobalStateCleanupHelper (void *p)
    {
        xmlGlobalStateCleanupHelperParams *params = (xmlGlobalStateCleanupHelperParams *) p;
        WaitForSingleObject(params->thread, INFINITE);
        CloseHandle(params->thread);
        xmlFreeGlobalState(params->memory);
        free(params);
        _endthread();
    }
    #endif /* HAVE_COMPILER_TLS && LIBXML_STATIC */
    #endif /* HAVE_WIN32_THREADS */
    
    /**
     * 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);
    
        if ((globalval = (xmlGlobalState *)
    		pthread_getspecific(globalkey)) == NULL) {
            xmlGlobalState *tsd = xmlNewGlobalState();
    
            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;
    
        if (run_once_init) { 
    	run_once_init = 0; 
    	xmlOnceInit(); 
        }
        if ((globalval = (xmlGlobalState *) TlsGetValue(globalkey)) == NULL) {
    	xmlGlobalState *tsd = xmlNewGlobalState();
    #if defined(LIBXML_STATIC)
    	xmlGlobalStateCleanupHelperParams *p = 
    	    (xmlGlobalStateCleanupHelperParams *) malloc(sizeof(xmlGlobalStateCleanupHelperParams));
    	p->memory = tsd;
    	DuplicateHandle(GetCurrentProcess(), GetCurrentThread(), 
    		GetCurrentProcess(), &p->thread, 0, TRUE, DUPLICATE_SAME_ACCESS);
    #endif
    	TlsSetValue(globalkey, tsd);
    #if defined(LIBXML_STATIC)
    	_beginthread(xmlGlobalStateCleanupHelper, 0, p);
    #endif
    
    	return (tsd);
        }
        return (globalval);
    #endif /* HAVE_COMPILER_TLS */
    #else
        return(NULL);
    #endif
    }
    
    /************************************************************************
     *									*
     *			Library wide thread interfaces			*
     *									*
     ************************************************************************/
    
    /**
     * xmlGetThreadId:
     *
     * xmlGetThreadId() find the current thread ID number
     *
     * Returns the current thread ID number
     */
    int
    xmlGetThreadId(void)
    {
    #ifdef HAVE_PTHREAD_H
        return((int) pthread_self());
    #elif defined HAVE_WIN32_THREADS
        return GetCurrentThreadId();
    #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
        pthread_once(&once_control, xmlOnceInit);
    #elif defined HAVE_WIN32_THREADS
        if (run_once_init) { 
    	run_once_init = 0; 
    	xmlOnceInit (); 
        }
    #endif
            
    #ifdef DEBUG_THREADS
        xmlGenericError(xmlGenericErrorContext, "xmlIsMainThread()\n");
    #endif
    #ifdef HAVE_PTHREAD_H
        return(mainthread == pthread_self());
    #elif defined HAVE_WIN32_THREADS
        return(mainthread == GetCurrentThreadId ());
    #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 DEBUG_THREADS
        xmlGenericError(xmlGenericErrorContext, "xmlInitThreads()\n");
    #endif
    }
    
    /**
     * xmlCleanupThreads:
     *
     * xmlCleanupThreads() is used to to cleanup all the thread related
     * data of the libxml2 library once processing has ended.
     */
    void
    xmlCleanupThreads(void)
    {
    #ifdef DEBUG_THREADS
        xmlGenericError(xmlGenericErrorContext, "xmlCleanupThreads()\n");
    #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();
    #endif
    
    #if defined(HAVE_WIN32_THREADS)
    #if !defined(HAVE_COMPILER_TLS)
        globalkey = TlsAlloc();
    #endif
        mainthread = GetCurrentThreadId();
    #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
     */
    #if defined(HAVE_WIN32_THREADS) && !defined(LIBXML_STATIC)
    BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved) 
    {
        switch(fdwReason) {
        case DLL_THREAD_DETACH:
    	if (globalkey != TLS_OUT_OF_INDEXES) {
    	    xmlGlobalState *globalval = (xmlGlobalState *)TlsGetValue(globalkey);
    	    if (globalval) {
    		xmlFreeGlobalState(globalval);
    		TlsSetValue(globalkey, NULL);
    	    }
    	}
    	break;
        case DLL_PROCESS_DETACH:
    	if (globalkey != TLS_OUT_OF_INDEXES) {
    	    TlsFree(globalkey);
    	    globalkey = TLS_OUT_OF_INDEXES;
    	}
    	break;
        }
        return TRUE;
    }
    #endif