blob: dc69f4719e77d4eb4a906550a21b9118059a119c [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
10#include "libxml.h"
11
12#include <string.h>
13
14#include <libxml/threads.h>
15#include <libxml/globals.h>
16
17#ifdef HAVE_SYS_TYPES_H
18#include <sys/types.h>
19#endif
20#ifdef HAVE_UNISTD_H
21#include <unistd.h>
22#endif
23#ifdef HAVE_STDLIB_H
24#include <stdlib.h>
25#endif
26#ifdef HAVE_PTHREAD_H
27#include <pthread.h>
28#endif
Daniel Veillarddb0eb8d2002-01-13 13:35:00 +000029#ifdef HAVE_WIN32_THREADS
30#include <windows.h>
31#ifndef _MSC_VER
32#include <process.h>
33#endif
34#endif
Daniel Veillardb8478642001-10-12 17:29:10 +000035
36#if defined(SOLARIS)
37#include <note.h>
38#endif
39
Daniel Veillard6f350292001-10-14 09:56:15 +000040/* #define DEBUG_THREADS */
Daniel Veillardb8478642001-10-12 17:29:10 +000041
42/*
43 * TODO: this module still uses malloc/free and not xmlMalloc/xmlFree
44 * to avoid some crazyness since xmlMalloc/xmlFree may actually
45 * be hosted on allocated blocks needing them for the allocation ...
46 */
47
48/*
49 * xmlMutex are a simple mutual exception locks
50 */
51struct _xmlMutex {
52#ifdef HAVE_PTHREAD_H
53 pthread_mutex_t lock;
Daniel Veillarddb0eb8d2002-01-13 13:35:00 +000054#elif defined HAVE_WIN32_THREADS
55 HANDLE mutex;
Daniel Veillardb8478642001-10-12 17:29:10 +000056#else
57 int empty;
58#endif
59};
60
61/*
62 * xmlRMutex are reentrant mutual exception locks
63 */
64struct _xmlRMutex {
65#ifdef HAVE_PTHREAD_H
66 pthread_mutex_t lock;
67 unsigned int held;
68 unsigned int waiters;
69 pthread_t tid;
70 pthread_cond_t cv;
Daniel Veillarddb0eb8d2002-01-13 13:35:00 +000071#elif defined HAVE_WIN32_THREADS
72 CRITICAL_SECTION cs;
73 unsigned int count;
Daniel Veillardb8478642001-10-12 17:29:10 +000074#else
75 int empty;
76#endif
77};
78/*
79 * This module still has some internal static data.
80 * - xmlLibraryLock a global lock
81 * - globalkey used for per-thread data
Daniel Veillardb8478642001-10-12 17:29:10 +000082 */
Daniel Veillard6f350292001-10-14 09:56:15 +000083
Daniel Veillardb8478642001-10-12 17:29:10 +000084#ifdef HAVE_PTHREAD_H
Daniel Veillardb8478642001-10-12 17:29:10 +000085static pthread_key_t globalkey;
Daniel Veillard6f350292001-10-14 09:56:15 +000086static pthread_t mainthread;
Daniel Veillarde28313b2001-12-06 14:08:31 +000087static pthread_once_t once_control = PTHREAD_ONCE_INIT;
Daniel Veillarddb0eb8d2002-01-13 13:35:00 +000088#elif defined HAVE_WIN32_THREADS
89#ifdef _MSC_VER
90static __declspec (thread) xmlGlobalState tlstate;
91static __declspec (thread) int tlstate_inited = 0;
92#else
93static DWORD globalkey;
94#endif /* _MSC_VER */
95static DWORD mainthread;
96static int run_once_init = 1;
97#endif /* HAVE_WIN32_THREADS */
Daniel Veillardb8478642001-10-12 17:29:10 +000098static xmlRMutexPtr xmlLibraryLock = NULL;
Daniel Veillarde28313b2001-12-06 14:08:31 +000099static void xmlOnceInit(void);
Daniel Veillardb8478642001-10-12 17:29:10 +0000100
101/**
102 * xmlMutexPtr:
103 *
104 * xmlNewMutex() is used to allocate a libxml2 token struct for use in
105 * synchronizing access to data.
106 *
107 * Returns a new simple mutex pointer or NULL in case of error
108 */
109xmlMutexPtr
110xmlNewMutex(void)
111{
112 xmlMutexPtr tok;
113
114 if ((tok = malloc(sizeof(xmlMutex))) == NULL)
115 return (NULL);
116#ifdef HAVE_PTHREAD_H
117 pthread_mutex_init(&tok->lock, NULL);
Daniel Veillarddb0eb8d2002-01-13 13:35:00 +0000118#elif defined HAVE_WIN32_THREADS
119 tok->mutex = CreateMutex (NULL, FALSE, NULL);
Daniel Veillardb8478642001-10-12 17:29:10 +0000120#endif
121 return (tok);
122}
123
124/**
125 * xmlFreeMutex:
126 * @tok: the simple mutex
127 *
128 * xmlFreeMutex() is used to reclaim resources associated with a libxml2 token
129 * struct.
130 */
131void
132xmlFreeMutex(xmlMutexPtr tok)
133{
134#ifdef HAVE_PTHREAD_H
135 pthread_mutex_destroy(&tok->lock);
Daniel Veillarddb0eb8d2002-01-13 13:35:00 +0000136#elif defined HAVE_WIN32_THREADS
137 CloseHandle (tok->mutex);
Daniel Veillardb8478642001-10-12 17:29:10 +0000138#endif
139 free(tok);
140}
141
142/**
143 * xmlMutexLock:
144 * @tok: the simple mutex
145 *
146 * xmlMutexLock() is used to lock a libxml2 token.
147 */
148void
149xmlMutexLock(xmlMutexPtr tok)
150{
151#ifdef HAVE_PTHREAD_H
152 pthread_mutex_lock(&tok->lock);
Daniel Veillarddb0eb8d2002-01-13 13:35:00 +0000153#elif defined HAVE_WIN32_THREADS
154 WaitForSingleObject (tok->mutex, INFINITE);
Daniel Veillardb8478642001-10-12 17:29:10 +0000155#endif
156
157}
158
159/**
160 * xmlMutexUnlock:
161 * @tok: the simple mutex
162 *
163 * xmlMutexUnlock() is used to unlock a libxml2 token.
164 */
165void
166xmlMutexUnlock(xmlMutexPtr tok)
167{
168#ifdef HAVE_PTHREAD_H
169 pthread_mutex_unlock(&tok->lock);
Daniel Veillarddb0eb8d2002-01-13 13:35:00 +0000170#elif defined HAVE_WIN32_THREADS
171 ReleaseMutex (tok->mutex);
Daniel Veillardb8478642001-10-12 17:29:10 +0000172#endif
173}
174
175/**
176 * xmlRNewMutex:
177 *
178 * xmlRNewMutex() is used to allocate a reentrant mutex for use in
179 * synchronizing access to data. token_r is a re-entrant lock and thus useful
180 * for synchronizing access to data structures that may be manipulated in a
181 * recursive fashion.
182 *
183 * Returns the new reentrant mutex pointer or NULL in case of error
184 */
185xmlRMutexPtr
186xmlNewRMutex(void)
187{
188 xmlRMutexPtr tok;
189
190 if ((tok = malloc(sizeof(xmlRMutex))) == NULL)
191 return (NULL);
192#ifdef HAVE_PTHREAD_H
193 pthread_mutex_init(&tok->lock, NULL);
194 tok->held = 0;
195 tok->waiters = 0;
Daniel Veillarddb0eb8d2002-01-13 13:35:00 +0000196#elif defined HAVE_WIN32_THREADS
197 InitializeCriticalSection (&tok->cs);
198 tok->count = 0;
Daniel Veillardb8478642001-10-12 17:29:10 +0000199#endif
200 return (tok);
201}
202
203/**
204 * xmlRFreeMutex:
205 * @tok: the reentrant mutex
206 *
207 * xmlRFreeMutex() is used to reclaim resources associated with a
208 * reentrant mutex.
209 */
210void
211xmlFreeRMutex(xmlRMutexPtr tok)
212{
213#ifdef HAVE_PTHREAD_H
214 pthread_mutex_destroy(&tok->lock);
Daniel Veillarddb0eb8d2002-01-13 13:35:00 +0000215#elif defined HAVE_WIN32_THREADS
216 DeleteCriticalSection (&tok->cs);
Daniel Veillardb8478642001-10-12 17:29:10 +0000217#endif
218 free(tok);
219}
220
221/**
222 * xmlRMutexLock:
223 * @tok: the reentrant mutex
224 *
225 * xmlRMutexLock() is used to lock a libxml2 token_r.
226 */
227void
228xmlRMutexLock(xmlRMutexPtr tok)
229{
230#ifdef HAVE_PTHREAD_H
231 pthread_mutex_lock(&tok->lock);
232 if (tok->held) {
233 if (pthread_equal(tok->tid, pthread_self())) {
234 tok->held++;
235 pthread_mutex_unlock(&tok->lock);
236 return;
237 } else {
238 tok->waiters++;
239 while (tok->held)
240 pthread_cond_wait(&tok->cv, &tok->lock);
241 tok->waiters--;
242 }
243 }
244 tok->tid = pthread_self();
245 tok->held = 1;
246 pthread_mutex_unlock(&tok->lock);
Daniel Veillarddb0eb8d2002-01-13 13:35:00 +0000247#elif defined HAVE_WIN32_THREADS
248 EnterCriticalSection (&tok->cs);
249 ++tok->count;
Daniel Veillardb8478642001-10-12 17:29:10 +0000250#endif
251}
252
253/**
254 * xmlRMutexUnlock:
255 * @tok: the reentrant mutex
256 *
257 * xmlRMutexUnlock() is used to unlock a libxml2 token_r.
258 */
259void
260xmlRMutexUnlock(xmlRMutexPtr tok)
261{
262#ifdef HAVE_PTHREAD_H
263 pthread_mutex_lock(&tok->lock);
264 tok->held--;
265 if (tok->held == 0) {
266 if (tok->waiters)
267 pthread_cond_signal(&tok->cv);
268 tok->tid = 0;
269 }
270 pthread_mutex_unlock(&tok->lock);
Daniel Veillarddb0eb8d2002-01-13 13:35:00 +0000271#elif defined HAVE_WIN32_THREADS
272 if (!--tok->count) LeaveCriticalSection (&tok->cs);
Daniel Veillardb8478642001-10-12 17:29:10 +0000273#endif
274}
275
276/************************************************************************
277 * *
278 * Per thread global state handling *
279 * *
280 ************************************************************************/
281
Daniel Veillard8bdb91d2001-10-31 17:52:43 +0000282#ifdef LIBXML_THREAD_ENABLED
Daniel Veillarddb0eb8d2002-01-13 13:35:00 +0000283#ifndef _MSC_VER
Daniel Veillardb8478642001-10-12 17:29:10 +0000284/**
285 * xmlFreeGlobalState:
286 * @state: a thread global state
287 *
288 * xmlFreeGlobalState() is called when a thread terminates with a non-NULL
289 * global state. It is is used here to reclaim memory resources.
290 */
291static void
292xmlFreeGlobalState(void *state)
293{
294 free(state);
295}
296
297/**
298 * xmlNewGlobalState:
299 *
300 * xmlNewGlobalState() allocates a global state. This structure is used to
301 * hold all data for use by a thread when supporting backwards compatibility
Daniel Veillardcbaf3992001-12-31 16:16:02 +0000302 * of libxml2 to pre-thread-safe behaviour.
Daniel Veillardb8478642001-10-12 17:29:10 +0000303 *
304 * Returns the newly allocated xmlGlobalStatePtr or NULL in case of error
305 */
306static xmlGlobalStatePtr
307xmlNewGlobalState(void)
308{
309 xmlGlobalState *gs;
310
311 gs = malloc(sizeof(xmlGlobalState));
312 if (gs == NULL)
313 return(NULL);
314
315 memset(gs, 0, sizeof(gs));
316 xmlInitializeGlobalState(gs);
317 return (gs);
318}
Daniel Veillarddb0eb8d2002-01-13 13:35:00 +0000319#endif /* _MSC_VER */
Daniel Veillard8bdb91d2001-10-31 17:52:43 +0000320#endif /* LIBXML_THREAD_ENABLED */
Daniel Veillardb8478642001-10-12 17:29:10 +0000321
322
323/**
324 * xmlGetGlobalState:
325 *
326 * xmlGetGlobalState() is called to retrieve the global state for a thread.
Daniel Veillardb8478642001-10-12 17:29:10 +0000327 *
328 * Returns the thread global state or NULL in case of error
329 */
Daniel Veillarddb0eb8d2002-01-13 13:35:00 +0000330
331#ifdef HAVE_WIN32_THREADS
332#ifndef _MSC_VER
333typedef struct _xmlGlobalStateCleanupHelperParams
334{
335 HANDLE thread;
336 void *memory;
337} xmlGlobalStateCleanupHelperParams;
338
339void __cdecl xmlGlobalStateCleanupHelper (void *p)
340{
341 xmlGlobalStateCleanupHelperParams *params = (xmlGlobalStateCleanupHelperParams *) p;
342 WaitForSingleObject (params->thread, INFINITE);
343 CloseHandle (params->thread);
344 xmlFreeGlobalState (params->memory);
345 free (params);
346 _endthread ();
347}
348#endif /* _MSC_VER */
349#endif /* HAVE_WIN32_THREADS */
350
Daniel Veillardb8478642001-10-12 17:29:10 +0000351xmlGlobalStatePtr
352xmlGetGlobalState(void)
353{
354#ifdef HAVE_PTHREAD_H
355 xmlGlobalState *globalval;
356
Daniel Veillarde28313b2001-12-06 14:08:31 +0000357 pthread_once(&once_control, xmlOnceInit);
358
Daniel Veillardb8478642001-10-12 17:29:10 +0000359 if ((globalval = (xmlGlobalState *)
360 pthread_getspecific(globalkey)) == NULL) {
361 xmlGlobalState *tsd = xmlNewGlobalState();
362
363 pthread_setspecific(globalkey, tsd);
364 return (tsd);
Daniel Veillard6f350292001-10-14 09:56:15 +0000365 }
366 return (globalval);
Daniel Veillarddb0eb8d2002-01-13 13:35:00 +0000367#elif defined HAVE_WIN32_THREADS
368#ifdef _MSC_VER
369 if (!tlstate_inited)
370 {
371 tlstate_inited = 1;
372 xmlInitializeGlobalState (&tlstate);
373 }
374
375 return &tlstate;
376#else /* !_MSC_VER */
377 xmlGlobalState *globalval;
378
379 if (run_once_init) { run_once_init = 0; xmlOnceInit (); }
380
381 if ((globalval = (xmlGlobalState *) TlsGetValue (globalkey)) == NULL)
382 {
383 xmlGlobalState *tsd = xmlNewGlobalState();
384 xmlGlobalStateCleanupHelperParams *p = (xmlGlobalStateCleanupHelperParams *) malloc (sizeof (xmlGlobalStateCleanupHelperParams));
385
386 p->memory = tsd;
387 DuplicateHandle (GetCurrentProcess (), GetCurrentThread (), GetCurrentProcess (), &p->thread, 0, TRUE, DUPLICATE_SAME_ACCESS);
388
389 TlsSetValue (globalkey, tsd);
390 _beginthread (xmlGlobalStateCleanupHelper, 0, p);
391
392 return (tsd);
393 }
394 return (globalval);
395#endif /* _MSC_VER */
Daniel Veillard6f350292001-10-14 09:56:15 +0000396#else
397 return(NULL);
Daniel Veillardb8478642001-10-12 17:29:10 +0000398#endif
399}
400
Daniel Veillardb8478642001-10-12 17:29:10 +0000401/************************************************************************
402 * *
403 * Library wide thread interfaces *
404 * *
405 ************************************************************************/
406
407/**
Daniel Veillard3c01b1d2001-10-17 15:58:35 +0000408 * xmlGetThreadId:
409 *
410 * xmlGetThreadId() find the current thread ID number
411 *
412 * Returns the current thread ID number
413 */
414int
415xmlGetThreadId(void)
416{
417#ifdef HAVE_PTHREAD_H
418 return((int) pthread_self());
Daniel Veillarddb0eb8d2002-01-13 13:35:00 +0000419#elif defined HAVE_WIN32_THREADS
420 return GetCurrentThreadId ();
Daniel Veillard3c01b1d2001-10-17 15:58:35 +0000421#else
422 return((int) 0);
423#endif
424}
425
426/**
Daniel Veillard6f350292001-10-14 09:56:15 +0000427 * xmlIsMainThread:
428 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +0000429 * xmlIsMainThread() check whether the current thread is the main thread.
Daniel Veillard6f350292001-10-14 09:56:15 +0000430 *
431 * Returns 1 if the current thread is the main thread, 0 otherwise
432 */
433int
434xmlIsMainThread(void)
435{
Daniel Veillarde28313b2001-12-06 14:08:31 +0000436#ifdef HAVE_PTHREAD_H
437 pthread_once(&once_control, xmlOnceInit);
Daniel Veillarddb0eb8d2002-01-13 13:35:00 +0000438#elif defined HAVE_WIN32_THREADS
439 if (run_once_init) { run_once_init = 0; xmlOnceInit (); }
Daniel Veillarde28313b2001-12-06 14:08:31 +0000440#endif
Daniel Veillard6f350292001-10-14 09:56:15 +0000441
442#ifdef DEBUG_THREADS
443 xmlGenericError(xmlGenericErrorContext, "xmlIsMainThread()\n");
444#endif
445#ifdef HAVE_PTHREAD_H
446 return(mainthread == pthread_self());
Daniel Veillarddb0eb8d2002-01-13 13:35:00 +0000447#elif defined HAVE_WIN32_THREADS
448 return (mainthread == GetCurrentThreadId ());
Daniel Veillard6f350292001-10-14 09:56:15 +0000449#else
450 return(1);
451#endif
452}
453
454/**
Daniel Veillardb8478642001-10-12 17:29:10 +0000455 * xmlLockLibrary:
456 *
457 * xmlLockLibrary() is used to take out a re-entrant lock on the libxml2
458 * library.
459 */
460void
461xmlLockLibrary(void)
462{
Daniel Veillard6f350292001-10-14 09:56:15 +0000463#ifdef DEBUG_THREADS
464 xmlGenericError(xmlGenericErrorContext, "xmlLockLibrary()\n");
465#endif
Daniel Veillardb8478642001-10-12 17:29:10 +0000466 xmlRMutexLock(xmlLibraryLock);
467}
468
469/**
470 * xmlUnlockLibrary:
471 *
472 * xmlUnlockLibrary() is used to release a re-entrant lock on the libxml2
473 * library.
474 */
475void
476xmlUnlockLibrary(void)
477{
Daniel Veillard6f350292001-10-14 09:56:15 +0000478#ifdef DEBUG_THREADS
479 xmlGenericError(xmlGenericErrorContext, "xmlUnlockLibrary()\n");
480#endif
Daniel Veillardb8478642001-10-12 17:29:10 +0000481 xmlRMutexUnlock(xmlLibraryLock);
482}
483
484/**
485 * xmlInitThreads:
486 *
487 * xmlInitThreads() is used to to initialize all the thread related
488 * data of the libxml2 library.
489 */
490void
491xmlInitThreads(void)
492{
Daniel Veillard6f350292001-10-14 09:56:15 +0000493#ifdef DEBUG_THREADS
494 xmlGenericError(xmlGenericErrorContext, "xmlInitThreads()\n");
495#endif
Daniel Veillardb8478642001-10-12 17:29:10 +0000496}
497
498/**
499 * xmlCleanupThreads:
500 *
501 * xmlCleanupThreads() is used to to cleanup all the thread related
502 * data of the libxml2 library once processing has ended.
503 */
504void
505xmlCleanupThreads(void)
506{
Daniel Veillard6f350292001-10-14 09:56:15 +0000507#ifdef DEBUG_THREADS
508 xmlGenericError(xmlGenericErrorContext, "xmlCleanupThreads()\n");
509#endif
Daniel Veillarde28313b2001-12-06 14:08:31 +0000510}
Daniel Veillard6f350292001-10-14 09:56:15 +0000511
Daniel Veillarde28313b2001-12-06 14:08:31 +0000512/**
513 * xmlOnceInit
514 *
515 * xmlOnceInit() is used to initialize the value of mainthread for use
516 * in other routines. This function should only be called using
517 * pthread_once() in association with the once_control variable to ensure
518 * that the function is only called once. See man pthread_once for more
519 * details.
520 */
521static void
522xmlOnceInit(void) {
523#ifdef HAVE_PTHREAD_H
524 (void) pthread_key_create(&globalkey, xmlFreeGlobalState);
525 mainthread = pthread_self();
Daniel Veillarddb0eb8d2002-01-13 13:35:00 +0000526#elif defined HAVE_WIN32_THREADS
527#ifndef _MSC_VER
528 globalkey = TlsAlloc ();
529#endif /* _MSC_VER */
530 mainthread = GetCurrentThreadId ();
Daniel Veillarde28313b2001-12-06 14:08:31 +0000531#endif
Daniel Veillardb8478642001-10-12 17:29:10 +0000532}