blob: 07a146987c4913571b9cbab7f4bc76bb45ede36e [file] [log] [blame]
Daniel Veillardb8478642001-10-12 17:29:10 +00001/**
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
Daniel Veillard34ce8be2002-03-18 19:37:11 +000010#define IN_LIBXML
Daniel Veillardb8478642001-10-12 17:29:10 +000011#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
Daniel Veillarddb0eb8d2002-01-13 13:35:00 +000030#ifdef HAVE_WIN32_THREADS
31#include <windows.h>
32#ifndef _MSC_VER
33#include <process.h>
34#endif
35#endif
Daniel Veillardb8478642001-10-12 17:29:10 +000036
37#if defined(SOLARIS)
38#include <note.h>
39#endif
40
Daniel Veillard6f350292001-10-14 09:56:15 +000041/* #define DEBUG_THREADS */
Daniel Veillardb8478642001-10-12 17:29:10 +000042
43/*
44 * TODO: this module still uses malloc/free and not xmlMalloc/xmlFree
45 * to avoid some crazyness since xmlMalloc/xmlFree may actually
46 * be hosted on allocated blocks needing them for the allocation ...
47 */
48
49/*
50 * xmlMutex are a simple mutual exception locks
51 */
52struct _xmlMutex {
53#ifdef HAVE_PTHREAD_H
54 pthread_mutex_t lock;
Daniel Veillarddb0eb8d2002-01-13 13:35:00 +000055#elif defined HAVE_WIN32_THREADS
56 HANDLE mutex;
Daniel Veillardb8478642001-10-12 17:29:10 +000057#else
58 int empty;
59#endif
60};
61
62/*
63 * xmlRMutex are reentrant mutual exception locks
64 */
65struct _xmlRMutex {
66#ifdef HAVE_PTHREAD_H
67 pthread_mutex_t lock;
68 unsigned int held;
69 unsigned int waiters;
70 pthread_t tid;
71 pthread_cond_t cv;
Daniel Veillarddb0eb8d2002-01-13 13:35:00 +000072#elif defined HAVE_WIN32_THREADS
73 CRITICAL_SECTION cs;
74 unsigned int count;
Daniel Veillardb8478642001-10-12 17:29:10 +000075#else
76 int empty;
77#endif
78};
79/*
80 * This module still has some internal static data.
81 * - xmlLibraryLock a global lock
82 * - globalkey used for per-thread data
Daniel Veillardb8478642001-10-12 17:29:10 +000083 */
Daniel Veillard6f350292001-10-14 09:56:15 +000084
Daniel Veillardb8478642001-10-12 17:29:10 +000085#ifdef HAVE_PTHREAD_H
Daniel Veillardb8478642001-10-12 17:29:10 +000086static pthread_key_t globalkey;
Daniel Veillard6f350292001-10-14 09:56:15 +000087static pthread_t mainthread;
Daniel Veillarde28313b2001-12-06 14:08:31 +000088static pthread_once_t once_control = PTHREAD_ONCE_INIT;
Daniel Veillarddb0eb8d2002-01-13 13:35:00 +000089#elif defined HAVE_WIN32_THREADS
Igor Zlatkovica6f2d902002-04-16 17:57:17 +000090#if defined(_MSC_VER) || defined(__BORLANDC__)
Daniel Veillarddb0eb8d2002-01-13 13:35:00 +000091static __declspec (thread) xmlGlobalState tlstate;
92static __declspec (thread) int tlstate_inited = 0;
93#else
94static DWORD globalkey;
95#endif /* _MSC_VER */
96static DWORD mainthread;
97static int run_once_init = 1;
98#endif /* HAVE_WIN32_THREADS */
Daniel Veillardb8478642001-10-12 17:29:10 +000099static xmlRMutexPtr xmlLibraryLock = NULL;
Daniel Veillarde28313b2001-12-06 14:08:31 +0000100static void xmlOnceInit(void);
Daniel Veillardb8478642001-10-12 17:29:10 +0000101
102/**
103 * xmlMutexPtr:
104 *
105 * xmlNewMutex() is used to allocate a libxml2 token struct for use in
106 * synchronizing access to data.
107 *
108 * Returns a new simple mutex pointer or NULL in case of error
109 */
110xmlMutexPtr
111xmlNewMutex(void)
112{
113 xmlMutexPtr tok;
114
115 if ((tok = malloc(sizeof(xmlMutex))) == NULL)
116 return (NULL);
117#ifdef HAVE_PTHREAD_H
118 pthread_mutex_init(&tok->lock, NULL);
Daniel Veillarddb0eb8d2002-01-13 13:35:00 +0000119#elif defined HAVE_WIN32_THREADS
120 tok->mutex = CreateMutex (NULL, FALSE, NULL);
Daniel Veillardb8478642001-10-12 17:29:10 +0000121#endif
122 return (tok);
123}
124
125/**
126 * xmlFreeMutex:
127 * @tok: the simple mutex
128 *
129 * xmlFreeMutex() is used to reclaim resources associated with a libxml2 token
130 * struct.
131 */
132void
133xmlFreeMutex(xmlMutexPtr tok)
134{
135#ifdef HAVE_PTHREAD_H
136 pthread_mutex_destroy(&tok->lock);
Daniel Veillarddb0eb8d2002-01-13 13:35:00 +0000137#elif defined HAVE_WIN32_THREADS
138 CloseHandle (tok->mutex);
Daniel Veillardb8478642001-10-12 17:29:10 +0000139#endif
140 free(tok);
141}
142
143/**
144 * xmlMutexLock:
145 * @tok: the simple mutex
146 *
147 * xmlMutexLock() is used to lock a libxml2 token.
148 */
149void
Daniel Veillard0ba59232002-02-10 13:20:39 +0000150xmlMutexLock(xmlMutexPtr tok ATTRIBUTE_UNUSED)
Daniel Veillardb8478642001-10-12 17:29:10 +0000151{
152#ifdef HAVE_PTHREAD_H
153 pthread_mutex_lock(&tok->lock);
Daniel Veillarddb0eb8d2002-01-13 13:35:00 +0000154#elif defined HAVE_WIN32_THREADS
155 WaitForSingleObject (tok->mutex, INFINITE);
Daniel Veillardb8478642001-10-12 17:29:10 +0000156#endif
157
158}
159
160/**
161 * xmlMutexUnlock:
162 * @tok: the simple mutex
163 *
164 * xmlMutexUnlock() is used to unlock a libxml2 token.
165 */
166void
Daniel Veillard0ba59232002-02-10 13:20:39 +0000167xmlMutexUnlock(xmlMutexPtr tok ATTRIBUTE_UNUSED)
Daniel Veillardb8478642001-10-12 17:29:10 +0000168{
169#ifdef HAVE_PTHREAD_H
170 pthread_mutex_unlock(&tok->lock);
Daniel Veillarddb0eb8d2002-01-13 13:35:00 +0000171#elif defined HAVE_WIN32_THREADS
172 ReleaseMutex (tok->mutex);
Daniel Veillardb8478642001-10-12 17:29:10 +0000173#endif
174}
175
176/**
177 * xmlRNewMutex:
178 *
179 * xmlRNewMutex() is used to allocate a reentrant mutex for use in
180 * synchronizing access to data. token_r is a re-entrant lock and thus useful
181 * for synchronizing access to data structures that may be manipulated in a
182 * recursive fashion.
183 *
184 * Returns the new reentrant mutex pointer or NULL in case of error
185 */
186xmlRMutexPtr
187xmlNewRMutex(void)
188{
189 xmlRMutexPtr tok;
190
191 if ((tok = malloc(sizeof(xmlRMutex))) == NULL)
192 return (NULL);
193#ifdef HAVE_PTHREAD_H
194 pthread_mutex_init(&tok->lock, NULL);
195 tok->held = 0;
196 tok->waiters = 0;
Daniel Veillarddb0eb8d2002-01-13 13:35:00 +0000197#elif defined HAVE_WIN32_THREADS
198 InitializeCriticalSection (&tok->cs);
199 tok->count = 0;
Daniel Veillardb8478642001-10-12 17:29:10 +0000200#endif
201 return (tok);
202}
203
204/**
205 * xmlRFreeMutex:
206 * @tok: the reentrant mutex
207 *
208 * xmlRFreeMutex() is used to reclaim resources associated with a
209 * reentrant mutex.
210 */
211void
Daniel Veillard0ba59232002-02-10 13:20:39 +0000212xmlFreeRMutex(xmlRMutexPtr tok ATTRIBUTE_UNUSED)
Daniel Veillardb8478642001-10-12 17:29:10 +0000213{
214#ifdef HAVE_PTHREAD_H
215 pthread_mutex_destroy(&tok->lock);
Daniel Veillarddb0eb8d2002-01-13 13:35:00 +0000216#elif defined HAVE_WIN32_THREADS
217 DeleteCriticalSection (&tok->cs);
Daniel Veillardb8478642001-10-12 17:29:10 +0000218#endif
219 free(tok);
220}
221
222/**
223 * xmlRMutexLock:
224 * @tok: the reentrant mutex
225 *
226 * xmlRMutexLock() is used to lock a libxml2 token_r.
227 */
228void
Daniel Veillard0ba59232002-02-10 13:20:39 +0000229xmlRMutexLock(xmlRMutexPtr tok ATTRIBUTE_UNUSED)
Daniel Veillardb8478642001-10-12 17:29:10 +0000230{
231#ifdef HAVE_PTHREAD_H
232 pthread_mutex_lock(&tok->lock);
233 if (tok->held) {
234 if (pthread_equal(tok->tid, pthread_self())) {
235 tok->held++;
236 pthread_mutex_unlock(&tok->lock);
237 return;
238 } else {
239 tok->waiters++;
240 while (tok->held)
241 pthread_cond_wait(&tok->cv, &tok->lock);
242 tok->waiters--;
243 }
244 }
245 tok->tid = pthread_self();
246 tok->held = 1;
247 pthread_mutex_unlock(&tok->lock);
Daniel Veillarddb0eb8d2002-01-13 13:35:00 +0000248#elif defined HAVE_WIN32_THREADS
249 EnterCriticalSection (&tok->cs);
250 ++tok->count;
Daniel Veillardb8478642001-10-12 17:29:10 +0000251#endif
252}
253
254/**
255 * xmlRMutexUnlock:
256 * @tok: the reentrant mutex
257 *
258 * xmlRMutexUnlock() is used to unlock a libxml2 token_r.
259 */
260void
Daniel Veillard0ba59232002-02-10 13:20:39 +0000261xmlRMutexUnlock(xmlRMutexPtr tok ATTRIBUTE_UNUSED)
Daniel Veillardb8478642001-10-12 17:29:10 +0000262{
263#ifdef HAVE_PTHREAD_H
264 pthread_mutex_lock(&tok->lock);
265 tok->held--;
266 if (tok->held == 0) {
267 if (tok->waiters)
268 pthread_cond_signal(&tok->cv);
269 tok->tid = 0;
270 }
271 pthread_mutex_unlock(&tok->lock);
Daniel Veillarddb0eb8d2002-01-13 13:35:00 +0000272#elif defined HAVE_WIN32_THREADS
273 if (!--tok->count) LeaveCriticalSection (&tok->cs);
Daniel Veillardb8478642001-10-12 17:29:10 +0000274#endif
275}
276
277/************************************************************************
278 * *
279 * Per thread global state handling *
280 * *
281 ************************************************************************/
282
Daniel Veillard8bdb91d2001-10-31 17:52:43 +0000283#ifdef LIBXML_THREAD_ENABLED
Daniel Veillarddb0eb8d2002-01-13 13:35:00 +0000284#ifndef _MSC_VER
Daniel Veillardb8478642001-10-12 17:29:10 +0000285/**
286 * xmlFreeGlobalState:
287 * @state: a thread global state
288 *
289 * xmlFreeGlobalState() is called when a thread terminates with a non-NULL
290 * global state. It is is used here to reclaim memory resources.
291 */
292static void
293xmlFreeGlobalState(void *state)
294{
295 free(state);
296}
297
298/**
299 * xmlNewGlobalState:
300 *
301 * xmlNewGlobalState() allocates a global state. This structure is used to
302 * hold all data for use by a thread when supporting backwards compatibility
Daniel Veillardcbaf3992001-12-31 16:16:02 +0000303 * of libxml2 to pre-thread-safe behaviour.
Daniel Veillardb8478642001-10-12 17:29:10 +0000304 *
305 * Returns the newly allocated xmlGlobalStatePtr or NULL in case of error
306 */
307static xmlGlobalStatePtr
308xmlNewGlobalState(void)
309{
310 xmlGlobalState *gs;
311
312 gs = malloc(sizeof(xmlGlobalState));
313 if (gs == NULL)
314 return(NULL);
315
316 memset(gs, 0, sizeof(gs));
317 xmlInitializeGlobalState(gs);
318 return (gs);
319}
Daniel Veillarddb0eb8d2002-01-13 13:35:00 +0000320#endif /* _MSC_VER */
Daniel Veillard8bdb91d2001-10-31 17:52:43 +0000321#endif /* LIBXML_THREAD_ENABLED */
Daniel Veillardb8478642001-10-12 17:29:10 +0000322
323
324/**
325 * xmlGetGlobalState:
326 *
327 * xmlGetGlobalState() is called to retrieve the global state for a thread.
Daniel Veillardb8478642001-10-12 17:29:10 +0000328 *
329 * Returns the thread global state or NULL in case of error
330 */
Daniel Veillarddb0eb8d2002-01-13 13:35:00 +0000331
332#ifdef HAVE_WIN32_THREADS
Igor Zlatkovica6f2d902002-04-16 17:57:17 +0000333#if !defined(_MSC_VER) && !defined(__BORLANDC__)
Daniel Veillarddb0eb8d2002-01-13 13:35:00 +0000334typedef struct _xmlGlobalStateCleanupHelperParams
335{
336 HANDLE thread;
337 void *memory;
338} xmlGlobalStateCleanupHelperParams;
339
340void __cdecl xmlGlobalStateCleanupHelper (void *p)
341{
342 xmlGlobalStateCleanupHelperParams *params = (xmlGlobalStateCleanupHelperParams *) p;
343 WaitForSingleObject (params->thread, INFINITE);
344 CloseHandle (params->thread);
345 xmlFreeGlobalState (params->memory);
346 free (params);
347 _endthread ();
348}
349#endif /* _MSC_VER */
350#endif /* HAVE_WIN32_THREADS */
351
Daniel Veillardb8478642001-10-12 17:29:10 +0000352xmlGlobalStatePtr
353xmlGetGlobalState(void)
354{
355#ifdef HAVE_PTHREAD_H
356 xmlGlobalState *globalval;
357
Daniel Veillarde28313b2001-12-06 14:08:31 +0000358 pthread_once(&once_control, xmlOnceInit);
359
Daniel Veillardb8478642001-10-12 17:29:10 +0000360 if ((globalval = (xmlGlobalState *)
361 pthread_getspecific(globalkey)) == NULL) {
362 xmlGlobalState *tsd = xmlNewGlobalState();
363
364 pthread_setspecific(globalkey, tsd);
365 return (tsd);
Daniel Veillard6f350292001-10-14 09:56:15 +0000366 }
367 return (globalval);
Daniel Veillarddb0eb8d2002-01-13 13:35:00 +0000368#elif defined HAVE_WIN32_THREADS
Igor Zlatkovica6f2d902002-04-16 17:57:17 +0000369#if defined(_MSC_VER) || defined(__BORLANDC__)
Daniel Veillarddb0eb8d2002-01-13 13:35:00 +0000370 if (!tlstate_inited)
371 {
372 tlstate_inited = 1;
373 xmlInitializeGlobalState (&tlstate);
374 }
375
376 return &tlstate;
377#else /* !_MSC_VER */
378 xmlGlobalState *globalval;
379
380 if (run_once_init) { run_once_init = 0; xmlOnceInit (); }
381
382 if ((globalval = (xmlGlobalState *) TlsGetValue (globalkey)) == NULL)
383 {
384 xmlGlobalState *tsd = xmlNewGlobalState();
385 xmlGlobalStateCleanupHelperParams *p = (xmlGlobalStateCleanupHelperParams *) malloc (sizeof (xmlGlobalStateCleanupHelperParams));
386
387 p->memory = tsd;
388 DuplicateHandle (GetCurrentProcess (), GetCurrentThread (), GetCurrentProcess (), &p->thread, 0, TRUE, DUPLICATE_SAME_ACCESS);
389
390 TlsSetValue (globalkey, tsd);
391 _beginthread (xmlGlobalStateCleanupHelper, 0, p);
392
393 return (tsd);
394 }
395 return (globalval);
396#endif /* _MSC_VER */
Daniel Veillard6f350292001-10-14 09:56:15 +0000397#else
398 return(NULL);
Daniel Veillardb8478642001-10-12 17:29:10 +0000399#endif
400}
401
Daniel Veillardb8478642001-10-12 17:29:10 +0000402/************************************************************************
403 * *
404 * Library wide thread interfaces *
405 * *
406 ************************************************************************/
407
408/**
Daniel Veillard3c01b1d2001-10-17 15:58:35 +0000409 * xmlGetThreadId:
410 *
411 * xmlGetThreadId() find the current thread ID number
412 *
413 * Returns the current thread ID number
414 */
415int
416xmlGetThreadId(void)
417{
418#ifdef HAVE_PTHREAD_H
419 return((int) pthread_self());
Daniel Veillarddb0eb8d2002-01-13 13:35:00 +0000420#elif defined HAVE_WIN32_THREADS
421 return GetCurrentThreadId ();
Daniel Veillard3c01b1d2001-10-17 15:58:35 +0000422#else
423 return((int) 0);
424#endif
425}
426
427/**
Daniel Veillard6f350292001-10-14 09:56:15 +0000428 * xmlIsMainThread:
429 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +0000430 * xmlIsMainThread() check whether the current thread is the main thread.
Daniel Veillard6f350292001-10-14 09:56:15 +0000431 *
432 * Returns 1 if the current thread is the main thread, 0 otherwise
433 */
434int
435xmlIsMainThread(void)
436{
Daniel Veillarde28313b2001-12-06 14:08:31 +0000437#ifdef HAVE_PTHREAD_H
438 pthread_once(&once_control, xmlOnceInit);
Daniel Veillarddb0eb8d2002-01-13 13:35:00 +0000439#elif defined HAVE_WIN32_THREADS
440 if (run_once_init) { run_once_init = 0; xmlOnceInit (); }
Daniel Veillarde28313b2001-12-06 14:08:31 +0000441#endif
Daniel Veillard6f350292001-10-14 09:56:15 +0000442
443#ifdef DEBUG_THREADS
444 xmlGenericError(xmlGenericErrorContext, "xmlIsMainThread()\n");
445#endif
446#ifdef HAVE_PTHREAD_H
447 return(mainthread == pthread_self());
Daniel Veillarddb0eb8d2002-01-13 13:35:00 +0000448#elif defined HAVE_WIN32_THREADS
449 return (mainthread == GetCurrentThreadId ());
Daniel Veillard6f350292001-10-14 09:56:15 +0000450#else
451 return(1);
452#endif
453}
454
455/**
Daniel Veillardb8478642001-10-12 17:29:10 +0000456 * xmlLockLibrary:
457 *
458 * xmlLockLibrary() is used to take out a re-entrant lock on the libxml2
459 * library.
460 */
461void
462xmlLockLibrary(void)
463{
Daniel Veillard6f350292001-10-14 09:56:15 +0000464#ifdef DEBUG_THREADS
465 xmlGenericError(xmlGenericErrorContext, "xmlLockLibrary()\n");
466#endif
Daniel Veillardb8478642001-10-12 17:29:10 +0000467 xmlRMutexLock(xmlLibraryLock);
468}
469
470/**
471 * xmlUnlockLibrary:
472 *
473 * xmlUnlockLibrary() is used to release a re-entrant lock on the libxml2
474 * library.
475 */
476void
477xmlUnlockLibrary(void)
478{
Daniel Veillard6f350292001-10-14 09:56:15 +0000479#ifdef DEBUG_THREADS
480 xmlGenericError(xmlGenericErrorContext, "xmlUnlockLibrary()\n");
481#endif
Daniel Veillardb8478642001-10-12 17:29:10 +0000482 xmlRMutexUnlock(xmlLibraryLock);
483}
484
485/**
486 * xmlInitThreads:
487 *
488 * xmlInitThreads() is used to to initialize all the thread related
489 * data of the libxml2 library.
490 */
491void
492xmlInitThreads(void)
493{
Daniel Veillard6f350292001-10-14 09:56:15 +0000494#ifdef DEBUG_THREADS
495 xmlGenericError(xmlGenericErrorContext, "xmlInitThreads()\n");
496#endif
Daniel Veillardb8478642001-10-12 17:29:10 +0000497}
498
499/**
500 * xmlCleanupThreads:
501 *
502 * xmlCleanupThreads() is used to to cleanup all the thread related
503 * data of the libxml2 library once processing has ended.
504 */
505void
506xmlCleanupThreads(void)
507{
Daniel Veillard6f350292001-10-14 09:56:15 +0000508#ifdef DEBUG_THREADS
509 xmlGenericError(xmlGenericErrorContext, "xmlCleanupThreads()\n");
510#endif
Daniel Veillarde28313b2001-12-06 14:08:31 +0000511}
Daniel Veillard6f350292001-10-14 09:56:15 +0000512
Daniel Veillarde28313b2001-12-06 14:08:31 +0000513/**
514 * xmlOnceInit
515 *
516 * xmlOnceInit() is used to initialize the value of mainthread for use
517 * in other routines. This function should only be called using
518 * pthread_once() in association with the once_control variable to ensure
519 * that the function is only called once. See man pthread_once for more
520 * details.
521 */
522static void
523xmlOnceInit(void) {
524#ifdef HAVE_PTHREAD_H
525 (void) pthread_key_create(&globalkey, xmlFreeGlobalState);
526 mainthread = pthread_self();
Daniel Veillarddb0eb8d2002-01-13 13:35:00 +0000527#elif defined HAVE_WIN32_THREADS
Igor Zlatkovica6f2d902002-04-16 17:57:17 +0000528#if !defined(_MSC_VER) && !defined(__BORLANDC__)
Daniel Veillarddb0eb8d2002-01-13 13:35:00 +0000529 globalkey = TlsAlloc ();
530#endif /* _MSC_VER */
531 mainthread = GetCurrentThreadId ();
Daniel Veillarde28313b2001-12-06 14:08:31 +0000532#endif
Daniel Veillardb8478642001-10-12 17:29:10 +0000533}