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