threads.c revision 5d4644ef6e38479a648615eca758c5e962a141d5
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#ifdef HAVE_BEOS_THREADS 39#include <OS.h> 40#include <TLS.h> 41#endif 42 43#if defined(SOLARIS) 44#include <note.h> 45#endif 46 47/* #define DEBUG_THREADS */ 48 49/* 50 * TODO: this module still uses malloc/free and not xmlMalloc/xmlFree 51 * to avoid some crazyness since xmlMalloc/xmlFree may actually 52 * be hosted on allocated blocks needing them for the allocation ... 53 */ 54 55/* 56 * xmlMutex are a simple mutual exception locks 57 */ 58struct _xmlMutex { 59#ifdef HAVE_PTHREAD_H 60 pthread_mutex_t lock; 61#elif defined HAVE_WIN32_THREADS 62 HANDLE mutex; 63#elif defined HAVE_BEOS_THREADS 64 sem_id sem; 65 thread_id tid; 66#else 67 int empty; 68#endif 69}; 70 71/* 72 * xmlRMutex are reentrant mutual exception locks 73 */ 74struct _xmlRMutex { 75#ifdef HAVE_PTHREAD_H 76 pthread_mutex_t lock; 77 unsigned int held; 78 unsigned int waiters; 79 pthread_t tid; 80 pthread_cond_t cv; 81#elif defined HAVE_WIN32_THREADS 82 CRITICAL_SECTION cs; 83 unsigned int count; 84#elif defined HAVE_BEOS_THREADS 85 xmlMutexPtr lock; 86 thread_id tid; 87 int32 count; 88#else 89 int empty; 90#endif 91}; 92/* 93 * This module still has some internal static data. 94 * - xmlLibraryLock a global lock 95 * - globalkey used for per-thread data 96 */ 97 98#ifdef HAVE_PTHREAD_H 99static pthread_key_t globalkey; 100static pthread_t mainthread; 101static pthread_once_t once_control = PTHREAD_ONCE_INIT; 102#elif defined HAVE_WIN32_THREADS 103#if defined(HAVE_COMPILER_TLS) 104static __declspec(thread) xmlGlobalState tlstate; 105static __declspec(thread) int tlstate_inited = 0; 106#else /* HAVE_COMPILER_TLS */ 107static DWORD globalkey = TLS_OUT_OF_INDEXES; 108#endif /* HAVE_COMPILER_TLS */ 109static DWORD mainthread; 110static struct 111{ 112 DWORD done; 113 DWORD control; 114} run_once = { 0, 0 }; 115/* endif HAVE_WIN32_THREADS */ 116#elif defined HAVE_BEOS_THREADS 117int32 globalkey = 0; 118thread_id mainthread = 0; 119int32 run_once_init = 0; 120#endif 121 122static xmlRMutexPtr xmlLibraryLock = NULL; 123#ifdef LIBXML_THREAD_ENABLED 124static void xmlOnceInit(void); 125#endif 126 127/** 128 * xmlNewMutex: 129 * 130 * xmlNewMutex() is used to allocate a libxml2 token struct for use in 131 * synchronizing access to data. 132 * 133 * Returns a new simple mutex pointer or NULL in case of error 134 */ 135xmlMutexPtr 136xmlNewMutex(void) 137{ 138 xmlMutexPtr tok; 139 140 if ((tok = malloc(sizeof(xmlMutex))) == NULL) 141 return (NULL); 142#ifdef HAVE_PTHREAD_H 143 pthread_mutex_init(&tok->lock, NULL); 144#elif defined HAVE_WIN32_THREADS 145 tok->mutex = CreateMutex(NULL, FALSE, NULL); 146#elif defined HAVE_BEOS_THREADS 147 if ((tok->sem = create_sem(1, "xmlMutex")) < B_OK) { 148 free(tok); 149 return NULL; 150 } 151 tok->tid = -1; 152#endif 153 return (tok); 154} 155 156/** 157 * xmlFreeMutex: 158 * @tok: the simple mutex 159 * 160 * xmlFreeMutex() is used to reclaim resources associated with a libxml2 token 161 * struct. 162 */ 163void 164xmlFreeMutex(xmlMutexPtr tok) 165{ 166 if (tok == NULL) return; 167 168#ifdef HAVE_PTHREAD_H 169 pthread_mutex_destroy(&tok->lock); 170#elif defined HAVE_WIN32_THREADS 171 CloseHandle(tok->mutex); 172#elif defined HAVE_BEOS_THREADS 173 delete_sem(tok->sem); 174#endif 175 free(tok); 176} 177 178/** 179 * xmlMutexLock: 180 * @tok: the simple mutex 181 * 182 * xmlMutexLock() is used to lock a libxml2 token. 183 */ 184void 185xmlMutexLock(xmlMutexPtr tok) 186{ 187 if (tok == NULL) 188 return; 189#ifdef HAVE_PTHREAD_H 190 pthread_mutex_lock(&tok->lock); 191#elif defined HAVE_WIN32_THREADS 192 WaitForSingleObject(tok->mutex, INFINITE); 193#elif defined HAVE_BEOS_THREADS 194 if (acquire_sem(tok->sem) != B_NO_ERROR) { 195#ifdef DEBUG_THREADS 196 xmlGenericError(xmlGenericErrorContext, "xmlMutexLock():BeOS:Couldn't aquire semaphore\n"); 197 exit(); 198#endif 199 } 200 tok->tid = find_thread(NULL); 201#endif 202 203} 204 205/** 206 * xmlMutexUnlock: 207 * @tok: the simple mutex 208 * 209 * xmlMutexUnlock() is used to unlock a libxml2 token. 210 */ 211void 212xmlMutexUnlock(xmlMutexPtr tok) 213{ 214 if (tok == NULL) 215 return; 216#ifdef HAVE_PTHREAD_H 217 pthread_mutex_unlock(&tok->lock); 218#elif defined HAVE_WIN32_THREADS 219 ReleaseMutex(tok->mutex); 220#elif defined HAVE_BEOS_THREADS 221 if (tok->tid == find_thread(NULL)) { 222 tok->tid = -1; 223 release_sem(tok->sem); 224 } 225#endif 226} 227 228/** 229 * xmlNewRMutex: 230 * 231 * xmlRNewMutex() is used to allocate a reentrant mutex for use in 232 * synchronizing access to data. token_r is a re-entrant lock and thus useful 233 * for synchronizing access to data structures that may be manipulated in a 234 * recursive fashion. 235 * 236 * Returns the new reentrant mutex pointer or NULL in case of error 237 */ 238xmlRMutexPtr 239xmlNewRMutex(void) 240{ 241 xmlRMutexPtr tok; 242 243 if ((tok = malloc(sizeof(xmlRMutex))) == NULL) 244 return (NULL); 245#ifdef HAVE_PTHREAD_H 246 pthread_mutex_init(&tok->lock, NULL); 247 tok->held = 0; 248 tok->waiters = 0; 249 pthread_cond_init(&tok->cv, NULL); 250#elif defined HAVE_WIN32_THREADS 251 InitializeCriticalSection(&tok->cs); 252 tok->count = 0; 253#elif defined HAVE_BEOS_THREADS 254 if ((tok->lock = xmlNewMutex()) == NULL) { 255 free(tok); 256 return NULL; 257 } 258 tok->count = 0; 259#endif 260 return (tok); 261} 262 263/** 264 * xmlFreeRMutex: 265 * @tok: the reentrant mutex 266 * 267 * xmlRFreeMutex() is used to reclaim resources associated with a 268 * reentrant mutex. 269 */ 270void 271xmlFreeRMutex(xmlRMutexPtr tok ATTRIBUTE_UNUSED) 272{ 273#ifdef HAVE_PTHREAD_H 274 pthread_mutex_destroy(&tok->lock); 275#elif defined HAVE_WIN32_THREADS 276 DeleteCriticalSection(&tok->cs); 277#elif defined HAVE_BEOS_THREADS 278 xmlFreeMutex(tok->lock); 279#endif 280 free(tok); 281} 282 283/** 284 * xmlRMutexLock: 285 * @tok: the reentrant mutex 286 * 287 * xmlRMutexLock() is used to lock a libxml2 token_r. 288 */ 289void 290xmlRMutexLock(xmlRMutexPtr tok) 291{ 292 if (tok == NULL) 293 return; 294#ifdef HAVE_PTHREAD_H 295 pthread_mutex_lock(&tok->lock); 296 if (tok->held) { 297 if (pthread_equal(tok->tid, pthread_self())) { 298 tok->held++; 299 pthread_mutex_unlock(&tok->lock); 300 return; 301 } else { 302 tok->waiters++; 303 while (tok->held) 304 pthread_cond_wait(&tok->cv, &tok->lock); 305 tok->waiters--; 306 } 307 } 308 tok->tid = pthread_self(); 309 tok->held = 1; 310 pthread_mutex_unlock(&tok->lock); 311#elif defined HAVE_WIN32_THREADS 312 EnterCriticalSection(&tok->cs); 313 ++tok->count; 314#elif defined HAVE_BEOS_THREADS 315 if (tok->lock->tid == find_thread(NULL)) { 316 tok->count++; 317 return; 318 } else { 319 xmlMutexLock(tok->lock); 320 tok->count = 1; 321 } 322#endif 323} 324 325/** 326 * xmlRMutexUnlock: 327 * @tok: the reentrant mutex 328 * 329 * xmlRMutexUnlock() is used to unlock a libxml2 token_r. 330 */ 331void 332xmlRMutexUnlock(xmlRMutexPtr tok ATTRIBUTE_UNUSED) 333{ 334 if (tok == NULL) 335 return; 336#ifdef HAVE_PTHREAD_H 337 pthread_mutex_lock(&tok->lock); 338 tok->held--; 339 if (tok->held == 0) { 340 if (tok->waiters) 341 pthread_cond_signal(&tok->cv); 342 tok->tid = 0; 343 } 344 pthread_mutex_unlock(&tok->lock); 345#elif defined HAVE_WIN32_THREADS 346 if (!--tok->count) 347 LeaveCriticalSection(&tok->cs); 348#elif defined HAVE_BEOS_THREADS 349 if (tok->lock->tid == find_thread(NULL)) { 350 tok->count--; 351 if (tok->count == 0) { 352 xmlMutexUnlock(tok->lock); 353 } 354 return; 355 } 356#endif 357} 358 359/************************************************************************ 360 * * 361 * Per thread global state handling * 362 * * 363 ************************************************************************/ 364 365#ifdef LIBXML_THREAD_ENABLED 366#ifdef xmlLastError 367#undef xmlLastError 368#endif 369/** 370 * xmlFreeGlobalState: 371 * @state: a thread global state 372 * 373 * xmlFreeGlobalState() is called when a thread terminates with a non-NULL 374 * global state. It is is used here to reclaim memory resources. 375 */ 376static void 377xmlFreeGlobalState(void *state) 378{ 379 xmlGlobalState *gs = (xmlGlobalState *) state; 380 381 /* free any memory allocated in the thread's xmlLastError */ 382 xmlResetError(&(gs->xmlLastError)); 383 free(state); 384} 385 386/** 387 * xmlNewGlobalState: 388 * 389 * xmlNewGlobalState() allocates a global state. This structure is used to 390 * hold all data for use by a thread when supporting backwards compatibility 391 * of libxml2 to pre-thread-safe behaviour. 392 * 393 * Returns the newly allocated xmlGlobalStatePtr or NULL in case of error 394 */ 395static xmlGlobalStatePtr 396xmlNewGlobalState(void) 397{ 398 xmlGlobalState *gs; 399 400 gs = malloc(sizeof(xmlGlobalState)); 401 if (gs == NULL) 402 return(NULL); 403 404 memset(gs, 0, sizeof(xmlGlobalState)); 405 xmlInitializeGlobalState(gs); 406 return (gs); 407} 408#endif /* LIBXML_THREAD_ENABLED */ 409 410 411#ifdef HAVE_WIN32_THREADS 412#if !defined(HAVE_COMPILER_TLS) 413#if defined(LIBXML_STATIC) && !defined(LIBXML_STATIC_FOR_DLL) 414typedef struct _xmlGlobalStateCleanupHelperParams 415{ 416 HANDLE thread; 417 void *memory; 418} xmlGlobalStateCleanupHelperParams; 419 420static void xmlGlobalStateCleanupHelper (void *p) 421{ 422 xmlGlobalStateCleanupHelperParams *params = (xmlGlobalStateCleanupHelperParams *) p; 423 WaitForSingleObject(params->thread, INFINITE); 424 CloseHandle(params->thread); 425 xmlFreeGlobalState(params->memory); 426 free(params); 427 _endthread(); 428} 429#else /* LIBXML_STATIC && !LIBXML_STATIC_FOR_DLL */ 430 431typedef struct _xmlGlobalStateCleanupHelperParams 432{ 433 void *memory; 434 struct _xmlGlobalStateCleanupHelperParams * prev; 435 struct _xmlGlobalStateCleanupHelperParams * next; 436} xmlGlobalStateCleanupHelperParams; 437 438static xmlGlobalStateCleanupHelperParams * cleanup_helpers_head = NULL; 439static CRITICAL_SECTION cleanup_helpers_cs; 440 441#endif /* LIBXMLSTATIC && !LIBXML_STATIC_FOR_DLL */ 442#endif /* HAVE_COMPILER_TLS */ 443#endif /* HAVE_WIN32_THREADS */ 444 445#if defined HAVE_BEOS_THREADS 446/** 447 * xmlGlobalStateCleanup: 448 * @data: unused parameter 449 * 450 * Used for Beos only 451 */ 452void xmlGlobalStateCleanup(void *data) 453{ 454 void *globalval = tls_get(globalkey); 455 if (globalval != NULL) 456 xmlFreeGlobalState(globalval); 457} 458#endif 459 460/** 461 * xmlGetGlobalState: 462 * 463 * xmlGetGlobalState() is called to retrieve the global state for a thread. 464 * 465 * Returns the thread global state or NULL in case of error 466 */ 467xmlGlobalStatePtr 468xmlGetGlobalState(void) 469{ 470#ifdef HAVE_PTHREAD_H 471 xmlGlobalState *globalval; 472 473 pthread_once(&once_control, xmlOnceInit); 474 475 if ((globalval = (xmlGlobalState *) 476 pthread_getspecific(globalkey)) == NULL) { 477 xmlGlobalState *tsd = xmlNewGlobalState(); 478 479 pthread_setspecific(globalkey, tsd); 480 return (tsd); 481 } 482 return (globalval); 483#elif defined HAVE_WIN32_THREADS 484#if defined(HAVE_COMPILER_TLS) 485 if (!tlstate_inited) { 486 tlstate_inited = 1; 487 xmlInitializeGlobalState(&tlstate); 488 } 489 return &tlstate; 490#else /* HAVE_COMPILER_TLS */ 491 xmlGlobalState *globalval; 492 xmlGlobalStateCleanupHelperParams * p; 493 494 xmlOnceInit(); 495#if defined(LIBXML_STATIC) && !defined(LIBXML_STATIC_FOR_DLL) 496 globalval = (xmlGlobalState *)TlsGetValue(globalkey); 497#else 498 p = (xmlGlobalStateCleanupHelperParams*)TlsGetValue(globalkey); 499 globalval = (xmlGlobalState *)(p ? p->memory : NULL); 500#endif 501 if (globalval == NULL) { 502 xmlGlobalState *tsd = xmlNewGlobalState(); 503 p = (xmlGlobalStateCleanupHelperParams *) malloc(sizeof(xmlGlobalStateCleanupHelperParams)); 504 p->memory = tsd; 505#if defined(LIBXML_STATIC) && !defined(LIBXML_STATIC_FOR_DLL) 506 DuplicateHandle(GetCurrentProcess(), GetCurrentThread(), 507 GetCurrentProcess(), &p->thread, 0, TRUE, DUPLICATE_SAME_ACCESS); 508 TlsSetValue(globalkey, tsd); 509 _beginthread(xmlGlobalStateCleanupHelper, 0, p); 510#else 511 EnterCriticalSection(&cleanup_helpers_cs); 512 if (cleanup_helpers_head != NULL) { 513 cleanup_helpers_head->prev = p; 514 } 515 p->next = cleanup_helpers_head; 516 p->prev = NULL; 517 cleanup_helpers_head = p; 518 TlsSetValue(globalkey, p); 519 LeaveCriticalSection(&cleanup_helpers_cs); 520#endif 521 522 return (tsd); 523 } 524 return (globalval); 525#endif /* HAVE_COMPILER_TLS */ 526#elif defined HAVE_BEOS_THREADS 527 xmlGlobalState *globalval; 528 529 xmlOnceInit(); 530 531 if ((globalval = (xmlGlobalState *) 532 tls_get(globalkey)) == NULL) { 533 xmlGlobalState *tsd = xmlNewGlobalState(); 534 535 tls_set(globalkey, tsd); 536 on_exit_thread(xmlGlobalStateCleanup, NULL); 537 return (tsd); 538 } 539 return (globalval); 540#else 541 return(NULL); 542#endif 543} 544 545/************************************************************************ 546 * * 547 * Library wide thread interfaces * 548 * * 549 ************************************************************************/ 550 551/** 552 * xmlGetThreadId: 553 * 554 * xmlGetThreadId() find the current thread ID number 555 * 556 * Returns the current thread ID number 557 */ 558int 559xmlGetThreadId(void) 560{ 561#ifdef HAVE_PTHREAD_H 562 return((int) pthread_self()); 563#elif defined HAVE_WIN32_THREADS 564 return GetCurrentThreadId(); 565#elif defined HAVE_BEOS_THREADS 566 return find_thread(NULL); 567#else 568 return((int) 0); 569#endif 570} 571 572/** 573 * xmlIsMainThread: 574 * 575 * xmlIsMainThread() check whether the current thread is the main thread. 576 * 577 * Returns 1 if the current thread is the main thread, 0 otherwise 578 */ 579int 580xmlIsMainThread(void) 581{ 582#ifdef HAVE_PTHREAD_H 583 pthread_once(&once_control, xmlOnceInit); 584#elif defined HAVE_WIN32_THREADS 585 xmlOnceInit (); 586#elif defined HAVE_BEOS_THREADS 587 xmlOnceInit(); 588#endif 589 590#ifdef DEBUG_THREADS 591 xmlGenericError(xmlGenericErrorContext, "xmlIsMainThread()\n"); 592#endif 593#ifdef HAVE_PTHREAD_H 594 return(mainthread == pthread_self()); 595#elif defined HAVE_WIN32_THREADS 596 return(mainthread == GetCurrentThreadId ()); 597#elif defined HAVE_BEOS_THREADS 598 return(mainthread == find_thread(NULL)); 599#else 600 return(1); 601#endif 602} 603 604/** 605 * xmlLockLibrary: 606 * 607 * xmlLockLibrary() is used to take out a re-entrant lock on the libxml2 608 * library. 609 */ 610void 611xmlLockLibrary(void) 612{ 613#ifdef DEBUG_THREADS 614 xmlGenericError(xmlGenericErrorContext, "xmlLockLibrary()\n"); 615#endif 616 xmlRMutexLock(xmlLibraryLock); 617} 618 619/** 620 * xmlUnlockLibrary: 621 * 622 * xmlUnlockLibrary() is used to release a re-entrant lock on the libxml2 623 * library. 624 */ 625void 626xmlUnlockLibrary(void) 627{ 628#ifdef DEBUG_THREADS 629 xmlGenericError(xmlGenericErrorContext, "xmlUnlockLibrary()\n"); 630#endif 631 xmlRMutexUnlock(xmlLibraryLock); 632} 633 634/** 635 * xmlInitThreads: 636 * 637 * xmlInitThreads() is used to to initialize all the thread related 638 * data of the libxml2 library. 639 */ 640void 641xmlInitThreads(void) 642{ 643#ifdef DEBUG_THREADS 644 xmlGenericError(xmlGenericErrorContext, "xmlInitThreads()\n"); 645#endif 646#if defined(HAVE_WIN32_THREADS) && !defined(HAVE_COMPILER_TLS) && (!defined(LIBXML_STATIC) || defined(LIBXML_STATIC_FOR_DLL)) 647 InitializeCriticalSection(&cleanup_helpers_cs); 648#endif 649} 650 651/** 652 * xmlCleanupThreads: 653 * 654 * xmlCleanupThreads() is used to to cleanup all the thread related 655 * data of the libxml2 library once processing has ended. 656 */ 657void 658xmlCleanupThreads(void) 659{ 660#ifdef DEBUG_THREADS 661 xmlGenericError(xmlGenericErrorContext, "xmlCleanupThreads()\n"); 662#endif 663#if defined(HAVE_WIN32_THREADS) && !defined(HAVE_COMPILER_TLS) && (!defined(LIBXML_STATIC) || defined(LIBXML_STATIC_FOR_DLL)) 664 if (globalkey != TLS_OUT_OF_INDEXES) { 665 xmlGlobalStateCleanupHelperParams * p; 666 EnterCriticalSection(&cleanup_helpers_cs); 667 p = cleanup_helpers_head; 668 while (p != NULL) { 669 xmlGlobalStateCleanupHelperParams * temp = p; 670 p = p->next; 671 xmlFreeGlobalState(temp->memory); 672 free(temp); 673 } 674 cleanup_helpers_head = 0; 675 LeaveCriticalSection(&cleanup_helpers_cs); 676 TlsFree(globalkey); 677 globalkey = TLS_OUT_OF_INDEXES; 678 } 679 DeleteCriticalSection(&cleanup_helpers_cs); 680#endif 681} 682 683#ifdef LIBXML_THREAD_ENABLED 684/** 685 * xmlOnceInit 686 * 687 * xmlOnceInit() is used to initialize the value of mainthread for use 688 * in other routines. This function should only be called using 689 * pthread_once() in association with the once_control variable to ensure 690 * that the function is only called once. See man pthread_once for more 691 * details. 692 */ 693static void 694xmlOnceInit(void) { 695#ifdef HAVE_PTHREAD_H 696 (void) pthread_key_create(&globalkey, xmlFreeGlobalState); 697 mainthread = pthread_self(); 698#endif 699 700#if defined(HAVE_WIN32_THREADS) 701 if (!run_once.done) { 702 if (InterlockedIncrement(&run_once.control) == 1) 703 { 704#if !defined(HAVE_COMPILER_TLS) 705 globalkey = TlsAlloc(); 706#endif 707 mainthread = GetCurrentThreadId(); 708 run_once.done = 1; 709 } 710 else { 711 /* Another thread is working; give up our slice and 712 * wait until they're done. */ 713 while (!run_once.done) 714 Sleep(0); 715 } 716 } 717#endif 718 719#ifdef HAVE_BEOS_THREADS 720 if (atomic_add(&run_once_init, 1) == 0) { 721 globalkey = tls_allocate(); 722 tls_set(globalkey, NULL); 723 mainthread = find_thread(NULL); 724 } else 725 atomic_add(&run_once_init, -1); 726#endif 727} 728#endif 729 730/** 731 * DllMain: 732 * @hinstDLL: handle to DLL instance 733 * @fdwReason: Reason code for entry 734 * @lpvReserved: generic pointer (depends upon reason code) 735 * 736 * Entry point for Windows library. It is being used to free thread-specific 737 * storage. 738 * 739 * Returns TRUE always 740 */ 741#if defined(HAVE_WIN32_THREADS) && !defined(HAVE_COMPILER_TLS) && (!defined(LIBXML_STATIC) || defined(LIBXML_STATIC_FOR_DLL)) 742#if defined(LIBXML_STATIC_FOR_DLL) 743BOOL WINAPI xmlDllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved) 744#else 745BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved) 746#endif 747{ 748 switch(fdwReason) { 749 case DLL_THREAD_DETACH: 750 if (globalkey != TLS_OUT_OF_INDEXES) { 751 xmlGlobalState *globalval = NULL; 752 xmlGlobalStateCleanupHelperParams * p = 753 (xmlGlobalStateCleanupHelperParams*)TlsGetValue(globalkey); 754 globalval = (xmlGlobalState *)(p ? p->memory : NULL); 755 if (globalval) { 756 xmlFreeGlobalState(globalval); 757 TlsSetValue(globalkey,NULL); 758 } 759 if (p) 760 { 761 EnterCriticalSection(&cleanup_helpers_cs); 762 if (p == cleanup_helpers_head) 763 cleanup_helpers_head = p->next; 764 else 765 p->prev->next = p->next; 766 if (p->next != NULL) 767 p->next->prev = p->prev; 768 LeaveCriticalSection(&cleanup_helpers_cs); 769 free(p); 770 } 771 } 772 break; 773 } 774 return TRUE; 775} 776#endif 777#define bottom_threads 778#include "elfgcchack.h" 779