threads.c revision c790bf4b36c3a7955351035f636ae93828bf141a
1/** 2 * threads.c: set of generic threading related routines 3 * 4 * See Copyright for the status of this software. 5 * 6 * Gary Pennington <Gary.Pennington@uk.sun.com> 7 * daniel@veillard.com 8 */ 9 10#define IN_LIBXML 11#include "libxml.h" 12 13#include <string.h> 14 15#include <libxml/threads.h> 16#include <libxml/globals.h> 17 18#ifdef HAVE_SYS_TYPES_H 19#include <sys/types.h> 20#endif 21#ifdef HAVE_UNISTD_H 22#include <unistd.h> 23#endif 24#ifdef HAVE_STDLIB_H 25#include <stdlib.h> 26#endif 27#ifdef HAVE_PTHREAD_H 28#include <pthread.h> 29#endif 30 31#ifdef HAVE_WIN32_THREADS 32#include <windows.h> 33#ifndef HAVE_COMPILER_TLS 34#include <process.h> 35#endif 36#endif 37 38#if defined(SOLARIS) 39#include <note.h> 40#endif 41 42/* #define DEBUG_THREADS */ 43 44/* 45 * TODO: this module still uses malloc/free and not xmlMalloc/xmlFree 46 * to avoid some crazyness since xmlMalloc/xmlFree may actually 47 * be hosted on allocated blocks needing them for the allocation ... 48 */ 49 50/* 51 * xmlMutex are a simple mutual exception locks 52 */ 53struct _xmlMutex { 54#ifdef HAVE_PTHREAD_H 55 pthread_mutex_t lock; 56#elif defined HAVE_WIN32_THREADS 57 HANDLE mutex; 58#else 59 int empty; 60#endif 61}; 62 63/* 64 * xmlRMutex are reentrant mutual exception locks 65 */ 66struct _xmlRMutex { 67#ifdef HAVE_PTHREAD_H 68 pthread_mutex_t lock; 69 unsigned int held; 70 unsigned int waiters; 71 pthread_t tid; 72 pthread_cond_t cv; 73#elif defined HAVE_WIN32_THREADS 74 CRITICAL_SECTION cs; 75 unsigned int count; 76#else 77 int empty; 78#endif 79}; 80/* 81 * This module still has some internal static data. 82 * - xmlLibraryLock a global lock 83 * - globalkey used for per-thread data 84 */ 85 86#ifdef HAVE_PTHREAD_H 87static pthread_key_t globalkey; 88static pthread_t mainthread; 89static pthread_once_t once_control = PTHREAD_ONCE_INIT; 90#elif defined HAVE_WIN32_THREADS 91#if defined(HAVE_COMPILER_TLS) 92static __declspec(thread) xmlGlobalState tlstate; 93static __declspec(thread) int tlstate_inited = 0; 94#else /* HAVE_COMPILER_TLS */ 95static DWORD globalkey = TLS_OUT_OF_INDEXES; 96#endif /* HAVE_COMPILER_TLS */ 97static DWORD mainthread; 98static int run_once_init = 1; 99#endif /* HAVE_WIN32_THREADS */ 100 101static xmlRMutexPtr xmlLibraryLock = NULL; 102#ifdef LIBXML_THREAD_ENABLED 103static void xmlOnceInit(void); 104#endif 105 106/** 107 * xmlNewMutex: 108 * 109 * xmlNewMutex() is used to allocate a libxml2 token struct for use in 110 * synchronizing access to data. 111 * 112 * Returns a new simple mutex pointer or NULL in case of error 113 */ 114xmlMutexPtr 115xmlNewMutex(void) 116{ 117 xmlMutexPtr tok; 118 119 if ((tok = malloc(sizeof(xmlMutex))) == NULL) 120 return (NULL); 121#ifdef HAVE_PTHREAD_H 122 pthread_mutex_init(&tok->lock, NULL); 123#elif defined HAVE_WIN32_THREADS 124 tok->mutex = CreateMutex(NULL, FALSE, NULL); 125#endif 126 return (tok); 127} 128 129/** 130 * xmlFreeMutex: 131 * @tok: the simple mutex 132 * 133 * xmlFreeMutex() is used to reclaim resources associated with a libxml2 token 134 * struct. 135 */ 136void 137xmlFreeMutex(xmlMutexPtr tok) 138{ 139 if (tok == NULL) return; 140 141#ifdef HAVE_PTHREAD_H 142 pthread_mutex_destroy(&tok->lock); 143#elif defined HAVE_WIN32_THREADS 144 CloseHandle(tok->mutex); 145#endif 146 free(tok); 147} 148 149/** 150 * xmlMutexLock: 151 * @tok: the simple mutex 152 * 153 * xmlMutexLock() is used to lock a libxml2 token. 154 */ 155void 156xmlMutexLock(xmlMutexPtr tok) 157{ 158 if (tok == NULL) 159 return; 160#ifdef HAVE_PTHREAD_H 161 pthread_mutex_lock(&tok->lock); 162#elif defined HAVE_WIN32_THREADS 163 WaitForSingleObject(tok->mutex, INFINITE); 164#endif 165 166} 167 168/** 169 * xmlMutexUnlock: 170 * @tok: the simple mutex 171 * 172 * xmlMutexUnlock() is used to unlock a libxml2 token. 173 */ 174void 175xmlMutexUnlock(xmlMutexPtr tok) 176{ 177 if (tok == NULL) 178 return; 179#ifdef HAVE_PTHREAD_H 180 pthread_mutex_unlock(&tok->lock); 181#elif defined HAVE_WIN32_THREADS 182 ReleaseMutex(tok->mutex); 183#endif 184} 185 186/** 187 * xmlNewRMutex: 188 * 189 * xmlRNewMutex() is used to allocate a reentrant mutex for use in 190 * synchronizing access to data. token_r is a re-entrant lock and thus useful 191 * for synchronizing access to data structures that may be manipulated in a 192 * recursive fashion. 193 * 194 * Returns the new reentrant mutex pointer or NULL in case of error 195 */ 196xmlRMutexPtr 197xmlNewRMutex(void) 198{ 199 xmlRMutexPtr tok; 200 201 if ((tok = malloc(sizeof(xmlRMutex))) == NULL) 202 return (NULL); 203#ifdef HAVE_PTHREAD_H 204 pthread_mutex_init(&tok->lock, NULL); 205 tok->held = 0; 206 tok->waiters = 0; 207 pthread_cond_init(&tok->cv, NULL); 208#elif defined HAVE_WIN32_THREADS 209 InitializeCriticalSection(&tok->cs); 210 tok->count = 0; 211#endif 212 return (tok); 213} 214 215/** 216 * xmlFreeRMutex: 217 * @tok: the reentrant mutex 218 * 219 * xmlRFreeMutex() is used to reclaim resources associated with a 220 * reentrant mutex. 221 */ 222void 223xmlFreeRMutex(xmlRMutexPtr tok ATTRIBUTE_UNUSED) 224{ 225#ifdef HAVE_PTHREAD_H 226 pthread_mutex_destroy(&tok->lock); 227#elif defined HAVE_WIN32_THREADS 228 DeleteCriticalSection(&tok->cs); 229#endif 230 free(tok); 231} 232 233/** 234 * xmlRMutexLock: 235 * @tok: the reentrant mutex 236 * 237 * xmlRMutexLock() is used to lock a libxml2 token_r. 238 */ 239void 240xmlRMutexLock(xmlRMutexPtr tok) 241{ 242 if (tok == NULL) 243 return; 244#ifdef HAVE_PTHREAD_H 245 pthread_mutex_lock(&tok->lock); 246 if (tok->held) { 247 if (pthread_equal(tok->tid, pthread_self())) { 248 tok->held++; 249 pthread_mutex_unlock(&tok->lock); 250 return; 251 } else { 252 tok->waiters++; 253 while (tok->held) 254 pthread_cond_wait(&tok->cv, &tok->lock); 255 tok->waiters--; 256 } 257 } 258 tok->tid = pthread_self(); 259 tok->held = 1; 260 pthread_mutex_unlock(&tok->lock); 261#elif defined HAVE_WIN32_THREADS 262 EnterCriticalSection(&tok->cs); 263 ++tok->count; 264#endif 265} 266 267/** 268 * xmlRMutexUnlock: 269 * @tok: the reentrant mutex 270 * 271 * xmlRMutexUnlock() is used to unlock a libxml2 token_r. 272 */ 273void 274xmlRMutexUnlock(xmlRMutexPtr tok ATTRIBUTE_UNUSED) 275{ 276 if (tok == NULL) 277 return; 278#ifdef HAVE_PTHREAD_H 279 pthread_mutex_lock(&tok->lock); 280 tok->held--; 281 if (tok->held == 0) { 282 if (tok->waiters) 283 pthread_cond_signal(&tok->cv); 284 tok->tid = 0; 285 } 286 pthread_mutex_unlock(&tok->lock); 287#elif defined HAVE_WIN32_THREADS 288 if (!--tok->count) 289 LeaveCriticalSection(&tok->cs); 290#endif 291} 292 293/************************************************************************ 294 * * 295 * Per thread global state handling * 296 * * 297 ************************************************************************/ 298 299#ifdef LIBXML_THREAD_ENABLED 300/** 301 * xmlFreeGlobalState: 302 * @state: a thread global state 303 * 304 * xmlFreeGlobalState() is called when a thread terminates with a non-NULL 305 * global state. It is is used here to reclaim memory resources. 306 */ 307static void 308xmlFreeGlobalState(void *state) 309{ 310 free(state); 311} 312 313/** 314 * xmlNewGlobalState: 315 * 316 * xmlNewGlobalState() allocates a global state. This structure is used to 317 * hold all data for use by a thread when supporting backwards compatibility 318 * of libxml2 to pre-thread-safe behaviour. 319 * 320 * Returns the newly allocated xmlGlobalStatePtr or NULL in case of error 321 */ 322static xmlGlobalStatePtr 323xmlNewGlobalState(void) 324{ 325 xmlGlobalState *gs; 326 327 gs = malloc(sizeof(xmlGlobalState)); 328 if (gs == NULL) 329 return(NULL); 330 331 memset(gs, 0, sizeof(xmlGlobalState)); 332 xmlInitializeGlobalState(gs); 333 return (gs); 334} 335#endif /* LIBXML_THREAD_ENABLED */ 336 337 338#ifdef HAVE_WIN32_THREADS 339#if !defined(HAVE_COMPILER_TLS) 340#if defined(LIBXML_STATIC) && !defined(LIBXML_STATIC_FOR_DLL) 341typedef struct _xmlGlobalStateCleanupHelperParams 342{ 343 HANDLE thread; 344 void *memory; 345} xmlGlobalStateCleanupHelperParams; 346 347static void xmlGlobalStateCleanupHelper (void *p) 348{ 349 xmlGlobalStateCleanupHelperParams *params = (xmlGlobalStateCleanupHelperParams *) p; 350 WaitForSingleObject(params->thread, INFINITE); 351 CloseHandle(params->thread); 352 xmlFreeGlobalState(params->memory); 353 free(params); 354 _endthread(); 355} 356#else /* LIBXML_STATIC && !LIBXML_STATIC_FOR_DLL */ 357 358typedef struct _xmlGlobalStateCleanupHelperParams 359{ 360 void *memory; 361 struct _xmlGlobalStateCleanupHelperParams * prev; 362 struct _xmlGlobalStateCleanupHelperParams * next; 363} xmlGlobalStateCleanupHelperParams; 364 365static xmlGlobalStateCleanupHelperParams * cleanup_helpers_head = NULL; 366static CRITICAL_SECTION cleanup_helpers_cs; 367 368#endif /* LIBXMLSTATIC && !LIBXML_STATIC_FOR_DLL */ 369#endif /* HAVE_COMPILER_TLS */ 370#endif /* HAVE_WIN32_THREADS */ 371 372/** 373 * xmlGetGlobalState: 374 * 375 * xmlGetGlobalState() is called to retrieve the global state for a thread. 376 * 377 * Returns the thread global state or NULL in case of error 378 */ 379xmlGlobalStatePtr 380xmlGetGlobalState(void) 381{ 382#ifdef HAVE_PTHREAD_H 383 xmlGlobalState *globalval; 384 385 pthread_once(&once_control, xmlOnceInit); 386 387 if ((globalval = (xmlGlobalState *) 388 pthread_getspecific(globalkey)) == NULL) { 389 xmlGlobalState *tsd = xmlNewGlobalState(); 390 391 pthread_setspecific(globalkey, tsd); 392 return (tsd); 393 } 394 return (globalval); 395#elif defined HAVE_WIN32_THREADS 396#if defined(HAVE_COMPILER_TLS) 397 if (!tlstate_inited) { 398 tlstate_inited = 1; 399 xmlInitializeGlobalState(&tlstate); 400 } 401 return &tlstate; 402#else /* HAVE_COMPILER_TLS */ 403 xmlGlobalState *globalval; 404 xmlGlobalStateCleanupHelperParams * p; 405 406 if (run_once_init) { 407 run_once_init = 0; 408 xmlOnceInit(); 409 } 410#if defined(LIBXML_STATIC) && !defined(LIBXML_STATIC_FOR_DLL) 411 globalval = (xmlGlobalState *)TlsGetValue(globalkey); 412#else 413 p = (xmlGlobalStateCleanupHelperParams*)TlsGetValue(globalkey); 414 globalval = (xmlGlobalState *)(p ? p->memory : NULL); 415#endif 416 if (globalval == NULL) { 417 xmlGlobalState *tsd = xmlNewGlobalState(); 418 p = (xmlGlobalStateCleanupHelperParams *) malloc(sizeof(xmlGlobalStateCleanupHelperParams)); 419 p->memory = tsd; 420#if defined(LIBXML_STATIC) && !defined(LIBXML_STATIC_FOR_DLL) 421 DuplicateHandle(GetCurrentProcess(), GetCurrentThread(), 422 GetCurrentProcess(), &p->thread, 0, TRUE, DUPLICATE_SAME_ACCESS); 423 TlsSetValue(globalkey, tsd); 424 _beginthread(xmlGlobalStateCleanupHelper, 0, p); 425#else 426 EnterCriticalSection(&cleanup_helpers_cs); 427 if (cleanup_helpers_head != NULL) { 428 cleanup_helpers_head->prev = p; 429 } 430 p->next = cleanup_helpers_head; 431 p->prev = NULL; 432 cleanup_helpers_head = p; 433 TlsSetValue(globalkey, p); 434 LeaveCriticalSection(&cleanup_helpers_cs); 435#endif 436 437 return (tsd); 438 } 439 return (globalval); 440#endif /* HAVE_COMPILER_TLS */ 441#else 442 return(NULL); 443#endif 444} 445 446/************************************************************************ 447 * * 448 * Library wide thread interfaces * 449 * * 450 ************************************************************************/ 451 452/** 453 * xmlGetThreadId: 454 * 455 * xmlGetThreadId() find the current thread ID number 456 * 457 * Returns the current thread ID number 458 */ 459int 460xmlGetThreadId(void) 461{ 462#ifdef HAVE_PTHREAD_H 463 return((int) pthread_self()); 464#elif defined HAVE_WIN32_THREADS 465 return GetCurrentThreadId(); 466#else 467 return((int) 0); 468#endif 469} 470 471/** 472 * xmlIsMainThread: 473 * 474 * xmlIsMainThread() check whether the current thread is the main thread. 475 * 476 * Returns 1 if the current thread is the main thread, 0 otherwise 477 */ 478int 479xmlIsMainThread(void) 480{ 481#ifdef HAVE_PTHREAD_H 482 pthread_once(&once_control, xmlOnceInit); 483#elif defined HAVE_WIN32_THREADS 484 if (run_once_init) { 485 run_once_init = 0; 486 xmlOnceInit (); 487 } 488#endif 489 490#ifdef DEBUG_THREADS 491 xmlGenericError(xmlGenericErrorContext, "xmlIsMainThread()\n"); 492#endif 493#ifdef HAVE_PTHREAD_H 494 return(mainthread == pthread_self()); 495#elif defined HAVE_WIN32_THREADS 496 return(mainthread == GetCurrentThreadId ()); 497#else 498 return(1); 499#endif 500} 501 502/** 503 * xmlLockLibrary: 504 * 505 * xmlLockLibrary() is used to take out a re-entrant lock on the libxml2 506 * library. 507 */ 508void 509xmlLockLibrary(void) 510{ 511#ifdef DEBUG_THREADS 512 xmlGenericError(xmlGenericErrorContext, "xmlLockLibrary()\n"); 513#endif 514 xmlRMutexLock(xmlLibraryLock); 515} 516 517/** 518 * xmlUnlockLibrary: 519 * 520 * xmlUnlockLibrary() is used to release a re-entrant lock on the libxml2 521 * library. 522 */ 523void 524xmlUnlockLibrary(void) 525{ 526#ifdef DEBUG_THREADS 527 xmlGenericError(xmlGenericErrorContext, "xmlUnlockLibrary()\n"); 528#endif 529 xmlRMutexUnlock(xmlLibraryLock); 530} 531 532/** 533 * xmlInitThreads: 534 * 535 * xmlInitThreads() is used to to initialize all the thread related 536 * data of the libxml2 library. 537 */ 538void 539xmlInitThreads(void) 540{ 541#ifdef DEBUG_THREADS 542 xmlGenericError(xmlGenericErrorContext, "xmlInitThreads()\n"); 543#endif 544#if defined(HAVE_WIN32_THREADS) && !defined(HAVE_COMPILER_TLS) && (!defined(LIBXML_STATIC) || defined(LIBXML_STATIC_FOR_DLL)) 545 InitializeCriticalSection(&cleanup_helpers_cs); 546#endif 547} 548 549/** 550 * xmlCleanupThreads: 551 * 552 * xmlCleanupThreads() is used to to cleanup all the thread related 553 * data of the libxml2 library once processing has ended. 554 */ 555void 556xmlCleanupThreads(void) 557{ 558#ifdef DEBUG_THREADS 559 xmlGenericError(xmlGenericErrorContext, "xmlCleanupThreads()\n"); 560#endif 561#if defined(HAVE_WIN32_THREADS) && !defined(HAVE_COMPILER_TLS) && (!defined(LIBXML_STATIC) || defined(LIBXML_STATIC_FOR_DLL)) 562 if (globalkey != TLS_OUT_OF_INDEXES) { 563 xmlGlobalStateCleanupHelperParams * p; 564 EnterCriticalSection(&cleanup_helpers_cs); 565 p = cleanup_helpers_head; 566 while (p != NULL) { 567 xmlGlobalStateCleanupHelperParams * temp = p; 568 p = p->next; 569 xmlFreeGlobalState(temp->memory); 570 free(temp); 571 } 572 cleanup_helpers_head = 0; 573 LeaveCriticalSection(&cleanup_helpers_cs); 574 TlsFree(globalkey); 575 globalkey = TLS_OUT_OF_INDEXES; 576 } 577 DeleteCriticalSection(&cleanup_helpers_cs); 578#endif 579} 580 581#ifdef LIBXML_THREAD_ENABLED 582/** 583 * xmlOnceInit 584 * 585 * xmlOnceInit() is used to initialize the value of mainthread for use 586 * in other routines. This function should only be called using 587 * pthread_once() in association with the once_control variable to ensure 588 * that the function is only called once. See man pthread_once for more 589 * details. 590 */ 591static void 592xmlOnceInit(void) { 593#ifdef HAVE_PTHREAD_H 594 (void) pthread_key_create(&globalkey, xmlFreeGlobalState); 595 mainthread = pthread_self(); 596#endif 597 598#if defined(HAVE_WIN32_THREADS) 599#if !defined(HAVE_COMPILER_TLS) 600 globalkey = TlsAlloc(); 601#endif 602 mainthread = GetCurrentThreadId(); 603#endif 604} 605#endif 606 607/** 608 * DllMain: 609 * @hinstDLL: handle to DLL instance 610 * @fdwReason: Reason code for entry 611 * @lpvReserved: generic pointer (depends upon reason code) 612 * 613 * Entry point for Windows library. It is being used to free thread-specific 614 * storage. 615 * 616 * Returns TRUE always 617 */ 618#if defined(HAVE_WIN32_THREADS) && !defined(HAVE_COMPILER_TLS) && (!defined(LIBXML_STATIC) || defined(LIBXML_STATIC_FOR_DLL)) 619#if defined(LIBXML_STATIC_FOR_DLL) 620BOOL WINAPI xmlDllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved) 621#else 622BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved) 623#endif 624{ 625 switch(fdwReason) { 626 case DLL_THREAD_DETACH: 627 if (globalkey != TLS_OUT_OF_INDEXES) { 628 xmlGlobalState *globalval = NULL; 629 xmlGlobalStateCleanupHelperParams * p = 630 (xmlGlobalStateCleanupHelperParams*)TlsGetValue(globalkey); 631 globalval = (xmlGlobalState *)(p ? p->memory : NULL); 632 if (globalval) { 633 xmlFreeGlobalState(globalval); 634 TlsSetValue(globalkey,NULL); 635 } 636 if (p) 637 { 638 EnterCriticalSection(&cleanup_helpers_cs); 639 if (p == cleanup_helpers_head) 640 cleanup_helpers_head = p->next; 641 else 642 p->prev->next = p->next; 643 if (p->next != NULL) 644 p->next->prev = p->prev; 645 LeaveCriticalSection(&cleanup_helpers_cs); 646 free(p); 647 } 648 } 649 break; 650 } 651 return TRUE; 652} 653#endif 654