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