updated libxml2 to 2.5.10
[TestXSLT.git] / libxml2 / threads.c
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 #if defined(SOLARIS)
39 #include <note.h>
40 #endif
41
42 /* #define DEBUG_THREADS */
43
44 /*
45  * TODO: this module still uses malloc/free and not xmlMalloc/xmlFree
46  *       to avoid some crazyness since xmlMalloc/xmlFree may actually
47  *       be hosted on allocated blocks needing them for the allocation ...
48  */
49
50 /*
51  * xmlMutex are a simple mutual exception locks
52  */
53 struct _xmlMutex {
54 #ifdef HAVE_PTHREAD_H
55     pthread_mutex_t lock;
56 #elif defined HAVE_WIN32_THREADS
57     HANDLE mutex;
58 #else
59     int empty;
60 #endif
61 };
62
63 /*
64  * xmlRMutex are reentrant mutual exception locks
65  */
66 struct _xmlRMutex {
67 #ifdef HAVE_PTHREAD_H
68     pthread_mutex_t lock;
69     unsigned int    held;
70     unsigned int    waiters;
71     pthread_t       tid;
72     pthread_cond_t  cv;
73 #elif defined HAVE_WIN32_THREADS
74     CRITICAL_SECTION cs;
75     unsigned int count;
76 #else
77     int empty;
78 #endif
79 };
80 /*
81  * This module still has some internal static data.
82  *   - xmlLibraryLock a global lock
83  *   - globalkey used for per-thread data
84  */
85
86 #ifdef HAVE_PTHREAD_H
87 static pthread_key_t    globalkey;
88 static pthread_t        mainthread;
89 static pthread_once_t once_control = PTHREAD_ONCE_INIT;
90 #elif defined HAVE_WIN32_THREADS
91 #if defined(HAVE_COMPILER_TLS)
92 static __declspec(thread) xmlGlobalState tlstate;
93 static __declspec(thread) int tlstate_inited = 0;
94 #else /* HAVE_COMPILER_TLS */
95 static DWORD globalkey = TLS_OUT_OF_INDEXES;
96 #endif /* HAVE_COMPILER_TLS */
97 static DWORD mainthread;
98 static int run_once_init = 1;
99 #endif /* HAVE_WIN32_THREADS */
100
101 static xmlRMutexPtr     xmlLibraryLock = NULL;
102 static void xmlOnceInit(void);
103
104 /**
105  * xmlNewMutex:
106  *
107  * xmlNewMutex() is used to allocate a libxml2 token struct for use in
108  * synchronizing access to data.
109  *
110  * Returns a new simple mutex pointer or NULL in case of error
111  */
112 xmlMutexPtr
113 xmlNewMutex(void)
114 {
115     xmlMutexPtr tok;
116
117     if ((tok = malloc(sizeof(xmlMutex))) == NULL)
118         return (NULL);
119 #ifdef HAVE_PTHREAD_H
120     pthread_mutex_init(&tok->lock, NULL);
121 #elif defined HAVE_WIN32_THREADS
122     tok->mutex = CreateMutex(NULL, FALSE, NULL);
123 #endif
124     return (tok);
125 }
126
127 /**
128  * xmlFreeMutex:
129  * @tok:  the simple mutex
130  *
131  * xmlFreeMutex() is used to reclaim resources associated with a libxml2 token
132  * struct.
133  */
134 void
135 xmlFreeMutex(xmlMutexPtr tok)
136 {
137     if (tok == NULL) return;
138
139 #ifdef HAVE_PTHREAD_H
140     pthread_mutex_destroy(&tok->lock);
141 #elif defined HAVE_WIN32_THREADS
142     CloseHandle(tok->mutex);
143 #endif
144     free(tok);
145 }
146
147 /**
148  * xmlMutexLock:
149  * @tok:  the simple mutex
150  *
151  * xmlMutexLock() is used to lock a libxml2 token.
152  */
153 void
154 xmlMutexLock(xmlMutexPtr tok)
155 {
156     if (tok == NULL)
157         return;
158 #ifdef HAVE_PTHREAD_H
159     pthread_mutex_lock(&tok->lock);
160 #elif defined HAVE_WIN32_THREADS
161     WaitForSingleObject(tok->mutex, INFINITE);
162 #endif
163
164 }
165
166 /**
167  * xmlMutexUnlock:
168  * @tok:  the simple mutex
169  *
170  * xmlMutexUnlock() is used to unlock a libxml2 token.
171  */
172 void
173 xmlMutexUnlock(xmlMutexPtr tok ATTRIBUTE_UNUSED)
174 {
175 #ifdef HAVE_PTHREAD_H
176     pthread_mutex_unlock(&tok->lock);
177 #elif defined HAVE_WIN32_THREADS
178     ReleaseMutex(tok->mutex);
179 #endif
180 }
181
182 /**
183  * xmlNewRMutex:
184  *
185  * xmlRNewMutex() is used to allocate a reentrant mutex for use in
186  * synchronizing access to data. token_r is a re-entrant lock and thus useful
187  * for synchronizing access to data structures that may be manipulated in a
188  * recursive fashion.
189  *
190  * Returns the new reentrant mutex pointer or NULL in case of error
191  */
192 xmlRMutexPtr
193 xmlNewRMutex(void)
194 {
195     xmlRMutexPtr tok;
196
197     if ((tok = malloc(sizeof(xmlRMutex))) == NULL)
198         return (NULL);
199 #ifdef HAVE_PTHREAD_H
200     pthread_mutex_init(&tok->lock, NULL);
201     tok->held = 0;
202     tok->waiters = 0;
203     pthread_cond_init(&tok->cv, NULL);
204 #elif defined HAVE_WIN32_THREADS
205     InitializeCriticalSection(&tok->cs);
206     tok->count = 0;
207 #endif
208     return (tok);
209 }
210
211 /**
212  * xmlFreeRMutex:
213  * @tok:  the reentrant mutex
214  *
215  * xmlRFreeMutex() is used to reclaim resources associated with a
216  * reentrant mutex.
217  */
218 void
219 xmlFreeRMutex(xmlRMutexPtr tok ATTRIBUTE_UNUSED)
220 {
221 #ifdef HAVE_PTHREAD_H
222     pthread_mutex_destroy(&tok->lock);
223 #elif defined HAVE_WIN32_THREADS
224     DeleteCriticalSection(&tok->cs);
225 #endif
226     free(tok);
227 }
228
229 /**
230  * xmlRMutexLock:
231  * @tok:  the reentrant mutex
232  *
233  * xmlRMutexLock() is used to lock a libxml2 token_r.
234  */
235 void
236 xmlRMutexLock(xmlRMutexPtr tok ATTRIBUTE_UNUSED)
237 {
238 #ifdef HAVE_PTHREAD_H
239     pthread_mutex_lock(&tok->lock);
240     if (tok->held) {
241         if (pthread_equal(tok->tid, pthread_self())) {
242             tok->held++;
243             pthread_mutex_unlock(&tok->lock);
244             return;
245         } else {
246             tok->waiters++;
247             while (tok->held)
248                 pthread_cond_wait(&tok->cv, &tok->lock);
249             tok->waiters--;
250         }
251     }
252     tok->tid = pthread_self();
253     tok->held = 1;
254     pthread_mutex_unlock(&tok->lock);
255 #elif defined HAVE_WIN32_THREADS
256     EnterCriticalSection(&tok->cs);
257     ++tok->count;
258 #endif
259 }
260
261 /**
262  * xmlRMutexUnlock:
263  * @tok:  the reentrant mutex
264  *
265  * xmlRMutexUnlock() is used to unlock a libxml2 token_r.
266  */
267 void
268 xmlRMutexUnlock(xmlRMutexPtr tok ATTRIBUTE_UNUSED)
269 {
270 #ifdef HAVE_PTHREAD_H
271     pthread_mutex_lock(&tok->lock);
272     tok->held--;
273     if (tok->held == 0) {
274         if (tok->waiters)
275             pthread_cond_signal(&tok->cv);
276         tok->tid = 0;
277     }
278     pthread_mutex_unlock(&tok->lock);
279 #elif defined HAVE_WIN32_THREADS
280     if (!--tok->count) 
281         LeaveCriticalSection(&tok->cs);
282 #endif
283 }
284
285 /************************************************************************
286  *                                                                      *
287  *                      Per thread global state handling                *
288  *                                                                      *
289  ************************************************************************/
290
291 #ifdef LIBXML_THREAD_ENABLED
292 /**
293  * xmlFreeGlobalState:
294  * @state:  a thread global state
295  *
296  * xmlFreeGlobalState() is called when a thread terminates with a non-NULL
297  * global state. It is is used here to reclaim memory resources.
298  */
299 static void
300 xmlFreeGlobalState(void *state)
301 {
302     free(state);
303 }
304
305 /**
306  * xmlNewGlobalState:
307  *
308  * xmlNewGlobalState() allocates a global state. This structure is used to
309  * hold all data for use by a thread when supporting backwards compatibility
310  * of libxml2 to pre-thread-safe behaviour.
311  *
312  * Returns the newly allocated xmlGlobalStatePtr or NULL in case of error
313  */
314 static xmlGlobalStatePtr
315 xmlNewGlobalState(void)
316 {
317     xmlGlobalState *gs;
318     
319     gs = malloc(sizeof(xmlGlobalState));
320     if (gs == NULL)
321         return(NULL);
322
323     memset(gs, 0, sizeof(xmlGlobalState));
324     xmlInitializeGlobalState(gs);
325     return (gs);
326 }
327 #endif /* LIBXML_THREAD_ENABLED */
328
329
330 #ifdef HAVE_WIN32_THREADS
331 #if !defined(HAVE_COMPILER_TLS) && defined(LIBXML_STATIC)
332 typedef struct _xmlGlobalStateCleanupHelperParams
333 {
334     HANDLE thread;
335     void *memory;
336 } xmlGlobalStateCleanupHelperParams;
337
338 static void xmlGlobalStateCleanupHelper (void *p)
339 {
340     xmlGlobalStateCleanupHelperParams *params = (xmlGlobalStateCleanupHelperParams *) p;
341     WaitForSingleObject(params->thread, INFINITE);
342     CloseHandle(params->thread);
343     xmlFreeGlobalState(params->memory);
344     free(params);
345     _endthread();
346 }
347 #endif /* HAVE_COMPILER_TLS && LIBXML_STATIC */
348 #endif /* HAVE_WIN32_THREADS */
349
350 /**
351  * xmlGetGlobalState:
352  *
353  * xmlGetGlobalState() is called to retrieve the global state for a thread.
354  *
355  * Returns the thread global state or NULL in case of error
356  */
357 xmlGlobalStatePtr
358 xmlGetGlobalState(void)
359 {
360 #ifdef HAVE_PTHREAD_H
361     xmlGlobalState *globalval;
362
363     pthread_once(&once_control, xmlOnceInit);
364
365     if ((globalval = (xmlGlobalState *)
366                 pthread_getspecific(globalkey)) == NULL) {
367         xmlGlobalState *tsd = xmlNewGlobalState();
368
369         pthread_setspecific(globalkey, tsd);
370         return (tsd);
371     }
372     return (globalval);
373 #elif defined HAVE_WIN32_THREADS
374 #if defined(HAVE_COMPILER_TLS)
375     if (!tlstate_inited) {
376         tlstate_inited = 1;
377         xmlInitializeGlobalState(&tlstate);
378     }
379     return &tlstate;
380 #else /* HAVE_COMPILER_TLS */
381     xmlGlobalState *globalval;
382
383     if (run_once_init) { 
384         run_once_init = 0; 
385         xmlOnceInit(); 
386     }
387     if ((globalval = (xmlGlobalState *) TlsGetValue(globalkey)) == NULL) {
388         xmlGlobalState *tsd = xmlNewGlobalState();
389 #if defined(LIBXML_STATIC)
390         xmlGlobalStateCleanupHelperParams *p = 
391             (xmlGlobalStateCleanupHelperParams *) malloc(sizeof(xmlGlobalStateCleanupHelperParams));
392         p->memory = tsd;
393         DuplicateHandle(GetCurrentProcess(), GetCurrentThread(), 
394                 GetCurrentProcess(), &p->thread, 0, TRUE, DUPLICATE_SAME_ACCESS);
395 #endif
396         TlsSetValue(globalkey, tsd);
397 #if defined(LIBXML_STATIC)
398         _beginthread(xmlGlobalStateCleanupHelper, 0, p);
399 #endif
400
401         return (tsd);
402     }
403     return (globalval);
404 #endif /* HAVE_COMPILER_TLS */
405 #else
406     return(NULL);
407 #endif
408 }
409
410 /************************************************************************
411  *                                                                      *
412  *                      Library wide thread interfaces                  *
413  *                                                                      *
414  ************************************************************************/
415
416 /**
417  * xmlGetThreadId:
418  *
419  * xmlGetThreadId() find the current thread ID number
420  *
421  * Returns the current thread ID number
422  */
423 int
424 xmlGetThreadId(void)
425 {
426 #ifdef HAVE_PTHREAD_H
427     return((int) pthread_self());
428 #elif defined HAVE_WIN32_THREADS
429     return GetCurrentThreadId();
430 #else
431     return((int) 0);
432 #endif
433 }
434
435 /**
436  * xmlIsMainThread:
437  *
438  * xmlIsMainThread() check whether the current thread is the main thread.
439  *
440  * Returns 1 if the current thread is the main thread, 0 otherwise
441  */
442 int
443 xmlIsMainThread(void)
444 {
445 #ifdef HAVE_PTHREAD_H
446     pthread_once(&once_control, xmlOnceInit);
447 #elif defined HAVE_WIN32_THREADS
448     if (run_once_init) { 
449         run_once_init = 0; 
450         xmlOnceInit (); 
451     }
452 #endif
453         
454 #ifdef DEBUG_THREADS
455     xmlGenericError(xmlGenericErrorContext, "xmlIsMainThread()\n");
456 #endif
457 #ifdef HAVE_PTHREAD_H
458     return(mainthread == pthread_self());
459 #elif defined HAVE_WIN32_THREADS
460     return(mainthread == GetCurrentThreadId ());
461 #else
462     return(1);
463 #endif
464 }
465
466 /**
467  * xmlLockLibrary:
468  *
469  * xmlLockLibrary() is used to take out a re-entrant lock on the libxml2
470  * library.
471  */
472 void
473 xmlLockLibrary(void)
474 {
475 #ifdef DEBUG_THREADS
476     xmlGenericError(xmlGenericErrorContext, "xmlLockLibrary()\n");
477 #endif
478     xmlRMutexLock(xmlLibraryLock);
479 }
480
481 /**
482  * xmlUnlockLibrary:
483  *
484  * xmlUnlockLibrary() is used to release a re-entrant lock on the libxml2
485  * library.
486  */
487 void
488 xmlUnlockLibrary(void)
489 {
490 #ifdef DEBUG_THREADS
491     xmlGenericError(xmlGenericErrorContext, "xmlUnlockLibrary()\n");
492 #endif
493     xmlRMutexUnlock(xmlLibraryLock);
494 }
495
496 /**
497  * xmlInitThreads:
498  *
499  * xmlInitThreads() is used to to initialize all the thread related
500  * data of the libxml2 library.
501  */
502 void
503 xmlInitThreads(void)
504 {
505 #ifdef DEBUG_THREADS
506     xmlGenericError(xmlGenericErrorContext, "xmlInitThreads()\n");
507 #endif
508 }
509
510 /**
511  * xmlCleanupThreads:
512  *
513  * xmlCleanupThreads() is used to to cleanup all the thread related
514  * data of the libxml2 library once processing has ended.
515  */
516 void
517 xmlCleanupThreads(void)
518 {
519 #ifdef DEBUG_THREADS
520     xmlGenericError(xmlGenericErrorContext, "xmlCleanupThreads()\n");
521 #endif
522 }
523
524 /**
525  * xmlOnceInit
526  *
527  * xmlOnceInit() is used to initialize the value of mainthread for use
528  * in other routines. This function should only be called using
529  * pthread_once() in association with the once_control variable to ensure
530  * that the function is only called once. See man pthread_once for more
531  * details.
532  */
533 static void
534 xmlOnceInit(void) {
535 #ifdef HAVE_PTHREAD_H
536     (void) pthread_key_create(&globalkey, xmlFreeGlobalState);
537     mainthread = pthread_self();
538 #endif
539
540 #if defined(HAVE_WIN32_THREADS)
541 #if !defined(HAVE_COMPILER_TLS)
542     globalkey = TlsAlloc();
543 #endif
544     mainthread = GetCurrentThreadId();
545 #endif
546 }
547
548 /**
549  * DllMain
550  *
551  * Entry point for Windows library. It is being used to free thread-specific
552  * storage.
553  */
554 #if defined(HAVE_WIN32_THREADS) && !defined(LIBXML_STATIC)
555 BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved) 
556 {
557     switch(fdwReason) {
558     case DLL_THREAD_DETACH:
559         if (globalkey != TLS_OUT_OF_INDEXES) {
560             xmlGlobalState *globalval = (xmlGlobalState *)TlsGetValue(globalkey);
561             if (globalval) {
562                 xmlFreeGlobalState(globalval);
563                 TlsSetValue(globalkey, NULL);
564             }
565         }
566         break;
567     case DLL_PROCESS_DETACH:
568         if (globalkey != TLS_OUT_OF_INDEXES) {
569             TlsFree(globalkey);
570             globalkey = TLS_OUT_OF_INDEXES;
571         }
572         break;
573     }
574     return TRUE;
575 }
576 #endif
577