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