threads.c 11.4 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
/**
 * 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 "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
29 30 31 32 33 34
#ifdef HAVE_WIN32_THREADS
#include <windows.h>
#ifndef _MSC_VER
#include <process.h>
#endif
#endif
35 36 37 38 39

#if defined(SOLARIS)
#include <note.h>
#endif

40
/* #define DEBUG_THREADS */
41 42 43 44 45 46 47 48 49 50 51 52 53

/*
 * 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;
54 55
#elif defined HAVE_WIN32_THREADS
	HANDLE mutex;
56 57 58 59 60 61 62 63 64 65 66 67 68 69 70
#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;
71 72 73
#elif defined HAVE_WIN32_THREADS
	CRITICAL_SECTION cs;
	unsigned int count;
74 75 76 77 78 79 80 81 82
#else
    int empty;
#endif
};
/*
 * This module still has some internal static data.
 *   - xmlLibraryLock a global lock
 *   - globalkey used for per-thread data
 */
83

84 85
#ifdef HAVE_PTHREAD_H
static pthread_key_t	globalkey;
86
static pthread_t	mainthread;
87
static pthread_once_t once_control = PTHREAD_ONCE_INIT;
88 89 90 91 92 93 94 95 96 97
#elif defined HAVE_WIN32_THREADS
#ifdef _MSC_VER
static __declspec (thread) xmlGlobalState tlstate;
static __declspec (thread) int tlstate_inited = 0;
#else
static DWORD globalkey;
#endif /* _MSC_VER */
static DWORD mainthread;
static int run_once_init = 1;
#endif /* HAVE_WIN32_THREADS */
98
static xmlRMutexPtr	xmlLibraryLock = NULL;
99
static void xmlOnceInit(void);
100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117

/**
 * xmlMutexPtr:
 *
 * 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);
118 119
#elif defined HAVE_WIN32_THREADS
	tok->mutex = CreateMutex (NULL, FALSE, NULL);
120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135
#endif
    return (tok);
}

/**
 * xmlFreeMutex:
 * @tok:  the simple mutex
 *
 * xmlFreeMutex() is used to reclaim resources associated with a libxml2 token
 * struct.
 */
void
xmlFreeMutex(xmlMutexPtr tok)
{
#ifdef HAVE_PTHREAD_H
    pthread_mutex_destroy(&tok->lock);
136 137
#elif defined HAVE_WIN32_THREADS
	CloseHandle (tok->mutex);
138 139 140 141 142 143 144 145 146 147 148 149 150 151 152
#endif
    free(tok);
}

/**
 * xmlMutexLock:
 * @tok:  the simple mutex
 *
 * xmlMutexLock() is used to lock a libxml2 token.
 */
void
xmlMutexLock(xmlMutexPtr tok)
{
#ifdef HAVE_PTHREAD_H
    pthread_mutex_lock(&tok->lock);
153 154
#elif defined HAVE_WIN32_THREADS
	WaitForSingleObject (tok->mutex, INFINITE);
155 156 157 158 159 160 161 162 163 164 165 166 167 168 169
#endif

}

/**
 * xmlMutexUnlock:
 * @tok:  the simple mutex
 *
 * xmlMutexUnlock() is used to unlock a libxml2 token.
 */
void
xmlMutexUnlock(xmlMutexPtr tok)
{
#ifdef HAVE_PTHREAD_H
    pthread_mutex_unlock(&tok->lock);
170 171
#elif defined HAVE_WIN32_THREADS
	ReleaseMutex (tok->mutex);
172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195
#endif
}

/**
 * xmlRNewMutex:
 *
 * 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;
196 197 198
#elif defined HAVE_WIN32_THREADS
	InitializeCriticalSection (&tok->cs);
	tok->count = 0;
199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214
#endif
    return (tok);
}

/**
 * xmlRFreeMutex:
 * @tok:  the reentrant mutex
 *
 * xmlRFreeMutex() is used to reclaim resources associated with a
 * reentrant mutex.
 */
void
xmlFreeRMutex(xmlRMutexPtr tok)
{
#ifdef HAVE_PTHREAD_H
    pthread_mutex_destroy(&tok->lock);
215 216
#elif defined HAVE_WIN32_THREADS
	DeleteCriticalSection (&tok->cs);
217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246
#endif
    free(tok);
}

/**
 * xmlRMutexLock:
 * @tok:  the reentrant mutex
 *
 * xmlRMutexLock() is used to lock a libxml2 token_r.
 */
void
xmlRMutexLock(xmlRMutexPtr tok)
{
#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);
247 248 249
#elif defined HAVE_WIN32_THREADS
	EnterCriticalSection (&tok->cs);
	++tok->count;
250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270
#endif
}

/**
 * xmlRMutexUnlock:
 * @tok:  the reentrant mutex
 *
 * xmlRMutexUnlock() is used to unlock a libxml2 token_r.
 */
void
xmlRMutexUnlock(xmlRMutexPtr tok)
{
#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);
271 272
#elif defined HAVE_WIN32_THREADS
	if (!--tok->count) LeaveCriticalSection (&tok->cs);
273 274 275 276 277 278 279 280 281
#endif
}

/************************************************************************
 *									*
 *			Per thread global state handling		*
 *									*
 ************************************************************************/

282
#ifdef LIBXML_THREAD_ENABLED
283
#ifndef _MSC_VER
284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301
/**
 * 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
302
 * of libxml2 to pre-thread-safe behaviour.
303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318
 *
 * 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(gs));
    xmlInitializeGlobalState(gs);
    return (gs);
}
319
#endif /* _MSC_VER */
320
#endif /* LIBXML_THREAD_ENABLED */
321 322 323 324 325 326 327 328 329


/**
 * xmlGetGlobalState:
 *
 * xmlGetGlobalState() is called to retrieve the global state for a thread.
 *
 * Returns the thread global state or NULL in case of error
 */
330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350

#ifdef HAVE_WIN32_THREADS
#ifndef _MSC_VER
typedef struct _xmlGlobalStateCleanupHelperParams
{
	HANDLE thread;
	void *memory;
} xmlGlobalStateCleanupHelperParams;

void __cdecl xmlGlobalStateCleanupHelper (void *p)
{
	xmlGlobalStateCleanupHelperParams *params = (xmlGlobalStateCleanupHelperParams *) p;
	WaitForSingleObject (params->thread, INFINITE);
	CloseHandle (params->thread);
	xmlFreeGlobalState (params->memory);
	free (params);
	_endthread ();
}
#endif /* _MSC_VER */
#endif /* HAVE_WIN32_THREADS */

351 352 353 354 355 356
xmlGlobalStatePtr
xmlGetGlobalState(void)
{
#ifdef HAVE_PTHREAD_H
    xmlGlobalState *globalval;

357 358
    pthread_once(&once_control, xmlOnceInit);

359 360 361 362 363 364
    if ((globalval = (xmlGlobalState *)
         pthread_getspecific(globalkey)) == NULL) {
        xmlGlobalState *tsd = xmlNewGlobalState();

        pthread_setspecific(globalkey, tsd);
        return (tsd);
365 366
    }
    return (globalval);
367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395
#elif defined HAVE_WIN32_THREADS
#ifdef _MSC_VER
	if (!tlstate_inited)
	{
		tlstate_inited = 1;
		xmlInitializeGlobalState (&tlstate);
	}

	return &tlstate;
#else /* !_MSC_VER */
	xmlGlobalState *globalval;

	if (run_once_init) { run_once_init = 0; xmlOnceInit (); }

	if ((globalval = (xmlGlobalState *) TlsGetValue (globalkey)) == NULL)
	{
		xmlGlobalState *tsd = xmlNewGlobalState();
		xmlGlobalStateCleanupHelperParams *p = (xmlGlobalStateCleanupHelperParams *) malloc (sizeof (xmlGlobalStateCleanupHelperParams));

		p->memory = tsd;
		DuplicateHandle (GetCurrentProcess (), GetCurrentThread (), GetCurrentProcess (), &p->thread, 0, TRUE, DUPLICATE_SAME_ACCESS);

		TlsSetValue (globalkey, tsd);
		_beginthread (xmlGlobalStateCleanupHelper, 0, p);

		return (tsd);
	}
	return (globalval);
#endif /* _MSC_VER */
396 397
#else
    return(NULL);
398 399 400 401 402 403 404 405 406
#endif
}

/************************************************************************
 *									*
 *			Library wide thread interfaces			*
 *									*
 ************************************************************************/

407 408 409 410 411 412 413 414 415 416 417 418
/**
 * 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());
419 420
#elif defined HAVE_WIN32_THREADS
	return GetCurrentThreadId ();
421 422 423 424 425
#else
    return((int) 0);
#endif
}

426 427 428
/**
 * xmlIsMainThread:
 *
429
 * xmlIsMainThread() check whether the current thread is the main thread.
430 431 432 433 434 435
 *
 * Returns 1 if the current thread is the main thread, 0 otherwise
 */
int
xmlIsMainThread(void)
{
436 437
#ifdef HAVE_PTHREAD_H
    pthread_once(&once_control, xmlOnceInit);
438 439
#elif defined HAVE_WIN32_THREADS
	if (run_once_init) { run_once_init = 0; xmlOnceInit (); }
440
#endif
441 442 443 444 445 446
        
#ifdef DEBUG_THREADS
    xmlGenericError(xmlGenericErrorContext, "xmlIsMainThread()\n");
#endif
#ifdef HAVE_PTHREAD_H
    return(mainthread == pthread_self());
447 448
#elif defined HAVE_WIN32_THREADS
	return (mainthread == GetCurrentThreadId ());
449 450 451 452 453
#else
    return(1);
#endif
}

454 455 456 457 458 459 460 461 462
/**
 * xmlLockLibrary:
 *
 * xmlLockLibrary() is used to take out a re-entrant lock on the libxml2
 * library.
 */
void
xmlLockLibrary(void)
{
463 464 465
#ifdef DEBUG_THREADS
    xmlGenericError(xmlGenericErrorContext, "xmlLockLibrary()\n");
#endif
466 467 468 469 470 471 472 473 474 475 476 477
    xmlRMutexLock(xmlLibraryLock);
}

/**
 * xmlUnlockLibrary:
 *
 * xmlUnlockLibrary() is used to release a re-entrant lock on the libxml2
 * library.
 */
void
xmlUnlockLibrary(void)
{
478 479 480
#ifdef DEBUG_THREADS
    xmlGenericError(xmlGenericErrorContext, "xmlUnlockLibrary()\n");
#endif
481 482 483 484 485 486 487 488 489 490 491 492
    xmlRMutexUnlock(xmlLibraryLock);
}

/**
 * xmlInitThreads:
 *
 * xmlInitThreads() is used to to initialize all the thread related
 * data of the libxml2 library.
 */
void
xmlInitThreads(void)
{
493 494 495
#ifdef DEBUG_THREADS
    xmlGenericError(xmlGenericErrorContext, "xmlInitThreads()\n");
#endif
496 497 498 499 500 501 502 503 504 505 506
}

/**
 * xmlCleanupThreads:
 *
 * xmlCleanupThreads() is used to to cleanup all the thread related
 * data of the libxml2 library once processing has ended.
 */
void
xmlCleanupThreads(void)
{
507 508 509
#ifdef DEBUG_THREADS
    xmlGenericError(xmlGenericErrorContext, "xmlCleanupThreads()\n");
#endif
510
}
511

512 513 514 515 516 517 518 519 520 521 522 523 524 525
/**
 * 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();
526 527 528 529 530
#elif defined HAVE_WIN32_THREADS
#ifndef _MSC_VER
	globalkey = TlsAlloc ();
#endif /* _MSC_VER */
	mainthread = GetCurrentThreadId ();
531
#endif
532
}