xref: /aosp_15_r20/external/libxml2/threads.c (revision 7c5688314b92172186c154356a6374bf7684c3ca)
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 <stdarg.h>
15 #include <stdlib.h>
16 
17 #include <libxml/threads.h>
18 #include <libxml/parser.h>
19 #ifdef LIBXML_CATALOG_ENABLED
20 #include <libxml/catalog.h>
21 #endif
22 #ifdef LIBXML_SCHEMAS_ENABLED
23 #include <libxml/xmlschemastypes.h>
24 #include <libxml/relaxng.h>
25 #endif
26 
27 #if defined(SOLARIS)
28 #include <note.h>
29 #endif
30 
31 #include "private/cata.h"
32 #include "private/dict.h"
33 #include "private/enc.h"
34 #include "private/error.h"
35 #include "private/globals.h"
36 #include "private/io.h"
37 #include "private/memory.h"
38 #include "private/threads.h"
39 #include "private/xpath.h"
40 
41 /*
42  * TODO: this module still uses malloc/free and not xmlMalloc/xmlFree
43  *       to avoid some craziness since xmlMalloc/xmlFree may actually
44  *       be hosted on allocated blocks needing them for the allocation ...
45  */
46 
47 static xmlRMutex xmlLibraryLock;
48 
49 /**
50  * xmlInitMutex:
51  * @mutex:  the mutex
52  *
53  * Initialize a mutex.
54  */
55 void
xmlInitMutex(xmlMutexPtr mutex)56 xmlInitMutex(xmlMutexPtr mutex)
57 {
58 #ifdef HAVE_POSIX_THREADS
59     pthread_mutex_init(&mutex->lock, NULL);
60 #elif defined HAVE_WIN32_THREADS
61     InitializeCriticalSection(&mutex->cs);
62 #else
63     (void) mutex;
64 #endif
65 }
66 
67 /**
68  * xmlNewMutex:
69  *
70  * xmlNewMutex() is used to allocate a libxml2 token struct for use in
71  * synchronizing access to data.
72  *
73  * Returns a new simple mutex pointer or NULL in case of error
74  */
75 xmlMutexPtr
xmlNewMutex(void)76 xmlNewMutex(void)
77 {
78     xmlMutexPtr tok;
79 
80     tok = malloc(sizeof(xmlMutex));
81     if (tok == NULL)
82         return (NULL);
83     xmlInitMutex(tok);
84     return (tok);
85 }
86 
87 /**
88  * xmlCleanupMutex:
89  * @mutex:  the simple mutex
90  *
91  * Reclaim resources associated with a mutex.
92  */
93 void
xmlCleanupMutex(xmlMutexPtr mutex)94 xmlCleanupMutex(xmlMutexPtr mutex)
95 {
96 #ifdef HAVE_POSIX_THREADS
97     pthread_mutex_destroy(&mutex->lock);
98 #elif defined HAVE_WIN32_THREADS
99     DeleteCriticalSection(&mutex->cs);
100 #else
101     (void) mutex;
102 #endif
103 }
104 
105 /**
106  * xmlFreeMutex:
107  * @tok:  the simple mutex
108  *
109  * Free a mutex.
110  */
111 void
xmlFreeMutex(xmlMutexPtr tok)112 xmlFreeMutex(xmlMutexPtr tok)
113 {
114     if (tok == NULL)
115         return;
116 
117     xmlCleanupMutex(tok);
118     free(tok);
119 }
120 
121 /**
122  * xmlMutexLock:
123  * @tok:  the simple mutex
124  *
125  * xmlMutexLock() is used to lock a libxml2 token.
126  */
127 void
xmlMutexLock(xmlMutexPtr tok)128 xmlMutexLock(xmlMutexPtr tok)
129 {
130     if (tok == NULL)
131         return;
132 #ifdef HAVE_POSIX_THREADS
133     /*
134      * This assumes that __libc_single_threaded won't change while the
135      * lock is held.
136      */
137     pthread_mutex_lock(&tok->lock);
138 #elif defined HAVE_WIN32_THREADS
139     EnterCriticalSection(&tok->cs);
140 #endif
141 
142 }
143 
144 /**
145  * xmlMutexUnlock:
146  * @tok:  the simple mutex
147  *
148  * xmlMutexUnlock() is used to unlock a libxml2 token.
149  */
150 void
xmlMutexUnlock(xmlMutexPtr tok)151 xmlMutexUnlock(xmlMutexPtr tok)
152 {
153     if (tok == NULL)
154         return;
155 #ifdef HAVE_POSIX_THREADS
156     pthread_mutex_unlock(&tok->lock);
157 #elif defined HAVE_WIN32_THREADS
158     LeaveCriticalSection(&tok->cs);
159 #endif
160 }
161 
162 void
xmlInitRMutex(xmlRMutexPtr tok)163 xmlInitRMutex(xmlRMutexPtr tok) {
164     (void) tok;
165 
166 #ifdef HAVE_POSIX_THREADS
167     pthread_mutex_init(&tok->lock, NULL);
168     tok->held = 0;
169     tok->waiters = 0;
170     pthread_cond_init(&tok->cv, NULL);
171 #elif defined HAVE_WIN32_THREADS
172     InitializeCriticalSection(&tok->cs);
173 #endif
174 }
175 
176 /**
177  * xmlNewRMutex:
178  *
179  * xmlRNewMutex() is used to allocate a reentrant mutex for use in
180  * synchronizing access to data. token_r is a re-entrant lock and thus useful
181  * for synchronizing access to data structures that may be manipulated in a
182  * recursive fashion.
183  *
184  * Returns the new reentrant mutex pointer or NULL in case of error
185  */
186 xmlRMutexPtr
xmlNewRMutex(void)187 xmlNewRMutex(void)
188 {
189     xmlRMutexPtr tok;
190 
191     tok = malloc(sizeof(xmlRMutex));
192     if (tok == NULL)
193         return (NULL);
194     xmlInitRMutex(tok);
195     return (tok);
196 }
197 
198 void
xmlCleanupRMutex(xmlRMutexPtr tok)199 xmlCleanupRMutex(xmlRMutexPtr tok) {
200     (void) tok;
201 
202 #ifdef HAVE_POSIX_THREADS
203     pthread_mutex_destroy(&tok->lock);
204     pthread_cond_destroy(&tok->cv);
205 #elif defined HAVE_WIN32_THREADS
206     DeleteCriticalSection(&tok->cs);
207 #endif
208 }
209 
210 /**
211  * xmlFreeRMutex:
212  * @tok:  the reentrant mutex
213  *
214  * xmlRFreeMutex() is used to reclaim resources associated with a
215  * reentrant mutex.
216  */
217 void
xmlFreeRMutex(xmlRMutexPtr tok)218 xmlFreeRMutex(xmlRMutexPtr tok)
219 {
220     if (tok == NULL)
221         return;
222     xmlCleanupRMutex(tok);
223     free(tok);
224 }
225 
226 /**
227  * xmlRMutexLock:
228  * @tok:  the reentrant mutex
229  *
230  * xmlRMutexLock() is used to lock a libxml2 token_r.
231  */
232 void
xmlRMutexLock(xmlRMutexPtr tok)233 xmlRMutexLock(xmlRMutexPtr tok)
234 {
235     if (tok == NULL)
236         return;
237 #ifdef HAVE_POSIX_THREADS
238     pthread_mutex_lock(&tok->lock);
239     if (tok->held) {
240         if (pthread_equal(tok->tid, pthread_self())) {
241             tok->held++;
242             pthread_mutex_unlock(&tok->lock);
243             return;
244         } else {
245             tok->waiters++;
246             while (tok->held)
247                 pthread_cond_wait(&tok->cv, &tok->lock);
248             tok->waiters--;
249         }
250     }
251     tok->tid = pthread_self();
252     tok->held = 1;
253     pthread_mutex_unlock(&tok->lock);
254 #elif defined HAVE_WIN32_THREADS
255     EnterCriticalSection(&tok->cs);
256 #endif
257 }
258 
259 /**
260  * xmlRMutexUnlock:
261  * @tok:  the reentrant mutex
262  *
263  * xmlRMutexUnlock() is used to unlock a libxml2 token_r.
264  */
265 void
xmlRMutexUnlock(xmlRMutexPtr tok ATTRIBUTE_UNUSED)266 xmlRMutexUnlock(xmlRMutexPtr tok ATTRIBUTE_UNUSED)
267 {
268     if (tok == NULL)
269         return;
270 #ifdef HAVE_POSIX_THREADS
271     pthread_mutex_lock(&tok->lock);
272     tok->held--;
273     if (tok->held == 0) {
274         if (tok->waiters)
275             pthread_cond_signal(&tok->cv);
276         memset(&tok->tid, 0, sizeof(tok->tid));
277     }
278     pthread_mutex_unlock(&tok->lock);
279 #elif defined HAVE_WIN32_THREADS
280     LeaveCriticalSection(&tok->cs);
281 #endif
282 }
283 
284 /************************************************************************
285  *									*
286  *			Library wide thread interfaces			*
287  *									*
288  ************************************************************************/
289 
290 /**
291  * xmlGetThreadId:
292  *
293  * DEPRECATED: Internal function, do not use.
294  *
295  * xmlGetThreadId() find the current thread ID number
296  * Note that this is likely to be broken on some platforms using pthreads
297  * as the specification doesn't mandate pthread_t to be an integer type
298  *
299  * Returns the current thread ID number
300  */
301 int
xmlGetThreadId(void)302 xmlGetThreadId(void)
303 {
304 #ifdef HAVE_POSIX_THREADS
305     pthread_t id;
306     int ret;
307 
308     id = pthread_self();
309     /* horrible but preserves compat, see warning above */
310     memcpy(&ret, &id, sizeof(ret));
311     return (ret);
312 #elif defined HAVE_WIN32_THREADS
313     return GetCurrentThreadId();
314 #else
315     return ((int) 0);
316 #endif
317 }
318 
319 /**
320  * xmlLockLibrary:
321  *
322  * xmlLockLibrary() is used to take out a re-entrant lock on the libxml2
323  * library.
324  */
325 void
xmlLockLibrary(void)326 xmlLockLibrary(void)
327 {
328     xmlRMutexLock(&xmlLibraryLock);
329 }
330 
331 /**
332  * xmlUnlockLibrary:
333  *
334  * xmlUnlockLibrary() is used to release a re-entrant lock on the libxml2
335  * library.
336  */
337 void
xmlUnlockLibrary(void)338 xmlUnlockLibrary(void)
339 {
340     xmlRMutexUnlock(&xmlLibraryLock);
341 }
342 
343 /**
344  * xmlInitThreads:
345  *
346  * DEPRECATED: Alias for xmlInitParser.
347  */
348 void
xmlInitThreads(void)349 xmlInitThreads(void)
350 {
351     xmlInitParser();
352 }
353 
354 /**
355  * xmlCleanupThreads:
356  *
357  * DEPRECATED: This function is a no-op. Call xmlCleanupParser
358  * to free global state but see the warnings there. xmlCleanupParser
359  * should be only called once at program exit. In most cases, you don't
360  * have call cleanup functions at all.
361  */
362 void
xmlCleanupThreads(void)363 xmlCleanupThreads(void)
364 {
365 }
366 
367 static void
xmlInitThreadsInternal(void)368 xmlInitThreadsInternal(void) {
369     xmlInitRMutex(&xmlLibraryLock);
370 }
371 
372 static void
xmlCleanupThreadsInternal(void)373 xmlCleanupThreadsInternal(void) {
374     xmlCleanupRMutex(&xmlLibraryLock);
375 }
376 
377 /************************************************************************
378  *									*
379  *			Library wide initialization			*
380  *									*
381  ************************************************************************/
382 
383 static int xmlParserInitialized = 0;
384 
385 #ifdef HAVE_POSIX_THREADS
386 static pthread_once_t onceControl = PTHREAD_ONCE_INIT;
387 #elif defined HAVE_WIN32_THREADS
388 static INIT_ONCE onceControl = INIT_ONCE_STATIC_INIT;
389 #else
390 static int onceControl = 0;
391 #endif
392 
393 static void
xmlInitParserInternal(void)394 xmlInitParserInternal(void) {
395     /*
396      * Note that the initialization code must not make memory allocations.
397      */
398     xmlInitRandom(); /* Required by xmlInitGlobalsInternal */
399     xmlInitMemoryInternal();
400     xmlInitThreadsInternal();
401     xmlInitGlobalsInternal();
402     xmlInitDictInternal();
403     xmlInitEncodingInternal();
404 #if defined(LIBXML_XPATH_ENABLED)
405     xmlInitXPathInternal();
406 #endif
407     xmlInitIOCallbacks();
408 #ifdef LIBXML_CATALOG_ENABLED
409     xmlInitCatalogInternal();
410 #endif
411 
412     xmlParserInitialized = 1;
413 }
414 
415 #if defined(HAVE_WIN32_THREADS)
416 static BOOL WINAPI
xmlInitParserWinWrapper(INIT_ONCE * initOnce ATTRIBUTE_UNUSED,void * parameter ATTRIBUTE_UNUSED,void ** context ATTRIBUTE_UNUSED)417 xmlInitParserWinWrapper(INIT_ONCE *initOnce ATTRIBUTE_UNUSED,
418                         void *parameter ATTRIBUTE_UNUSED,
419                         void **context ATTRIBUTE_UNUSED) {
420     xmlInitParserInternal();
421     return(TRUE);
422 }
423 #endif
424 
425 /**
426  * xmlInitParser:
427  *
428  * Initialization function for the XML parser.
429  *
430  * For older versions, it's recommended to call this function once
431  * from the main thread before using the library in multithreaded
432  * programs.
433  *
434  * Since 2.14.0, there's no distinction between threads. It should
435  * be unnecessary to call this function.
436  */
437 void
xmlInitParser(void)438 xmlInitParser(void) {
439 #ifdef HAVE_POSIX_THREADS
440     pthread_once(&onceControl, xmlInitParserInternal);
441 #elif defined(HAVE_WIN32_THREADS)
442     InitOnceExecuteOnce(&onceControl, xmlInitParserWinWrapper, NULL, NULL);
443 #else
444     if (onceControl == 0) {
445         xmlInitParserInternal();
446         onceControl = 1;
447     }
448 #endif
449 }
450 
451 /**
452  * xmlCleanupParser:
453  *
454  * This function is named somewhat misleadingly. It does not clean up
455  * parser state but global memory allocated by the library itself.
456  *
457  * Since 2.9.11, cleanup is performed automatically if a shared or
458  * dynamic libxml2 library is unloaded. This function should only
459  * be used to avoid false positives from memory leak checkers in
460  * static builds.
461  *
462  * WARNING: xmlCleanupParser assumes that all other threads that called
463  * libxml2 functions have terminated. No library calls must be made
464  * after calling this function. In general, THIS FUNCTION SHOULD ONLY
465  * BE CALLED RIGHT BEFORE THE WHOLE PROCESS EXITS.
466  */
467 void
xmlCleanupParser(void)468 xmlCleanupParser(void) {
469     /*
470      * Unfortunately, some users call this function to fix memory
471      * leaks on unload with versions before 2.9.11. This can result
472      * in the library being reinitialized, so this use case must
473      * be supported.
474      */
475     if (!xmlParserInitialized)
476         return;
477 
478     xmlCleanupCharEncodingHandlers();
479 #ifdef LIBXML_CATALOG_ENABLED
480     xmlCatalogCleanup();
481     xmlCleanupCatalogInternal();
482 #endif
483 #ifdef LIBXML_SCHEMAS_ENABLED
484     xmlSchemaCleanupTypes();
485     xmlRelaxNGCleanupTypes();
486 #endif
487 
488     xmlCleanupDictInternal();
489     xmlCleanupRandom();
490     xmlCleanupGlobalsInternal();
491     xmlCleanupThreadsInternal();
492 
493     /*
494      * Must come after all cleanup functions that call xmlFree which
495      * uses xmlMemMutex in debug mode.
496      */
497     xmlCleanupMemoryInternal();
498 
499     xmlParserInitialized = 0;
500 
501     /*
502      * This is a bit sketchy but should make reinitialization work.
503      */
504 #ifdef HAVE_POSIX_THREADS
505     {
506         pthread_once_t tmp = PTHREAD_ONCE_INIT;
507         memcpy(&onceControl, &tmp, sizeof(tmp));
508     }
509 #elif defined(HAVE_WIN32_THREADS)
510     {
511         INIT_ONCE tmp = INIT_ONCE_STATIC_INIT;
512         memcpy(&onceControl, &tmp, sizeof(tmp));
513     }
514 #else
515     onceControl = 0;
516 #endif
517 }
518 
519 #if defined(HAVE_FUNC_ATTRIBUTE_DESTRUCTOR) && \
520     !defined(LIBXML_THREAD_ALLOC_ENABLED) && \
521     !defined(LIBXML_STATIC) && \
522     !defined(_WIN32)
523 static void
524 ATTRIBUTE_DESTRUCTOR
xmlDestructor(void)525 xmlDestructor(void) {
526     /*
527      * Calling custom deallocation functions in a destructor can cause
528      * problems, for example with Nokogiri.
529      */
530     if (xmlFree == free)
531         xmlCleanupParser();
532 }
533 #endif
534