xref: /aosp_15_r20/external/cronet/third_party/libxml/src/threads.c (revision 6777b5387eb2ff775bb5750e3f5d96f37fb7352b)
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