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 | #elif defined HAVE_WIN32_THREADS |
30 | #define WIN32_LEAN_AND_MEAN |
31 | #include <windows.h> |
32 | #ifndef HAVE_COMPILER_TLS |
33 | #include <process.h> |
34 | #endif |
35 | #endif |
36 | |
37 | #ifdef HAVE_BEOS_THREADS |
38 | #include <OS.h> |
39 | #include <TLS.h> |
40 | #endif |
41 | |
42 | #if defined(SOLARIS) |
43 | #include <note.h> |
44 | #endif |
45 | |
46 | /* #define DEBUG_THREADS */ |
47 | |
48 | #ifdef HAVE_PTHREAD_H |
49 | |
50 | #if defined(__GNUC__) && (__GNUC__ * 100 + __GNUC_MINOR__ >= 303) && \ |
51 | defined(__GLIBC__) && defined(__linux__) |
52 | |
53 | static int libxml_is_threaded = -1; |
54 | |
55 | #define XML_PTHREAD_WEAK |
56 | |
57 | #pragma weak pthread_once |
58 | #pragma weak pthread_getspecific |
59 | #pragma weak pthread_setspecific |
60 | #pragma weak pthread_key_create |
61 | #pragma weak pthread_key_delete |
62 | #pragma weak pthread_mutex_init |
63 | #pragma weak pthread_mutex_destroy |
64 | #pragma weak pthread_mutex_lock |
65 | #pragma weak pthread_mutex_unlock |
66 | #pragma weak pthread_cond_init |
67 | #pragma weak pthread_cond_destroy |
68 | #pragma weak pthread_cond_wait |
69 | #pragma weak pthread_equal |
70 | #pragma weak pthread_self |
71 | #pragma weak pthread_key_create |
72 | #pragma weak pthread_key_delete |
73 | #pragma weak pthread_cond_signal |
74 | |
75 | #else /* __GNUC__, __GLIBC__, __linux__ */ |
76 | |
77 | static int libxml_is_threaded = 1; |
78 | |
79 | #endif /* __GNUC__, __GLIBC__, __linux__ */ |
80 | |
81 | #endif /* HAVE_PTHREAD_H */ |
82 | |
83 | /* |
84 | * TODO: this module still uses malloc/free and not xmlMalloc/xmlFree |
85 | * to avoid some crazyness since xmlMalloc/xmlFree may actually |
86 | * be hosted on allocated blocks needing them for the allocation ... |
87 | */ |
88 | |
89 | /* |
90 | * xmlMutex are a simple mutual exception locks |
91 | */ |
92 | struct _xmlMutex { |
93 | #ifdef HAVE_PTHREAD_H |
94 | pthread_mutex_t lock; |
95 | #elif defined HAVE_WIN32_THREADS |
96 | HANDLE mutex; |
97 | #elif defined HAVE_BEOS_THREADS |
98 | sem_id sem; |
99 | thread_id tid; |
100 | #else |
101 | int empty; |
102 | #endif |
103 | }; |
104 | |
105 | /* |
106 | * xmlRMutex are reentrant mutual exception locks |
107 | */ |
108 | struct _xmlRMutex { |
109 | #ifdef HAVE_PTHREAD_H |
110 | pthread_mutex_t lock; |
111 | unsigned int held; |
112 | unsigned int waiters; |
113 | pthread_t tid; |
114 | pthread_cond_t cv; |
115 | #elif defined HAVE_WIN32_THREADS |
116 | CRITICAL_SECTION cs; |
117 | unsigned int count; |
118 | #elif defined HAVE_BEOS_THREADS |
119 | xmlMutexPtr lock; |
120 | thread_id tid; |
121 | int32 count; |
122 | #else |
123 | int empty; |
124 | #endif |
125 | }; |
126 | |
127 | /* |
128 | * This module still has some internal static data. |
129 | * - xmlLibraryLock a global lock |
130 | * - globalkey used for per-thread data |
131 | */ |
132 | |
133 | #ifdef HAVE_PTHREAD_H |
134 | static pthread_key_t globalkey; |
135 | static pthread_t mainthread; |
136 | static pthread_once_t once_control = PTHREAD_ONCE_INIT; |
137 | static pthread_once_t once_control_init = PTHREAD_ONCE_INIT; |
138 | static pthread_mutex_t global_init_lock = PTHREAD_MUTEX_INITIALIZER; |
139 | #elif defined HAVE_WIN32_THREADS |
140 | #if defined(HAVE_COMPILER_TLS) |
141 | static __declspec(thread) xmlGlobalState tlstate; |
142 | static __declspec(thread) int tlstate_inited = 0; |
143 | #else /* HAVE_COMPILER_TLS */ |
144 | static DWORD globalkey = TLS_OUT_OF_INDEXES; |
145 | #endif /* HAVE_COMPILER_TLS */ |
146 | static DWORD mainthread; |
147 | static struct { |
148 | DWORD done; |
149 | LONG control; |
150 | } run_once = { 0, 0}; |
151 | static volatile LPCRITICAL_SECTION global_init_lock = NULL; |
152 | |
153 | /* endif HAVE_WIN32_THREADS */ |
154 | #elif defined HAVE_BEOS_THREADS |
155 | int32 globalkey = 0; |
156 | thread_id mainthread = 0; |
157 | int32 run_once_init = 0; |
158 | static int32 global_init_lock = -1; |
159 | static vint32 global_init_count = 0; |
160 | #endif |
161 | |
162 | static xmlRMutexPtr xmlLibraryLock = NULL; |
163 | |
164 | #ifdef LIBXML_THREAD_ENABLED |
165 | static void xmlOnceInit(void); |
166 | #endif |
167 | |
168 | /** |
169 | * xmlNewMutex: |
170 | * |
171 | * xmlNewMutex() is used to allocate a libxml2 token struct for use in |
172 | * synchronizing access to data. |
173 | * |
174 | * Returns a new simple mutex pointer or NULL in case of error |
175 | */ |
176 | xmlMutexPtr |
177 | xmlNewMutex(void) |
178 | { |
179 | xmlMutexPtr tok; |
180 | |
181 | if ((tok = malloc(sizeof(xmlMutex))) == NULL) |
182 | return (NULL); |
183 | #ifdef HAVE_PTHREAD_H |
184 | if (libxml_is_threaded != 0) |
185 | pthread_mutex_init(&tok->lock, NULL); |
186 | #elif defined HAVE_WIN32_THREADS |
187 | tok->mutex = CreateMutex(NULL, FALSE, NULL); |
188 | #elif defined HAVE_BEOS_THREADS |
189 | if ((tok->sem = create_sem(1, "xmlMutex" )) < B_OK) { |
190 | free(tok); |
191 | return NULL; |
192 | } |
193 | tok->tid = -1; |
194 | #endif |
195 | return (tok); |
196 | } |
197 | |
198 | /** |
199 | * xmlFreeMutex: |
200 | * @tok: the simple mutex |
201 | * |
202 | * xmlFreeMutex() is used to reclaim resources associated with a libxml2 token |
203 | * struct. |
204 | */ |
205 | void |
206 | xmlFreeMutex(xmlMutexPtr tok) |
207 | { |
208 | if (tok == NULL) |
209 | return; |
210 | |
211 | #ifdef HAVE_PTHREAD_H |
212 | if (libxml_is_threaded != 0) |
213 | pthread_mutex_destroy(&tok->lock); |
214 | #elif defined HAVE_WIN32_THREADS |
215 | CloseHandle(tok->mutex); |
216 | #elif defined HAVE_BEOS_THREADS |
217 | delete_sem(tok->sem); |
218 | #endif |
219 | free(tok); |
220 | } |
221 | |
222 | /** |
223 | * xmlMutexLock: |
224 | * @tok: the simple mutex |
225 | * |
226 | * xmlMutexLock() is used to lock a libxml2 token. |
227 | */ |
228 | void |
229 | xmlMutexLock(xmlMutexPtr tok) |
230 | { |
231 | if (tok == NULL) |
232 | return; |
233 | #ifdef HAVE_PTHREAD_H |
234 | if (libxml_is_threaded != 0) |
235 | pthread_mutex_lock(&tok->lock); |
236 | #elif defined HAVE_WIN32_THREADS |
237 | WaitForSingleObject(tok->mutex, INFINITE); |
238 | #elif defined HAVE_BEOS_THREADS |
239 | if (acquire_sem(tok->sem) != B_NO_ERROR) { |
240 | #ifdef DEBUG_THREADS |
241 | xmlGenericError(xmlGenericErrorContext, |
242 | "xmlMutexLock():BeOS:Couldn't aquire semaphore\n" ); |
243 | #endif |
244 | } |
245 | tok->tid = find_thread(NULL); |
246 | #endif |
247 | |
248 | } |
249 | |
250 | /** |
251 | * xmlMutexUnlock: |
252 | * @tok: the simple mutex |
253 | * |
254 | * xmlMutexUnlock() is used to unlock a libxml2 token. |
255 | */ |
256 | void |
257 | xmlMutexUnlock(xmlMutexPtr tok) |
258 | { |
259 | if (tok == NULL) |
260 | return; |
261 | #ifdef HAVE_PTHREAD_H |
262 | if (libxml_is_threaded != 0) |
263 | pthread_mutex_unlock(&tok->lock); |
264 | #elif defined HAVE_WIN32_THREADS |
265 | ReleaseMutex(tok->mutex); |
266 | #elif defined HAVE_BEOS_THREADS |
267 | if (tok->tid == find_thread(NULL)) { |
268 | tok->tid = -1; |
269 | release_sem(tok->sem); |
270 | } |
271 | #endif |
272 | } |
273 | |
274 | /** |
275 | * xmlNewRMutex: |
276 | * |
277 | * xmlRNewMutex() is used to allocate a reentrant mutex for use in |
278 | * synchronizing access to data. token_r is a re-entrant lock and thus useful |
279 | * for synchronizing access to data structures that may be manipulated in a |
280 | * recursive fashion. |
281 | * |
282 | * Returns the new reentrant mutex pointer or NULL in case of error |
283 | */ |
284 | xmlRMutexPtr |
285 | xmlNewRMutex(void) |
286 | { |
287 | xmlRMutexPtr tok; |
288 | |
289 | if ((tok = malloc(sizeof(xmlRMutex))) == NULL) |
290 | return (NULL); |
291 | #ifdef HAVE_PTHREAD_H |
292 | if (libxml_is_threaded != 0) { |
293 | pthread_mutex_init(&tok->lock, NULL); |
294 | tok->held = 0; |
295 | tok->waiters = 0; |
296 | pthread_cond_init(&tok->cv, NULL); |
297 | } |
298 | #elif defined HAVE_WIN32_THREADS |
299 | InitializeCriticalSection(&tok->cs); |
300 | tok->count = 0; |
301 | #elif defined HAVE_BEOS_THREADS |
302 | if ((tok->lock = xmlNewMutex()) == NULL) { |
303 | free(tok); |
304 | return NULL; |
305 | } |
306 | tok->count = 0; |
307 | #endif |
308 | return (tok); |
309 | } |
310 | |
311 | /** |
312 | * xmlFreeRMutex: |
313 | * @tok: the reentrant mutex |
314 | * |
315 | * xmlRFreeMutex() is used to reclaim resources associated with a |
316 | * reentrant mutex. |
317 | */ |
318 | void |
319 | xmlFreeRMutex(xmlRMutexPtr tok ATTRIBUTE_UNUSED) |
320 | { |
321 | if (tok == NULL) |
322 | return; |
323 | #ifdef HAVE_PTHREAD_H |
324 | if (libxml_is_threaded != 0) { |
325 | pthread_mutex_destroy(&tok->lock); |
326 | pthread_cond_destroy(&tok->cv); |
327 | } |
328 | #elif defined HAVE_WIN32_THREADS |
329 | DeleteCriticalSection(&tok->cs); |
330 | #elif defined HAVE_BEOS_THREADS |
331 | xmlFreeMutex(tok->lock); |
332 | #endif |
333 | free(tok); |
334 | } |
335 | |
336 | /** |
337 | * xmlRMutexLock: |
338 | * @tok: the reentrant mutex |
339 | * |
340 | * xmlRMutexLock() is used to lock a libxml2 token_r. |
341 | */ |
342 | void |
343 | xmlRMutexLock(xmlRMutexPtr tok) |
344 | { |
345 | if (tok == NULL) |
346 | return; |
347 | #ifdef HAVE_PTHREAD_H |
348 | if (libxml_is_threaded == 0) |
349 | return; |
350 | |
351 | pthread_mutex_lock(&tok->lock); |
352 | if (tok->held) { |
353 | if (pthread_equal(tok->tid, pthread_self())) { |
354 | tok->held++; |
355 | pthread_mutex_unlock(&tok->lock); |
356 | return; |
357 | } else { |
358 | tok->waiters++; |
359 | while (tok->held) |
360 | pthread_cond_wait(&tok->cv, &tok->lock); |
361 | tok->waiters--; |
362 | } |
363 | } |
364 | tok->tid = pthread_self(); |
365 | tok->held = 1; |
366 | pthread_mutex_unlock(&tok->lock); |
367 | #elif defined HAVE_WIN32_THREADS |
368 | EnterCriticalSection(&tok->cs); |
369 | tok->count++; |
370 | #elif defined HAVE_BEOS_THREADS |
371 | if (tok->lock->tid == find_thread(NULL)) { |
372 | tok->count++; |
373 | return; |
374 | } else { |
375 | xmlMutexLock(tok->lock); |
376 | tok->count = 1; |
377 | } |
378 | #endif |
379 | } |
380 | |
381 | /** |
382 | * xmlRMutexUnlock: |
383 | * @tok: the reentrant mutex |
384 | * |
385 | * xmlRMutexUnlock() is used to unlock a libxml2 token_r. |
386 | */ |
387 | void |
388 | xmlRMutexUnlock(xmlRMutexPtr tok ATTRIBUTE_UNUSED) |
389 | { |
390 | if (tok == NULL) |
391 | return; |
392 | #ifdef HAVE_PTHREAD_H |
393 | if (libxml_is_threaded == 0) |
394 | return; |
395 | |
396 | pthread_mutex_lock(&tok->lock); |
397 | tok->held--; |
398 | if (tok->held == 0) { |
399 | if (tok->waiters) |
400 | pthread_cond_signal(&tok->cv); |
401 | memset(&tok->tid, 0, sizeof(tok->tid)); |
402 | } |
403 | pthread_mutex_unlock(&tok->lock); |
404 | #elif defined HAVE_WIN32_THREADS |
405 | if (tok->count > 0) { |
406 | tok->count--; |
407 | LeaveCriticalSection(&tok->cs); |
408 | } |
409 | #elif defined HAVE_BEOS_THREADS |
410 | if (tok->lock->tid == find_thread(NULL)) { |
411 | tok->count--; |
412 | if (tok->count == 0) { |
413 | xmlMutexUnlock(tok->lock); |
414 | } |
415 | return; |
416 | } |
417 | #endif |
418 | } |
419 | |
420 | /** |
421 | * xmlGlobalInitMutexLock |
422 | * |
423 | * Makes sure that the global initialization mutex is initialized and |
424 | * locks it. |
425 | */ |
426 | void |
427 | __xmlGlobalInitMutexLock(void) |
428 | { |
429 | /* Make sure the global init lock is initialized and then lock it. */ |
430 | #ifdef HAVE_PTHREAD_H |
431 | /* The mutex is statically initialized, so we just lock it. */ |
432 | #ifdef XML_PTHREAD_WEAK |
433 | if (pthread_mutex_lock == NULL) |
434 | return; |
435 | #endif /* XML_PTHREAD_WEAK */ |
436 | pthread_mutex_lock(&global_init_lock); |
437 | #elif defined HAVE_WIN32_THREADS |
438 | LPCRITICAL_SECTION cs; |
439 | |
440 | /* Create a new critical section */ |
441 | if (global_init_lock == NULL) { |
442 | cs = malloc(sizeof(CRITICAL_SECTION)); |
443 | if (cs == NULL) { |
444 | xmlGenericError(xmlGenericErrorContext, |
445 | "xmlGlobalInitMutexLock: out of memory\n" ); |
446 | return; |
447 | } |
448 | InitializeCriticalSection(cs); |
449 | |
450 | /* Swap it into the global_init_lock */ |
451 | #ifdef InterlockedCompareExchangePointer |
452 | InterlockedCompareExchangePointer((void **) &global_init_lock, |
453 | cs, NULL); |
454 | #else /* Use older void* version */ |
455 | InterlockedCompareExchange((void **) &global_init_lock, |
456 | (void *) cs, NULL); |
457 | #endif /* InterlockedCompareExchangePointer */ |
458 | |
459 | /* If another thread successfully recorded its critical |
460 | * section in the global_init_lock then discard the one |
461 | * allocated by this thread. */ |
462 | if (global_init_lock != cs) { |
463 | DeleteCriticalSection(cs); |
464 | free(cs); |
465 | } |
466 | } |
467 | |
468 | /* Lock the chosen critical section */ |
469 | EnterCriticalSection(global_init_lock); |
470 | #elif defined HAVE_BEOS_THREADS |
471 | int32 sem; |
472 | |
473 | /* Allocate a new semaphore */ |
474 | sem = create_sem(1, "xmlGlobalinitMutex" ); |
475 | |
476 | while (global_init_lock == -1) { |
477 | if (atomic_add(&global_init_count, 1) == 0) { |
478 | global_init_lock = sem; |
479 | } else { |
480 | snooze(1); |
481 | atomic_add(&global_init_count, -1); |
482 | } |
483 | } |
484 | |
485 | /* If another thread successfully recorded its critical |
486 | * section in the global_init_lock then discard the one |
487 | * allocated by this thread. */ |
488 | if (global_init_lock != sem) |
489 | delete_sem(sem); |
490 | |
491 | /* Acquire the chosen semaphore */ |
492 | if (acquire_sem(global_init_lock) != B_NO_ERROR) { |
493 | #ifdef DEBUG_THREADS |
494 | xmlGenericError(xmlGenericErrorContext, |
495 | "xmlGlobalInitMutexLock():BeOS:Couldn't acquire semaphore\n" ); |
496 | #endif |
497 | } |
498 | #endif |
499 | } |
500 | |
501 | void |
502 | __xmlGlobalInitMutexUnlock(void) |
503 | { |
504 | #ifdef HAVE_PTHREAD_H |
505 | #ifdef XML_PTHREAD_WEAK |
506 | if (pthread_mutex_unlock == NULL) |
507 | return; |
508 | #endif /* XML_PTHREAD_WEAK */ |
509 | pthread_mutex_unlock(&global_init_lock); |
510 | #elif defined HAVE_WIN32_THREADS |
511 | if (global_init_lock != NULL) { |
512 | LeaveCriticalSection(global_init_lock); |
513 | } |
514 | #elif defined HAVE_BEOS_THREADS |
515 | release_sem(global_init_lock); |
516 | #endif |
517 | } |
518 | |
519 | /** |
520 | * xmlGlobalInitMutexDestroy |
521 | * |
522 | * Makes sure that the global initialization mutex is destroyed before |
523 | * application termination. |
524 | */ |
525 | void |
526 | __xmlGlobalInitMutexDestroy(void) |
527 | { |
528 | #ifdef HAVE_PTHREAD_H |
529 | #elif defined HAVE_WIN32_THREADS |
530 | if (global_init_lock != NULL) { |
531 | DeleteCriticalSection(global_init_lock); |
532 | free(global_init_lock); |
533 | global_init_lock = NULL; |
534 | } |
535 | #endif |
536 | } |
537 | |
538 | /************************************************************************ |
539 | * * |
540 | * Per thread global state handling * |
541 | * * |
542 | ************************************************************************/ |
543 | |
544 | #ifdef LIBXML_THREAD_ENABLED |
545 | #ifdef xmlLastError |
546 | #undef xmlLastError |
547 | #endif |
548 | |
549 | /** |
550 | * xmlFreeGlobalState: |
551 | * @state: a thread global state |
552 | * |
553 | * xmlFreeGlobalState() is called when a thread terminates with a non-NULL |
554 | * global state. It is is used here to reclaim memory resources. |
555 | */ |
556 | static void |
557 | xmlFreeGlobalState(void *state) |
558 | { |
559 | xmlGlobalState *gs = (xmlGlobalState *) state; |
560 | |
561 | /* free any memory allocated in the thread's xmlLastError */ |
562 | xmlResetError(&(gs->xmlLastError)); |
563 | free(state); |
564 | } |
565 | |
566 | /** |
567 | * xmlNewGlobalState: |
568 | * |
569 | * xmlNewGlobalState() allocates a global state. This structure is used to |
570 | * hold all data for use by a thread when supporting backwards compatibility |
571 | * of libxml2 to pre-thread-safe behaviour. |
572 | * |
573 | * Returns the newly allocated xmlGlobalStatePtr or NULL in case of error |
574 | */ |
575 | static xmlGlobalStatePtr |
576 | xmlNewGlobalState(void) |
577 | { |
578 | xmlGlobalState *gs; |
579 | |
580 | gs = malloc(sizeof(xmlGlobalState)); |
581 | if (gs == NULL) { |
582 | xmlGenericError(xmlGenericErrorContext, |
583 | "xmlGetGlobalState: out of memory\n" ); |
584 | return (NULL); |
585 | } |
586 | |
587 | memset(gs, 0, sizeof(xmlGlobalState)); |
588 | xmlInitializeGlobalState(gs); |
589 | return (gs); |
590 | } |
591 | #endif /* LIBXML_THREAD_ENABLED */ |
592 | |
593 | #ifdef HAVE_PTHREAD_H |
594 | #elif defined HAVE_WIN32_THREADS |
595 | #if !defined(HAVE_COMPILER_TLS) |
596 | #if defined(LIBXML_STATIC) && !defined(LIBXML_STATIC_FOR_DLL) |
597 | typedef struct _xmlGlobalStateCleanupHelperParams { |
598 | HANDLE thread; |
599 | void *memory; |
600 | } xmlGlobalStateCleanupHelperParams; |
601 | |
602 | static void XMLCDECL |
603 | xmlGlobalStateCleanupHelper(void *p) |
604 | { |
605 | xmlGlobalStateCleanupHelperParams *params = |
606 | (xmlGlobalStateCleanupHelperParams *) p; |
607 | WaitForSingleObject(params->thread, INFINITE); |
608 | CloseHandle(params->thread); |
609 | xmlFreeGlobalState(params->memory); |
610 | free(params); |
611 | _endthread(); |
612 | } |
613 | #else /* LIBXML_STATIC && !LIBXML_STATIC_FOR_DLL */ |
614 | |
615 | typedef struct _xmlGlobalStateCleanupHelperParams { |
616 | void *memory; |
617 | struct _xmlGlobalStateCleanupHelperParams *prev; |
618 | struct _xmlGlobalStateCleanupHelperParams *next; |
619 | } xmlGlobalStateCleanupHelperParams; |
620 | |
621 | static xmlGlobalStateCleanupHelperParams *cleanup_helpers_head = NULL; |
622 | static CRITICAL_SECTION cleanup_helpers_cs; |
623 | |
624 | #endif /* LIBXMLSTATIC && !LIBXML_STATIC_FOR_DLL */ |
625 | #endif /* HAVE_COMPILER_TLS */ |
626 | #endif /* HAVE_WIN32_THREADS */ |
627 | |
628 | #if defined HAVE_BEOS_THREADS |
629 | |
630 | /** |
631 | * xmlGlobalStateCleanup: |
632 | * @data: unused parameter |
633 | * |
634 | * Used for Beos only |
635 | */ |
636 | void |
637 | xmlGlobalStateCleanup(void *data) |
638 | { |
639 | void *globalval = tls_get(globalkey); |
640 | |
641 | if (globalval != NULL) |
642 | xmlFreeGlobalState(globalval); |
643 | } |
644 | #endif |
645 | |
646 | /** |
647 | * xmlGetGlobalState: |
648 | * |
649 | * xmlGetGlobalState() is called to retrieve the global state for a thread. |
650 | * |
651 | * Returns the thread global state or NULL in case of error |
652 | */ |
653 | xmlGlobalStatePtr |
654 | xmlGetGlobalState(void) |
655 | { |
656 | #ifdef HAVE_PTHREAD_H |
657 | xmlGlobalState *globalval; |
658 | |
659 | if (libxml_is_threaded == 0) |
660 | return (NULL); |
661 | |
662 | pthread_once(&once_control, xmlOnceInit); |
663 | |
664 | if ((globalval = (xmlGlobalState *) |
665 | pthread_getspecific(globalkey)) == NULL) { |
666 | xmlGlobalState *tsd = xmlNewGlobalState(); |
667 | if (tsd == NULL) |
668 | return(NULL); |
669 | |
670 | pthread_setspecific(globalkey, tsd); |
671 | return (tsd); |
672 | } |
673 | return (globalval); |
674 | #elif defined HAVE_WIN32_THREADS |
675 | #if defined(HAVE_COMPILER_TLS) |
676 | if (!tlstate_inited) { |
677 | tlstate_inited = 1; |
678 | xmlInitializeGlobalState(&tlstate); |
679 | } |
680 | return &tlstate; |
681 | #else /* HAVE_COMPILER_TLS */ |
682 | xmlGlobalState *globalval; |
683 | xmlGlobalStateCleanupHelperParams *p; |
684 | |
685 | xmlOnceInit(); |
686 | #if defined(LIBXML_STATIC) && !defined(LIBXML_STATIC_FOR_DLL) |
687 | globalval = (xmlGlobalState *) TlsGetValue(globalkey); |
688 | #else |
689 | p = (xmlGlobalStateCleanupHelperParams *) TlsGetValue(globalkey); |
690 | globalval = (xmlGlobalState *) (p ? p->memory : NULL); |
691 | #endif |
692 | if (globalval == NULL) { |
693 | xmlGlobalState *tsd = xmlNewGlobalState(); |
694 | |
695 | if (tsd == NULL) |
696 | return(NULL); |
697 | p = (xmlGlobalStateCleanupHelperParams *) |
698 | malloc(sizeof(xmlGlobalStateCleanupHelperParams)); |
699 | if (p == NULL) { |
700 | xmlGenericError(xmlGenericErrorContext, |
701 | "xmlGetGlobalState: out of memory\n" ); |
702 | xmlFreeGlobalState(tsd); |
703 | return(NULL); |
704 | } |
705 | p->memory = tsd; |
706 | #if defined(LIBXML_STATIC) && !defined(LIBXML_STATIC_FOR_DLL) |
707 | DuplicateHandle(GetCurrentProcess(), GetCurrentThread(), |
708 | GetCurrentProcess(), &p->thread, 0, TRUE, |
709 | DUPLICATE_SAME_ACCESS); |
710 | TlsSetValue(globalkey, tsd); |
711 | _beginthread(xmlGlobalStateCleanupHelper, 0, p); |
712 | #else |
713 | EnterCriticalSection(&cleanup_helpers_cs); |
714 | if (cleanup_helpers_head != NULL) { |
715 | cleanup_helpers_head->prev = p; |
716 | } |
717 | p->next = cleanup_helpers_head; |
718 | p->prev = NULL; |
719 | cleanup_helpers_head = p; |
720 | TlsSetValue(globalkey, p); |
721 | LeaveCriticalSection(&cleanup_helpers_cs); |
722 | #endif |
723 | |
724 | return (tsd); |
725 | } |
726 | return (globalval); |
727 | #endif /* HAVE_COMPILER_TLS */ |
728 | #elif defined HAVE_BEOS_THREADS |
729 | xmlGlobalState *globalval; |
730 | |
731 | xmlOnceInit(); |
732 | |
733 | if ((globalval = (xmlGlobalState *) tls_get(globalkey)) == NULL) { |
734 | xmlGlobalState *tsd = xmlNewGlobalState(); |
735 | if (tsd == NULL) |
736 | return (NULL); |
737 | |
738 | tls_set(globalkey, tsd); |
739 | on_exit_thread(xmlGlobalStateCleanup, NULL); |
740 | return (tsd); |
741 | } |
742 | return (globalval); |
743 | #else |
744 | return (NULL); |
745 | #endif |
746 | } |
747 | |
748 | /************************************************************************ |
749 | * * |
750 | * Library wide thread interfaces * |
751 | * * |
752 | ************************************************************************/ |
753 | |
754 | /** |
755 | * xmlGetThreadId: |
756 | * |
757 | * xmlGetThreadId() find the current thread ID number |
758 | * Note that this is likely to be broken on some platforms using pthreads |
759 | * as the specification doesn't mandate pthread_t to be an integer type |
760 | * |
761 | * Returns the current thread ID number |
762 | */ |
763 | int |
764 | xmlGetThreadId(void) |
765 | { |
766 | #ifdef HAVE_PTHREAD_H |
767 | pthread_t id; |
768 | int ret; |
769 | |
770 | if (libxml_is_threaded == 0) |
771 | return (0); |
772 | id = pthread_self(); |
773 | /* horrible but preserves compat, see warning above */ |
774 | memcpy(&ret, &id, sizeof(ret)); |
775 | return (ret); |
776 | #elif defined HAVE_WIN32_THREADS |
777 | return GetCurrentThreadId(); |
778 | #elif defined HAVE_BEOS_THREADS |
779 | return find_thread(NULL); |
780 | #else |
781 | return ((int) 0); |
782 | #endif |
783 | } |
784 | |
785 | /** |
786 | * xmlIsMainThread: |
787 | * |
788 | * xmlIsMainThread() check whether the current thread is the main thread. |
789 | * |
790 | * Returns 1 if the current thread is the main thread, 0 otherwise |
791 | */ |
792 | int |
793 | xmlIsMainThread(void) |
794 | { |
795 | #ifdef HAVE_PTHREAD_H |
796 | if (libxml_is_threaded == -1) |
797 | xmlInitThreads(); |
798 | if (libxml_is_threaded == 0) |
799 | return (1); |
800 | pthread_once(&once_control, xmlOnceInit); |
801 | #elif defined HAVE_WIN32_THREADS |
802 | xmlOnceInit(); |
803 | #elif defined HAVE_BEOS_THREADS |
804 | xmlOnceInit(); |
805 | #endif |
806 | |
807 | #ifdef DEBUG_THREADS |
808 | xmlGenericError(xmlGenericErrorContext, "xmlIsMainThread()\n" ); |
809 | #endif |
810 | #ifdef HAVE_PTHREAD_H |
811 | return (pthread_equal(mainthread,pthread_self())); |
812 | #elif defined HAVE_WIN32_THREADS |
813 | return (mainthread == GetCurrentThreadId()); |
814 | #elif defined HAVE_BEOS_THREADS |
815 | return (mainthread == find_thread(NULL)); |
816 | #else |
817 | return (1); |
818 | #endif |
819 | } |
820 | |
821 | /** |
822 | * xmlLockLibrary: |
823 | * |
824 | * xmlLockLibrary() is used to take out a re-entrant lock on the libxml2 |
825 | * library. |
826 | */ |
827 | void |
828 | xmlLockLibrary(void) |
829 | { |
830 | #ifdef DEBUG_THREADS |
831 | xmlGenericError(xmlGenericErrorContext, "xmlLockLibrary()\n" ); |
832 | #endif |
833 | xmlRMutexLock(xmlLibraryLock); |
834 | } |
835 | |
836 | /** |
837 | * xmlUnlockLibrary: |
838 | * |
839 | * xmlUnlockLibrary() is used to release a re-entrant lock on the libxml2 |
840 | * library. |
841 | */ |
842 | void |
843 | xmlUnlockLibrary(void) |
844 | { |
845 | #ifdef DEBUG_THREADS |
846 | xmlGenericError(xmlGenericErrorContext, "xmlUnlockLibrary()\n" ); |
847 | #endif |
848 | xmlRMutexUnlock(xmlLibraryLock); |
849 | } |
850 | |
851 | /** |
852 | * xmlInitThreads: |
853 | * |
854 | * xmlInitThreads() is used to to initialize all the thread related |
855 | * data of the libxml2 library. |
856 | */ |
857 | void |
858 | xmlInitThreads(void) |
859 | { |
860 | #ifdef HAVE_PTHREAD_H |
861 | #ifdef XML_PTHREAD_WEAK |
862 | if (libxml_is_threaded == -1) { |
863 | if ((pthread_once != NULL) && |
864 | (pthread_getspecific != NULL) && |
865 | (pthread_setspecific != NULL) && |
866 | (pthread_key_create != NULL) && |
867 | (pthread_key_delete != NULL) && |
868 | (pthread_mutex_init != NULL) && |
869 | (pthread_mutex_destroy != NULL) && |
870 | (pthread_mutex_lock != NULL) && |
871 | (pthread_mutex_unlock != NULL) && |
872 | (pthread_cond_init != NULL) && |
873 | (pthread_cond_destroy != NULL) && |
874 | (pthread_cond_wait != NULL) && |
875 | (pthread_equal != NULL) && |
876 | (pthread_self != NULL) && |
877 | (pthread_cond_signal != NULL)) { |
878 | libxml_is_threaded = 1; |
879 | |
880 | /* fprintf(stderr, "Running multithreaded\n"); */ |
881 | } else { |
882 | |
883 | /* fprintf(stderr, "Running without multithread\n"); */ |
884 | libxml_is_threaded = 0; |
885 | } |
886 | } |
887 | #endif /* XML_PTHREAD_WEAK */ |
888 | #elif defined(HAVE_WIN32_THREADS) && !defined(HAVE_COMPILER_TLS) && (!defined(LIBXML_STATIC) || defined(LIBXML_STATIC_FOR_DLL)) |
889 | InitializeCriticalSection(&cleanup_helpers_cs); |
890 | #endif |
891 | } |
892 | |
893 | /** |
894 | * xmlCleanupThreads: |
895 | * |
896 | * xmlCleanupThreads() is used to to cleanup all the thread related |
897 | * data of the libxml2 library once processing has ended. |
898 | * |
899 | * WARNING: if your application is multithreaded or has plugin support |
900 | * calling this may crash the application if another thread or |
901 | * a plugin is still using libxml2. It's sometimes very hard to |
902 | * guess if libxml2 is in use in the application, some libraries |
903 | * or plugins may use it without notice. In case of doubt abstain |
904 | * from calling this function or do it just before calling exit() |
905 | * to avoid leak reports from valgrind ! |
906 | */ |
907 | void |
908 | xmlCleanupThreads(void) |
909 | { |
910 | #ifdef DEBUG_THREADS |
911 | xmlGenericError(xmlGenericErrorContext, "xmlCleanupThreads()\n" ); |
912 | #endif |
913 | #ifdef HAVE_PTHREAD_H |
914 | if (libxml_is_threaded != 0) |
915 | pthread_key_delete(globalkey); |
916 | once_control = once_control_init; |
917 | #elif defined(HAVE_WIN32_THREADS) && !defined(HAVE_COMPILER_TLS) && (!defined(LIBXML_STATIC) || defined(LIBXML_STATIC_FOR_DLL)) |
918 | if (globalkey != TLS_OUT_OF_INDEXES) { |
919 | xmlGlobalStateCleanupHelperParams *p; |
920 | |
921 | EnterCriticalSection(&cleanup_helpers_cs); |
922 | p = cleanup_helpers_head; |
923 | while (p != NULL) { |
924 | xmlGlobalStateCleanupHelperParams *temp = p; |
925 | |
926 | p = p->next; |
927 | xmlFreeGlobalState(temp->memory); |
928 | free(temp); |
929 | } |
930 | cleanup_helpers_head = 0; |
931 | LeaveCriticalSection(&cleanup_helpers_cs); |
932 | TlsFree(globalkey); |
933 | globalkey = TLS_OUT_OF_INDEXES; |
934 | } |
935 | DeleteCriticalSection(&cleanup_helpers_cs); |
936 | #endif |
937 | } |
938 | |
939 | #ifdef LIBXML_THREAD_ENABLED |
940 | |
941 | /** |
942 | * xmlOnceInit |
943 | * |
944 | * xmlOnceInit() is used to initialize the value of mainthread for use |
945 | * in other routines. This function should only be called using |
946 | * pthread_once() in association with the once_control variable to ensure |
947 | * that the function is only called once. See man pthread_once for more |
948 | * details. |
949 | */ |
950 | static void |
951 | xmlOnceInit(void) |
952 | { |
953 | #ifdef HAVE_PTHREAD_H |
954 | (void) pthread_key_create(&globalkey, xmlFreeGlobalState); |
955 | mainthread = pthread_self(); |
956 | __xmlInitializeDict(); |
957 | #elif defined(HAVE_WIN32_THREADS) |
958 | if (!run_once.done) { |
959 | if (InterlockedIncrement(&run_once.control) == 1) { |
960 | #if !defined(HAVE_COMPILER_TLS) |
961 | globalkey = TlsAlloc(); |
962 | #endif |
963 | mainthread = GetCurrentThreadId(); |
964 | __xmlInitializeDict(); |
965 | run_once.done = 1; |
966 | } else { |
967 | /* Another thread is working; give up our slice and |
968 | * wait until they're done. */ |
969 | while (!run_once.done) |
970 | Sleep(0); |
971 | } |
972 | } |
973 | #elif defined HAVE_BEOS_THREADS |
974 | if (atomic_add(&run_once_init, 1) == 0) { |
975 | globalkey = tls_allocate(); |
976 | tls_set(globalkey, NULL); |
977 | mainthread = find_thread(NULL); |
978 | __xmlInitializeDict(); |
979 | } else |
980 | atomic_add(&run_once_init, -1); |
981 | #endif |
982 | } |
983 | #endif |
984 | |
985 | /** |
986 | * DllMain: |
987 | * @hinstDLL: handle to DLL instance |
988 | * @fdwReason: Reason code for entry |
989 | * @lpvReserved: generic pointer (depends upon reason code) |
990 | * |
991 | * Entry point for Windows library. It is being used to free thread-specific |
992 | * storage. |
993 | * |
994 | * Returns TRUE always |
995 | */ |
996 | #ifdef HAVE_PTHREAD_H |
997 | #elif defined(HAVE_WIN32_THREADS) && !defined(HAVE_COMPILER_TLS) && (!defined(LIBXML_STATIC) || defined(LIBXML_STATIC_FOR_DLL)) |
998 | #if defined(LIBXML_STATIC_FOR_DLL) |
999 | int XMLCALL |
1000 | xmlDllMain(ATTRIBUTE_UNUSED void *hinstDLL, unsigned long fdwReason, |
1001 | ATTRIBUTE_UNUSED void *lpvReserved) |
1002 | #else |
1003 | /* declare to avoid "no previous prototype for 'DllMain'" warning */ |
1004 | /* Note that we do NOT want to include this function declaration in |
1005 | a public header because it's meant to be called by Windows itself, |
1006 | not a program that uses this library. This also has to be exported. */ |
1007 | |
1008 | XMLPUBFUN BOOL WINAPI |
1009 | DllMain (HINSTANCE hinstDLL, |
1010 | DWORD fdwReason, |
1011 | LPVOID lpvReserved); |
1012 | |
1013 | BOOL WINAPI |
1014 | DllMain(ATTRIBUTE_UNUSED HINSTANCE hinstDLL, DWORD fdwReason, |
1015 | ATTRIBUTE_UNUSED LPVOID lpvReserved) |
1016 | #endif |
1017 | { |
1018 | switch (fdwReason) { |
1019 | case DLL_THREAD_DETACH: |
1020 | if (globalkey != TLS_OUT_OF_INDEXES) { |
1021 | xmlGlobalState *globalval = NULL; |
1022 | xmlGlobalStateCleanupHelperParams *p = |
1023 | (xmlGlobalStateCleanupHelperParams *) |
1024 | TlsGetValue(globalkey); |
1025 | globalval = (xmlGlobalState *) (p ? p->memory : NULL); |
1026 | if (globalval) { |
1027 | xmlFreeGlobalState(globalval); |
1028 | TlsSetValue(globalkey, NULL); |
1029 | } |
1030 | if (p) { |
1031 | EnterCriticalSection(&cleanup_helpers_cs); |
1032 | if (p == cleanup_helpers_head) |
1033 | cleanup_helpers_head = p->next; |
1034 | else |
1035 | p->prev->next = p->next; |
1036 | if (p->next != NULL) |
1037 | p->next->prev = p->prev; |
1038 | LeaveCriticalSection(&cleanup_helpers_cs); |
1039 | free(p); |
1040 | } |
1041 | } |
1042 | break; |
1043 | } |
1044 | return TRUE; |
1045 | } |
1046 | #endif |
1047 | #define bottom_threads |
1048 | #include "elfgcchack.h" |
1049 | |