Reactos
at master 681 lines 17 kB view raw
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#include <stdlib.h> 15 16#include <libxml/threads.h> 17#include <libxml/parser.h> 18#ifdef LIBXML_CATALOG_ENABLED 19#include <libxml/catalog.h> 20#endif 21#ifdef LIBXML_SCHEMAS_ENABLED 22#include <libxml/xmlschemastypes.h> 23#include <libxml/relaxng.h> 24#endif 25 26#if defined(SOLARIS) 27#include <note.h> 28#endif 29 30#include "private/dict.h" 31#include "private/enc.h" 32#include "private/globals.h" 33#include "private/memory.h" 34#include "private/threads.h" 35#include "private/xpath.h" 36 37#if defined(HAVE_POSIX_THREADS) && \ 38 defined(__GLIBC__) && \ 39 __GLIBC__ * 100 + __GLIBC_MINOR__ >= 234 40 41/* 42 * The modern way available since glibc 2.32. 43 * 44 * The check above is for glibc 2.34 which merged the pthread symbols into 45 * libc. Since we still allow linking without pthread symbols (see below), 46 * this only works if pthread symbols are guaranteed to be available. 47 */ 48 49#include <sys/single_threaded.h> 50 51#define XML_IS_THREADED() (!__libc_single_threaded) 52#define XML_IS_NEVER_THREADED() 0 53 54#elif defined(HAVE_POSIX_THREADS) && \ 55 defined(__GLIBC__) && \ 56 defined(__GNUC__) 57 58/* 59 * The traditional way to check for single-threaded applications with 60 * glibc was to check whether the separate libpthread library is 61 * linked in. This works by not linking libxml2 with libpthread (see 62 * BASE_THREAD_LIBS in configure.ac and Makefile.am) and declaring 63 * pthread functions as weak symbols. 64 * 65 * In glibc 2.34, the pthread symbols were moved from libpthread to libc, 66 * so this doesn't work anymore. 67 * 68 * At some point, this legacy code and the BASE_THREAD_LIBS hack in 69 * configure.ac can probably be removed. 70 */ 71 72#pragma weak pthread_mutex_init 73#pragma weak pthread_mutex_destroy 74#pragma weak pthread_mutex_lock 75#pragma weak pthread_mutex_unlock 76#pragma weak pthread_cond_init 77#pragma weak pthread_cond_destroy 78#pragma weak pthread_cond_wait 79#pragma weak pthread_equal 80#pragma weak pthread_self 81#pragma weak pthread_cond_signal 82 83#define XML_PTHREAD_WEAK 84#define XML_IS_THREADED() libxml_is_threaded 85#define XML_IS_NEVER_THREADED() (!libxml_is_threaded) 86 87static int libxml_is_threaded = -1; 88 89#else /* other POSIX platforms */ 90 91#define XML_IS_THREADED() 1 92#define XML_IS_NEVER_THREADED() 0 93 94#endif 95 96/* 97 * TODO: this module still uses malloc/free and not xmlMalloc/xmlFree 98 * to avoid some craziness since xmlMalloc/xmlFree may actually 99 * be hosted on allocated blocks needing them for the allocation ... 100 */ 101 102/* 103 * xmlRMutex are reentrant mutual exception locks 104 */ 105struct _xmlRMutex { 106#ifdef HAVE_POSIX_THREADS 107 pthread_mutex_t lock; 108 unsigned int held; 109 unsigned int waiters; 110 pthread_t tid; 111 pthread_cond_t cv; 112#elif defined HAVE_WIN32_THREADS 113 CRITICAL_SECTION cs; 114#else 115 int empty; 116#endif 117}; 118 119static xmlRMutexPtr xmlLibraryLock = NULL; 120 121/** 122 * xmlInitMutex: 123 * @mutex: the mutex 124 * 125 * Initialize a mutex. 126 */ 127void 128xmlInitMutex(xmlMutexPtr mutex) 129{ 130#ifdef HAVE_POSIX_THREADS 131 if (XML_IS_NEVER_THREADED() == 0) 132 pthread_mutex_init(&mutex->lock, NULL); 133#elif defined HAVE_WIN32_THREADS 134 InitializeCriticalSection(&mutex->cs); 135#else 136 (void) mutex; 137#endif 138} 139 140/** 141 * xmlNewMutex: 142 * 143 * xmlNewMutex() is used to allocate a libxml2 token struct for use in 144 * synchronizing access to data. 145 * 146 * Returns a new simple mutex pointer or NULL in case of error 147 */ 148xmlMutexPtr 149xmlNewMutex(void) 150{ 151 xmlMutexPtr tok; 152 153 if ((tok = malloc(sizeof(xmlMutex))) == NULL) 154 return (NULL); 155 xmlInitMutex(tok); 156 return (tok); 157} 158 159/** 160 * xmlCleanupMutex: 161 * @mutex: the simple mutex 162 * 163 * Reclaim resources associated with a mutex. 164 */ 165void 166xmlCleanupMutex(xmlMutexPtr mutex) 167{ 168#ifdef HAVE_POSIX_THREADS 169 if (XML_IS_NEVER_THREADED() == 0) 170 pthread_mutex_destroy(&mutex->lock); 171#elif defined HAVE_WIN32_THREADS 172 DeleteCriticalSection(&mutex->cs); 173#else 174 (void) mutex; 175#endif 176} 177 178/** 179 * xmlFreeMutex: 180 * @tok: the simple mutex 181 * 182 * Free a mutex. 183 */ 184void 185xmlFreeMutex(xmlMutexPtr tok) 186{ 187 if (tok == NULL) 188 return; 189 190 xmlCleanupMutex(tok); 191 free(tok); 192} 193 194/** 195 * xmlMutexLock: 196 * @tok: the simple mutex 197 * 198 * xmlMutexLock() is used to lock a libxml2 token. 199 */ 200void 201xmlMutexLock(xmlMutexPtr tok) 202{ 203 if (tok == NULL) 204 return; 205#ifdef HAVE_POSIX_THREADS 206 /* 207 * This assumes that __libc_single_threaded won't change while the 208 * lock is held. 209 */ 210 if (XML_IS_THREADED() != 0) 211 pthread_mutex_lock(&tok->lock); 212#elif defined HAVE_WIN32_THREADS 213 EnterCriticalSection(&tok->cs); 214#endif 215 216} 217 218/** 219 * xmlMutexUnlock: 220 * @tok: the simple mutex 221 * 222 * xmlMutexUnlock() is used to unlock a libxml2 token. 223 */ 224void 225xmlMutexUnlock(xmlMutexPtr tok) 226{ 227 if (tok == NULL) 228 return; 229#ifdef HAVE_POSIX_THREADS 230 if (XML_IS_THREADED() != 0) 231 pthread_mutex_unlock(&tok->lock); 232#elif defined HAVE_WIN32_THREADS 233 LeaveCriticalSection(&tok->cs); 234#endif 235} 236 237/** 238 * xmlNewRMutex: 239 * 240 * xmlRNewMutex() is used to allocate a reentrant mutex for use in 241 * synchronizing access to data. token_r is a re-entrant lock and thus useful 242 * for synchronizing access to data structures that may be manipulated in a 243 * recursive fashion. 244 * 245 * Returns the new reentrant mutex pointer or NULL in case of error 246 */ 247xmlRMutexPtr 248xmlNewRMutex(void) 249{ 250 xmlRMutexPtr tok; 251 252 if ((tok = malloc(sizeof(xmlRMutex))) == NULL) 253 return (NULL); 254#ifdef HAVE_POSIX_THREADS 255 if (XML_IS_NEVER_THREADED() == 0) { 256 pthread_mutex_init(&tok->lock, NULL); 257 tok->held = 0; 258 tok->waiters = 0; 259 pthread_cond_init(&tok->cv, NULL); 260 } 261#elif defined HAVE_WIN32_THREADS 262 InitializeCriticalSection(&tok->cs); 263#endif 264 return (tok); 265} 266 267/** 268 * xmlFreeRMutex: 269 * @tok: the reentrant mutex 270 * 271 * xmlRFreeMutex() is used to reclaim resources associated with a 272 * reentrant mutex. 273 */ 274void 275xmlFreeRMutex(xmlRMutexPtr tok ATTRIBUTE_UNUSED) 276{ 277 if (tok == NULL) 278 return; 279#ifdef HAVE_POSIX_THREADS 280 if (XML_IS_NEVER_THREADED() == 0) { 281 pthread_mutex_destroy(&tok->lock); 282 pthread_cond_destroy(&tok->cv); 283 } 284#elif defined HAVE_WIN32_THREADS 285 DeleteCriticalSection(&tok->cs); 286#endif 287 free(tok); 288} 289 290/** 291 * xmlRMutexLock: 292 * @tok: the reentrant mutex 293 * 294 * xmlRMutexLock() is used to lock a libxml2 token_r. 295 */ 296void 297xmlRMutexLock(xmlRMutexPtr tok) 298{ 299 if (tok == NULL) 300 return; 301#ifdef HAVE_POSIX_THREADS 302 if (XML_IS_THREADED() == 0) 303 return; 304 305 pthread_mutex_lock(&tok->lock); 306 if (tok->held) { 307 if (pthread_equal(tok->tid, pthread_self())) { 308 tok->held++; 309 pthread_mutex_unlock(&tok->lock); 310 return; 311 } else { 312 tok->waiters++; 313 while (tok->held) 314 pthread_cond_wait(&tok->cv, &tok->lock); 315 tok->waiters--; 316 } 317 } 318 tok->tid = pthread_self(); 319 tok->held = 1; 320 pthread_mutex_unlock(&tok->lock); 321#elif defined HAVE_WIN32_THREADS 322 EnterCriticalSection(&tok->cs); 323#endif 324} 325 326/** 327 * xmlRMutexUnlock: 328 * @tok: the reentrant mutex 329 * 330 * xmlRMutexUnlock() is used to unlock a libxml2 token_r. 331 */ 332void 333xmlRMutexUnlock(xmlRMutexPtr tok ATTRIBUTE_UNUSED) 334{ 335 if (tok == NULL) 336 return; 337#ifdef HAVE_POSIX_THREADS 338 if (XML_IS_THREADED() == 0) 339 return; 340 341 pthread_mutex_lock(&tok->lock); 342 tok->held--; 343 if (tok->held == 0) { 344 if (tok->waiters) 345 pthread_cond_signal(&tok->cv); 346 memset(&tok->tid, 0, sizeof(tok->tid)); 347 } 348 pthread_mutex_unlock(&tok->lock); 349#elif defined HAVE_WIN32_THREADS 350 LeaveCriticalSection(&tok->cs); 351#endif 352} 353 354/************************************************************************ 355 * * 356 * Library wide thread interfaces * 357 * * 358 ************************************************************************/ 359 360/** 361 * xmlGetThreadId: 362 * 363 * DEPRECATED: Internal function, do not use. 364 * 365 * xmlGetThreadId() find the current thread ID number 366 * Note that this is likely to be broken on some platforms using pthreads 367 * as the specification doesn't mandate pthread_t to be an integer type 368 * 369 * Returns the current thread ID number 370 */ 371int 372xmlGetThreadId(void) 373{ 374#ifdef HAVE_POSIX_THREADS 375 pthread_t id; 376 int ret; 377 378 if (XML_IS_THREADED() == 0) 379 return (0); 380 id = pthread_self(); 381 /* horrible but preserves compat, see warning above */ 382 memcpy(&ret, &id, sizeof(ret)); 383 return (ret); 384#elif defined HAVE_WIN32_THREADS 385 return GetCurrentThreadId(); 386#else 387 return ((int) 0); 388#endif 389} 390 391/** 392 * xmlLockLibrary: 393 * 394 * xmlLockLibrary() is used to take out a re-entrant lock on the libxml2 395 * library. 396 */ 397void 398xmlLockLibrary(void) 399{ 400 xmlRMutexLock(xmlLibraryLock); 401} 402 403/** 404 * xmlUnlockLibrary: 405 * 406 * xmlUnlockLibrary() is used to release a re-entrant lock on the libxml2 407 * library. 408 */ 409void 410xmlUnlockLibrary(void) 411{ 412 xmlRMutexUnlock(xmlLibraryLock); 413} 414 415/** 416 * xmlInitThreads: 417 * 418 * DEPRECATED: Alias for xmlInitParser. 419 */ 420void 421xmlInitThreads(void) 422{ 423 xmlInitParser(); 424} 425 426/** 427 * xmlCleanupThreads: 428 * 429 * DEPRECATED: This function is a no-op. Call xmlCleanupParser 430 * to free global state but see the warnings there. xmlCleanupParser 431 * should be only called once at program exit. In most cases, you don't 432 * have call cleanup functions at all. 433 */ 434void 435xmlCleanupThreads(void) 436{ 437} 438 439/************************************************************************ 440 * * 441 * Library wide initialization * 442 * * 443 ************************************************************************/ 444 445static int xmlParserInitialized = 0; 446static int xmlParserInnerInitialized = 0; 447 448 449#ifdef HAVE_POSIX_THREADS 450static pthread_mutex_t global_init_lock = PTHREAD_MUTEX_INITIALIZER; 451#elif defined HAVE_WIN32_THREADS 452static volatile LPCRITICAL_SECTION global_init_lock = NULL; 453#endif 454 455/** 456 * xmlGlobalInitMutexLock 457 * 458 * Makes sure that the global initialization mutex is initialized and 459 * locks it. 460 */ 461static void 462xmlGlobalInitMutexLock(void) { 463#ifdef HAVE_POSIX_THREADS 464 465#ifdef XML_PTHREAD_WEAK 466 /* 467 * This is somewhat unreliable since libpthread could be loaded 468 * later with dlopen() and threads could be created. But it's 469 * long-standing behavior and hard to work around. 470 */ 471 if (libxml_is_threaded == -1) 472 libxml_is_threaded = 473 (pthread_mutex_init != NULL) && 474 (pthread_mutex_destroy != NULL) && 475 (pthread_mutex_lock != NULL) && 476 (pthread_mutex_unlock != NULL) && 477 (pthread_cond_init != NULL) && 478 (pthread_cond_destroy != NULL) && 479 (pthread_cond_wait != NULL) && 480 /* 481 * pthread_equal can be inline, resuting in -Waddress warnings. 482 * Let's assume it's available if all the other functions are. 483 */ 484 /* (pthread_equal != NULL) && */ 485 (pthread_self != NULL) && 486 (pthread_cond_signal != NULL); 487#endif 488 489 /* The mutex is statically initialized, so we just lock it. */ 490 if (XML_IS_THREADED() != 0) 491 pthread_mutex_lock(&global_init_lock); 492 493#elif defined HAVE_WIN32_THREADS 494 495 LPCRITICAL_SECTION cs; 496 497 /* Create a new critical section */ 498 if (global_init_lock == NULL) { 499 cs = malloc(sizeof(CRITICAL_SECTION)); 500 if (cs == NULL) { 501 xmlGenericError(xmlGenericErrorContext, 502 "xmlGlobalInitMutexLock: out of memory\n"); 503 return; 504 } 505 InitializeCriticalSection(cs); 506 507 /* Swap it into the global_init_lock */ 508#ifdef InterlockedCompareExchangePointer 509 InterlockedCompareExchangePointer((void **) &global_init_lock, 510 cs, NULL); 511#else /* Use older void* version */ 512 InterlockedCompareExchange((void **) &global_init_lock, 513 (void *) cs, NULL); 514#endif /* InterlockedCompareExchangePointer */ 515 516 /* If another thread successfully recorded its critical 517 * section in the global_init_lock then discard the one 518 * allocated by this thread. */ 519 if (global_init_lock != cs) { 520 DeleteCriticalSection(cs); 521 free(cs); 522 } 523 } 524 525 /* Lock the chosen critical section */ 526 EnterCriticalSection(global_init_lock); 527 528#endif 529} 530 531static void 532xmlGlobalInitMutexUnlock(void) { 533#ifdef HAVE_POSIX_THREADS 534 if (XML_IS_THREADED() != 0) 535 pthread_mutex_unlock(&global_init_lock); 536#elif defined HAVE_WIN32_THREADS 537 if (global_init_lock != NULL) 538 LeaveCriticalSection(global_init_lock); 539#endif 540} 541 542/** 543 * xmlGlobalInitMutexDestroy 544 * 545 * Makes sure that the global initialization mutex is destroyed before 546 * application termination. 547 */ 548static void 549xmlGlobalInitMutexDestroy(void) { 550#ifdef HAVE_POSIX_THREADS 551#elif defined HAVE_WIN32_THREADS 552 if (global_init_lock != NULL) { 553 DeleteCriticalSection(global_init_lock); 554 free(global_init_lock); 555 global_init_lock = NULL; 556 } 557#endif 558} 559 560/** 561 * xmlInitParser: 562 * 563 * Initialization function for the XML parser. 564 * 565 * Call once from the main thread before using the library in 566 * multithreaded programs. 567 */ 568void 569xmlInitParser(void) { 570 /* 571 * Note that the initialization code must not make memory allocations. 572 */ 573 if (xmlParserInitialized != 0) 574 return; 575 576 xmlGlobalInitMutexLock(); 577 578 if (xmlParserInnerInitialized == 0) { 579#if defined(_WIN32) && \ 580 !defined(LIBXML_THREAD_ALLOC_ENABLED) && \ 581 (!defined(LIBXML_STATIC) || defined(LIBXML_STATIC_FOR_DLL)) 582 if (xmlFree == free) 583 atexit(xmlCleanupParser); 584#endif 585 586 xmlInitMemoryInternal(); /* Should come second */ 587 xmlInitGlobalsInternal(); 588 xmlInitRandom(); 589 xmlInitDictInternal(); 590 xmlInitEncodingInternal(); 591#if defined(LIBXML_XPATH_ENABLED) || defined(LIBXML_SCHEMAS_ENABLED) 592 xmlInitXPathInternal(); 593#endif 594 595 xmlRegisterDefaultInputCallbacks(); 596#ifdef LIBXML_OUTPUT_ENABLED 597 xmlRegisterDefaultOutputCallbacks(); 598#endif /* LIBXML_OUTPUT_ENABLED */ 599 600 xmlParserInnerInitialized = 1; 601 } 602 603 xmlGlobalInitMutexUnlock(); 604 605 xmlParserInitialized = 1; 606} 607 608/** 609 * xmlCleanupParser: 610 * 611 * This function name is somewhat misleading. It does not clean up 612 * parser state, it cleans up memory allocated by the library itself. 613 * It is a cleanup function for the XML library. It tries to reclaim all 614 * related global memory allocated for the library processing. 615 * It doesn't deallocate any document related memory. One should 616 * call xmlCleanupParser() only when the process has finished using 617 * the library and all XML/HTML documents built with it. 618 * See also xmlInitParser() which has the opposite function of preparing 619 * the library for operations. 620 * 621 * WARNING: if your application is multithreaded or has plugin support 622 * calling this may crash the application if another thread or 623 * a plugin is still using libxml2. It's sometimes very hard to 624 * guess if libxml2 is in use in the application, some libraries 625 * or plugins may use it without notice. In case of doubt abstain 626 * from calling this function or do it just before calling exit() 627 * to avoid leak reports from valgrind ! 628 */ 629void 630xmlCleanupParser(void) { 631 if (!xmlParserInitialized) 632 return; 633 634 /* These functions can call xmlFree. */ 635 636 xmlCleanupCharEncodingHandlers(); 637#ifdef LIBXML_CATALOG_ENABLED 638 xmlCatalogCleanup(); 639#endif 640#ifdef LIBXML_SCHEMAS_ENABLED 641 xmlSchemaCleanupTypes(); 642 xmlRelaxNGCleanupTypes(); 643#endif 644 645 /* These functions should never call xmlFree. */ 646 647 xmlCleanupInputCallbacks(); 648#ifdef LIBXML_OUTPUT_ENABLED 649 xmlCleanupOutputCallbacks(); 650#endif 651 652 xmlCleanupDictInternal(); 653 xmlCleanupRandom(); 654 xmlCleanupGlobalsInternal(); 655 /* 656 * Must come last. On Windows, xmlCleanupGlobalsInternal can call 657 * xmlFree which uses xmlMemMutex in debug mode. 658 */ 659 xmlCleanupMemoryInternal(); 660 661 xmlGlobalInitMutexDestroy(); 662 663 xmlParserInitialized = 0; 664 xmlParserInnerInitialized = 0; 665} 666 667#if defined(HAVE_ATTRIBUTE_DESTRUCTOR) && \ 668 !defined(LIBXML_THREAD_ALLOC_ENABLED) && \ 669 !defined(LIBXML_STATIC) && \ 670 !defined(_WIN32) 671static void 672ATTRIBUTE_DESTRUCTOR 673xmlDestructor(void) { 674 /* 675 * Calling custom deallocation functions in a destructor can cause 676 * problems, for example with Nokogiri. 677 */ 678 if (xmlFree == free) 679 xmlCleanupParser(); 680} 681#endif