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 #include <stdlib.h>
15
16 #include <libxml/threads.h>
17 #include <libxml/parser.h>
18 #ifdef LIBXML_CATALOG_ENABLED
19 #include <libxml/catalog.h>
20 #endif
21 #ifdef LIBXML_SCHEMAS_ENABLED
22 #include <libxml/xmlschemastypes.h>
23 #include <libxml/relaxng.h>
24 #endif
25
26 #if defined(SOLARIS)
27 #include <note.h>
28 #endif
29
30 #include "private/dict.h"
31 #include "private/enc.h"
32 #include "private/globals.h"
33 #include "private/memory.h"
34 #include "private/threads.h"
35 #include "private/xpath.h"
36
37 #if defined(HAVE_POSIX_THREADS) && \
38 defined(__GLIBC__) && \
39 __GLIBC__ * 100 + __GLIBC_MINOR__ >= 234
40
41 /*
42 * The modern way available since glibc 2.32.
43 *
44 * The check above is for glibc 2.34 which merged the pthread symbols into
45 * libc. Since we still allow linking without pthread symbols (see below),
46 * this only works if pthread symbols are guaranteed to be available.
47 */
48
49 #include <sys/single_threaded.h>
50
51 #define XML_IS_THREADED() (!__libc_single_threaded)
52 #define XML_IS_NEVER_THREADED() 0
53
54 #elif defined(HAVE_POSIX_THREADS) && \
55 defined(__GLIBC__) && \
56 defined(__GNUC__)
57
58 /*
59 * The traditional way to check for single-threaded applications with
60 * glibc was to check whether the separate libpthread library is
61 * linked in. This works by not linking libxml2 with libpthread (see
62 * BASE_THREAD_LIBS in configure.ac and Makefile.am) and declaring
63 * pthread functions as weak symbols.
64 *
65 * In glibc 2.34, the pthread symbols were moved from libpthread to libc,
66 * so this doesn't work anymore.
67 *
68 * At some point, this legacy code and the BASE_THREAD_LIBS hack in
69 * configure.ac can probably be removed.
70 */
71
72 #pragma weak pthread_mutex_init
73 #pragma weak pthread_mutex_destroy
74 #pragma weak pthread_mutex_lock
75 #pragma weak pthread_mutex_unlock
76 #pragma weak pthread_cond_init
77 #pragma weak pthread_cond_destroy
78 #pragma weak pthread_cond_wait
79 #pragma weak pthread_equal
80 #pragma weak pthread_self
81 #pragma weak pthread_cond_signal
82
83 #define XML_PTHREAD_WEAK
84 #define XML_IS_THREADED() libxml_is_threaded
85 #define XML_IS_NEVER_THREADED() (!libxml_is_threaded)
86
87 static int libxml_is_threaded = -1;
88
89 #else /* other POSIX platforms */
90
91 #define XML_IS_THREADED() 1
92 #define XML_IS_NEVER_THREADED() 0
93
94 #endif
95
96 /*
97 * TODO: this module still uses malloc/free and not xmlMalloc/xmlFree
98 * to avoid some craziness since xmlMalloc/xmlFree may actually
99 * be hosted on allocated blocks needing them for the allocation ...
100 */
101
102 /*
103 * xmlRMutex are reentrant mutual exception locks
104 */
105 struct _xmlRMutex {
106 #ifdef HAVE_POSIX_THREADS
107 pthread_mutex_t lock;
108 unsigned int held;
109 unsigned int waiters;
110 pthread_t tid;
111 pthread_cond_t cv;
112 #elif defined HAVE_WIN32_THREADS
113 CRITICAL_SECTION cs;
114 #else
115 int empty;
116 #endif
117 };
118
119 static xmlRMutexPtr xmlLibraryLock = NULL;
120
121 /**
122 * xmlInitMutex:
123 * @mutex: the mutex
124 *
125 * Initialize a mutex.
126 */
127 void
128 xmlInitMutex(xmlMutexPtr mutex)
129 {
130 #ifdef HAVE_POSIX_THREADS
131 if (XML_IS_NEVER_THREADED() == 0)
132 pthread_mutex_init(&mutex->lock, NULL);
133 #elif defined HAVE_WIN32_THREADS
134 InitializeCriticalSection(&mutex->cs);
135 #else
136 (void) mutex;
137 #endif
138 }
139
140 /**
141 * xmlNewMutex:
142 *
143 * xmlNewMutex() is used to allocate a libxml2 token struct for use in
144 * synchronizing access to data.
145 *
146 * Returns a new simple mutex pointer or NULL in case of error
147 */
148 xmlMutexPtr
149 xmlNewMutex(void)
150 {
151 xmlMutexPtr tok;
152
153 if ((tok = malloc(sizeof(xmlMutex))) == NULL)
154 return (NULL);
155 xmlInitMutex(tok);
156 return (tok);
157 }
158
159 /**
160 * xmlCleanupMutex:
161 * @mutex: the simple mutex
162 *
163 * Reclaim resources associated with a mutex.
164 */
165 void
166 xmlCleanupMutex(xmlMutexPtr mutex)
167 {
168 #ifdef HAVE_POSIX_THREADS
169 if (XML_IS_NEVER_THREADED() == 0)
170 pthread_mutex_destroy(&mutex->lock);
171 #elif defined HAVE_WIN32_THREADS
172 DeleteCriticalSection(&mutex->cs);
173 #else
174 (void) mutex;
175 #endif
176 }
177
178 /**
179 * xmlFreeMutex:
180 * @tok: the simple mutex
181 *
182 * Free a mutex.
183 */
184 void
185 xmlFreeMutex(xmlMutexPtr tok)
186 {
187 if (tok == NULL)
188 return;
189
190 xmlCleanupMutex(tok);
191 free(tok);
192 }
193
194 /**
195 * xmlMutexLock:
196 * @tok: the simple mutex
197 *
198 * xmlMutexLock() is used to lock a libxml2 token.
199 */
200 void
201 xmlMutexLock(xmlMutexPtr tok)
202 {
203 if (tok == NULL)
204 return;
205 #ifdef HAVE_POSIX_THREADS
206 /*
207 * This assumes that __libc_single_threaded won't change while the
208 * lock is held.
209 */
210 if (XML_IS_THREADED() != 0)
211 pthread_mutex_lock(&tok->lock);
212 #elif defined HAVE_WIN32_THREADS
213 EnterCriticalSection(&tok->cs);
214 #endif
215
216 }
217
218 /**
219 * xmlMutexUnlock:
220 * @tok: the simple mutex
221 *
222 * xmlMutexUnlock() is used to unlock a libxml2 token.
223 */
224 void
225 xmlMutexUnlock(xmlMutexPtr tok)
226 {
227 if (tok == NULL)
228 return;
229 #ifdef HAVE_POSIX_THREADS
230 if (XML_IS_THREADED() != 0)
231 pthread_mutex_unlock(&tok->lock);
232 #elif defined HAVE_WIN32_THREADS
233 LeaveCriticalSection(&tok->cs);
234 #endif
235 }
236
237 /**
238 * xmlNewRMutex:
239 *
240 * xmlRNewMutex() is used to allocate a reentrant mutex for use in
241 * synchronizing access to data. token_r is a re-entrant lock and thus useful
242 * for synchronizing access to data structures that may be manipulated in a
243 * recursive fashion.
244 *
245 * Returns the new reentrant mutex pointer or NULL in case of error
246 */
247 xmlRMutexPtr
248 xmlNewRMutex(void)
249 {
250 xmlRMutexPtr tok;
251
252 if ((tok = malloc(sizeof(xmlRMutex))) == NULL)
253 return (NULL);
254 #ifdef HAVE_POSIX_THREADS
255 if (XML_IS_NEVER_THREADED() == 0) {
256 pthread_mutex_init(&tok->lock, NULL);
257 tok->held = 0;
258 tok->waiters = 0;
259 pthread_cond_init(&tok->cv, NULL);
260 }
261 #elif defined HAVE_WIN32_THREADS
262 InitializeCriticalSection(&tok->cs);
263 #endif
264 return (tok);
265 }
266
267 /**
268 * xmlFreeRMutex:
269 * @tok: the reentrant mutex
270 *
271 * xmlRFreeMutex() is used to reclaim resources associated with a
272 * reentrant mutex.
273 */
274 void
275 xmlFreeRMutex(xmlRMutexPtr tok ATTRIBUTE_UNUSED)
276 {
277 if (tok == NULL)
278 return;
279 #ifdef HAVE_POSIX_THREADS
280 if (XML_IS_NEVER_THREADED() == 0) {
281 pthread_mutex_destroy(&tok->lock);
282 pthread_cond_destroy(&tok->cv);
283 }
284 #elif defined HAVE_WIN32_THREADS
285 DeleteCriticalSection(&tok->cs);
286 #endif
287 free(tok);
288 }
289
290 /**
291 * xmlRMutexLock:
292 * @tok: the reentrant mutex
293 *
294 * xmlRMutexLock() is used to lock a libxml2 token_r.
295 */
296 void
297 xmlRMutexLock(xmlRMutexPtr tok)
298 {
299 if (tok == NULL)
300 return;
301 #ifdef HAVE_POSIX_THREADS
302 if (XML_IS_THREADED() == 0)
303 return;
304
305 pthread_mutex_lock(&tok->lock);
306 if (tok->held) {
307 if (pthread_equal(tok->tid, pthread_self())) {
308 tok->held++;
309 pthread_mutex_unlock(&tok->lock);
310 return;
311 } else {
312 tok->waiters++;
313 while (tok->held)
314 pthread_cond_wait(&tok->cv, &tok->lock);
315 tok->waiters--;
316 }
317 }
318 tok->tid = pthread_self();
319 tok->held = 1;
320 pthread_mutex_unlock(&tok->lock);
321 #elif defined HAVE_WIN32_THREADS
322 EnterCriticalSection(&tok->cs);
323 #endif
324 }
325
326 /**
327 * xmlRMutexUnlock:
328 * @tok: the reentrant mutex
329 *
330 * xmlRMutexUnlock() is used to unlock a libxml2 token_r.
331 */
332 void
333 xmlRMutexUnlock(xmlRMutexPtr tok ATTRIBUTE_UNUSED)
334 {
335 if (tok == NULL)
336 return;
337 #ifdef HAVE_POSIX_THREADS
338 if (XML_IS_THREADED() == 0)
339 return;
340
341 pthread_mutex_lock(&tok->lock);
342 tok->held--;
343 if (tok->held == 0) {
344 if (tok->waiters)
345 pthread_cond_signal(&tok->cv);
346 memset(&tok->tid, 0, sizeof(tok->tid));
347 }
348 pthread_mutex_unlock(&tok->lock);
349 #elif defined HAVE_WIN32_THREADS
350 LeaveCriticalSection(&tok->cs);
351 #endif
352 }
353
354 /************************************************************************
355 * *
356 * Library wide thread interfaces *
357 * *
358 ************************************************************************/
359
360 /**
361 * xmlGetThreadId:
362 *
363 * DEPRECATED: Internal function, do not use.
364 *
365 * xmlGetThreadId() find the current thread ID number
366 * Note that this is likely to be broken on some platforms using pthreads
367 * as the specification doesn't mandate pthread_t to be an integer type
368 *
369 * Returns the current thread ID number
370 */
371 int
372 xmlGetThreadId(void)
373 {
374 #ifdef HAVE_POSIX_THREADS
375 pthread_t id;
376 int ret;
377
378 if (XML_IS_THREADED() == 0)
379 return (0);
380 id = pthread_self();
381 /* horrible but preserves compat, see warning above */
382 memcpy(&ret, &id, sizeof(ret));
383 return (ret);
384 #elif defined HAVE_WIN32_THREADS
385 return GetCurrentThreadId();
386 #else
387 return ((int) 0);
388 #endif
389 }
390
391 /**
392 * xmlLockLibrary:
393 *
394 * xmlLockLibrary() is used to take out a re-entrant lock on the libxml2
395 * library.
396 */
397 void
398 xmlLockLibrary(void)
399 {
400 xmlRMutexLock(xmlLibraryLock);
401 }
402
403 /**
404 * xmlUnlockLibrary:
405 *
406 * xmlUnlockLibrary() is used to release a re-entrant lock on the libxml2
407 * library.
408 */
409 void
410 xmlUnlockLibrary(void)
411 {
412 xmlRMutexUnlock(xmlLibraryLock);
413 }
414
415 /**
416 * xmlInitThreads:
417 *
418 * DEPRECATED: Alias for xmlInitParser.
419 */
420 void
421 xmlInitThreads(void)
422 {
423 xmlInitParser();
424 }
425
426 /**
427 * xmlCleanupThreads:
428 *
429 * DEPRECATED: This function is a no-op. Call xmlCleanupParser
430 * to free global state but see the warnings there. xmlCleanupParser
431 * should be only called once at program exit. In most cases, you don't
432 * have call cleanup functions at all.
433 */
434 void
435 xmlCleanupThreads(void)
436 {
437 }
438
439 /************************************************************************
440 * *
441 * Library wide initialization *
442 * *
443 ************************************************************************/
444
445 static int xmlParserInitialized = 0;
446 static int xmlParserInnerInitialized = 0;
447
448
449 #ifdef HAVE_POSIX_THREADS
450 static pthread_mutex_t global_init_lock = PTHREAD_MUTEX_INITIALIZER;
451 #elif defined HAVE_WIN32_THREADS
452 static volatile LPCRITICAL_SECTION global_init_lock = NULL;
453 #endif
454
455 /**
456 * xmlGlobalInitMutexLock
457 *
458 * Makes sure that the global initialization mutex is initialized and
459 * locks it.
460 */
461 static void
462 xmlGlobalInitMutexLock(void) {
463 #ifdef HAVE_POSIX_THREADS
464
465 #ifdef XML_PTHREAD_WEAK
466 /*
467 * This is somewhat unreliable since libpthread could be loaded
468 * later with dlopen() and threads could be created. But it's
469 * long-standing behavior and hard to work around.
470 */
471 if (libxml_is_threaded == -1)
472 libxml_is_threaded =
473 (pthread_mutex_init != NULL) &&
474 (pthread_mutex_destroy != NULL) &&
475 (pthread_mutex_lock != NULL) &&
476 (pthread_mutex_unlock != NULL) &&
477 (pthread_cond_init != NULL) &&
478 (pthread_cond_destroy != NULL) &&
479 (pthread_cond_wait != NULL) &&
480 /*
481 * pthread_equal can be inline, resuting in -Waddress warnings.
482 * Let's assume it's available if all the other functions are.
483 */
484 /* (pthread_equal != NULL) && */
485 (pthread_self != NULL) &&
486 (pthread_cond_signal != NULL);
487 #endif
488
489 /* The mutex is statically initialized, so we just lock it. */
490 if (XML_IS_THREADED() != 0)
491 pthread_mutex_lock(&global_init_lock);
492
493 #elif defined HAVE_WIN32_THREADS
494
495 LPCRITICAL_SECTION cs;
496
497 /* Create a new critical section */
498 if (global_init_lock == NULL) {
499 cs = malloc(sizeof(CRITICAL_SECTION));
500 if (cs == NULL) {
501 xmlGenericError(xmlGenericErrorContext,
502 "xmlGlobalInitMutexLock: out of memory\n");
503 return;
504 }
505 InitializeCriticalSection(cs);
506
507 /* Swap it into the global_init_lock */
508 #ifdef InterlockedCompareExchangePointer
509 InterlockedCompareExchangePointer((void **) &global_init_lock,
510 cs, NULL);
511 #else /* Use older void* version */
512 InterlockedCompareExchange((void **) &global_init_lock,
513 (void *) cs, NULL);
514 #endif /* InterlockedCompareExchangePointer */
515
516 /* If another thread successfully recorded its critical
517 * section in the global_init_lock then discard the one
518 * allocated by this thread. */
519 if (global_init_lock != cs) {
520 DeleteCriticalSection(cs);
521 free(cs);
522 }
523 }
524
525 /* Lock the chosen critical section */
526 EnterCriticalSection(global_init_lock);
527
528 #endif
529 }
530
531 static void
532 xmlGlobalInitMutexUnlock(void) {
533 #ifdef HAVE_POSIX_THREADS
534 if (XML_IS_THREADED() != 0)
535 pthread_mutex_unlock(&global_init_lock);
536 #elif defined HAVE_WIN32_THREADS
537 if (global_init_lock != NULL)
538 LeaveCriticalSection(global_init_lock);
539 #endif
540 }
541
542 /**
543 * xmlGlobalInitMutexDestroy
544 *
545 * Makes sure that the global initialization mutex is destroyed before
546 * application termination.
547 */
548 static void
549 xmlGlobalInitMutexDestroy(void) {
550 #ifdef HAVE_POSIX_THREADS
551 #elif defined HAVE_WIN32_THREADS
552 if (global_init_lock != NULL) {
553 DeleteCriticalSection(global_init_lock);
554 free(global_init_lock);
555 global_init_lock = NULL;
556 }
557 #endif
558 }
559
560 /**
561 * xmlInitParser:
562 *
563 * Initialization function for the XML parser.
564 *
565 * Call once from the main thread before using the library in
566 * multithreaded programs.
567 */
568 void
569 xmlInitParser(void) {
570 /*
571 * Note that the initialization code must not make memory allocations.
572 */
573 if (xmlParserInitialized != 0)
574 return;
575
576 xmlGlobalInitMutexLock();
577
578 if (xmlParserInnerInitialized == 0) {
579 #if defined(_WIN32) && \
580 !defined(LIBXML_THREAD_ALLOC_ENABLED) && \
581 (!defined(LIBXML_STATIC) || defined(LIBXML_STATIC_FOR_DLL))
582 if (xmlFree == free)
583 atexit(xmlCleanupParser);
584 #endif
585
586 xmlInitMemoryInternal(); /* Should come second */
587 xmlInitGlobalsInternal();
588 xmlInitRandom();
589 xmlInitDictInternal();
590 xmlInitEncodingInternal();
591 #if defined(LIBXML_XPATH_ENABLED) || defined(LIBXML_SCHEMAS_ENABLED)
592 xmlInitXPathInternal();
593 #endif
594
595 xmlRegisterDefaultInputCallbacks();
596 #ifdef LIBXML_OUTPUT_ENABLED
597 xmlRegisterDefaultOutputCallbacks();
598 #endif /* LIBXML_OUTPUT_ENABLED */
599
600 xmlParserInnerInitialized = 1;
601 }
602
603 xmlGlobalInitMutexUnlock();
604
605 xmlParserInitialized = 1;
606 }
607
608 /**
609 * xmlCleanupParser:
610 *
611 * This function name is somewhat misleading. It does not clean up
612 * parser state, it cleans up memory allocated by the library itself.
613 * It is a cleanup function for the XML library. It tries to reclaim all
614 * related global memory allocated for the library processing.
615 * It doesn't deallocate any document related memory. One should
616 * call xmlCleanupParser() only when the process has finished using
617 * the library and all XML/HTML documents built with it.
618 * See also xmlInitParser() which has the opposite function of preparing
619 * the library for operations.
620 *
621 * WARNING: if your application is multithreaded or has plugin support
622 * calling this may crash the application if another thread or
623 * a plugin is still using libxml2. It's sometimes very hard to
624 * guess if libxml2 is in use in the application, some libraries
625 * or plugins may use it without notice. In case of doubt abstain
626 * from calling this function or do it just before calling exit()
627 * to avoid leak reports from valgrind !
628 */
629 void
630 xmlCleanupParser(void) {
631 if (!xmlParserInitialized)
632 return;
633
634 /* These functions can call xmlFree. */
635
636 xmlCleanupCharEncodingHandlers();
637 #ifdef LIBXML_CATALOG_ENABLED
638 xmlCatalogCleanup();
639 #endif
640 #ifdef LIBXML_SCHEMAS_ENABLED
641 xmlSchemaCleanupTypes();
642 xmlRelaxNGCleanupTypes();
643 #endif
644
645 /* These functions should never call xmlFree. */
646
647 xmlCleanupInputCallbacks();
648 #ifdef LIBXML_OUTPUT_ENABLED
649 xmlCleanupOutputCallbacks();
650 #endif
651
652 xmlCleanupDictInternal();
653 xmlCleanupRandom();
654 xmlCleanupGlobalsInternal();
655 /*
656 * Must come last. On Windows, xmlCleanupGlobalsInternal can call
657 * xmlFree which uses xmlMemMutex in debug mode.
658 */
659 xmlCleanupMemoryInternal();
660
661 xmlGlobalInitMutexDestroy();
662
663 xmlParserInitialized = 0;
664 xmlParserInnerInitialized = 0;
665 }
666
667 #if defined(HAVE_ATTRIBUTE_DESTRUCTOR) && \
668 !defined(LIBXML_THREAD_ALLOC_ENABLED) && \
669 !defined(LIBXML_STATIC) && \
670 !defined(_WIN32)
671 static void
672 ATTRIBUTE_DESTRUCTOR
673 xmlDestructor(void) {
674 /*
675 * Calling custom deallocation functions in a destructor can cause
676 * problems, for example with Nokogiri.
677 */
678 if (xmlFree == free)
679 xmlCleanupParser();
680 }
681 #endif
682