• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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