VirtualBox

source: vbox/trunk/src/libs/libxml2-2.13.2/threads.c@ 106580

Last change on this file since 106580 was 105448, checked in by vboxsync, 4 months ago

libxml2-2.13.2: builds and runs on Linux. bugref:10730

  • Property svn:eol-style set to native
File size: 16.1 KB
Line 
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/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
88static 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 */
106struct _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
120static xmlRMutexPtr xmlLibraryLock = NULL;
121
122/**
123 * xmlInitMutex:
124 * @mutex: the mutex
125 *
126 * Initialize a mutex.
127 */
128void
129xmlInitMutex(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 */
149xmlMutexPtr
150xmlNewMutex(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 */
166void
167xmlCleanupMutex(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 */
185void
186xmlFreeMutex(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 */
201void
202xmlMutexLock(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 */
225void
226xmlMutexUnlock(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 */
248xmlRMutexPtr
249xmlNewRMutex(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 */
275void
276xmlFreeRMutex(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 */
297void
298xmlRMutexLock(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 */
333void
334xmlRMutexUnlock(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 */
372int
373xmlGetThreadId(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 */
398void
399xmlLockLibrary(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 */
410void
411xmlUnlockLibrary(void)
412{
413 xmlRMutexUnlock(xmlLibraryLock);
414}
415
416/**
417 * xmlInitThreads:
418 *
419 * DEPRECATED: Alias for xmlInitParser.
420 */
421void
422xmlInitThreads(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 */
435void
436xmlCleanupThreads(void)
437{
438}
439
440/************************************************************************
441 * *
442 * Library wide initialization *
443 * *
444 ************************************************************************/
445
446static int xmlParserInitialized = 0;
447static int xmlParserInnerInitialized = 0;
448
449
450#ifdef HAVE_POSIX_THREADS
451static pthread_mutex_t global_init_lock = PTHREAD_MUTEX_INITIALIZER;
452#elif defined HAVE_WIN32_THREADS
453static 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 */
462static void
463xmlGlobalInitMutexLock(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
531static void
532xmlGlobalInitMutexUnlock(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 */
548static void
549xmlGlobalInitMutexDestroy(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 */
568void
569xmlInitParser(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#ifndef VBOX
587 xmlInitRandom(); /* Required by xmlInitGlobalsInternal */
588#endif
589 xmlInitMemoryInternal();
590 xmlInitGlobalsInternal();
591 xmlInitDictInternal();
592 xmlInitEncodingInternal();
593#if defined(LIBXML_XPATH_ENABLED) || defined(LIBXML_SCHEMAS_ENABLED)
594 xmlInitXPathInternal();
595#endif
596 xmlInitIOCallbacks();
597
598 xmlParserInnerInitialized = 1;
599 }
600
601 xmlGlobalInitMutexUnlock();
602
603 xmlParserInitialized = 1;
604}
605
606/**
607 * xmlCleanupParser:
608 *
609 * This function name is somewhat misleading. It does not clean up
610 * parser state, it cleans up memory allocated by the library itself.
611 * It is a cleanup function for the XML library. It tries to reclaim all
612 * related global memory allocated for the library processing.
613 * It doesn't deallocate any document related memory. One should
614 * call xmlCleanupParser() only when the process has finished using
615 * the library and all XML/HTML documents built with it.
616 * See also xmlInitParser() which has the opposite function of preparing
617 * the library for operations.
618 *
619 * WARNING: if your application is multithreaded or has plugin support
620 * calling this may crash the application if another thread or
621 * a plugin is still using libxml2. It's sometimes very hard to
622 * guess if libxml2 is in use in the application, some libraries
623 * or plugins may use it without notice. In case of doubt abstain
624 * from calling this function or do it just before calling exit()
625 * to avoid leak reports from valgrind !
626 */
627void
628xmlCleanupParser(void) {
629 if (!xmlParserInitialized)
630 return;
631
632 /* These functions can call xmlFree. */
633
634 xmlCleanupCharEncodingHandlers();
635#ifdef LIBXML_CATALOG_ENABLED
636 xmlCatalogCleanup();
637#endif
638#ifdef LIBXML_SCHEMAS_ENABLED
639 xmlSchemaCleanupTypes();
640 xmlRelaxNGCleanupTypes();
641#endif
642
643 /* These functions should never call xmlFree. */
644
645 xmlCleanupDictInternal();
646#ifndef VBOX
647 xmlCleanupRandom();
648#endif
649 xmlCleanupGlobalsInternal();
650 /*
651 * Must come last. On Windows, xmlCleanupGlobalsInternal can call
652 * xmlFree which uses xmlMemMutex in debug mode.
653 */
654 xmlCleanupMemoryInternal();
655
656 xmlGlobalInitMutexDestroy();
657
658 xmlParserInitialized = 0;
659 xmlParserInnerInitialized = 0;
660}
661
662#if defined(HAVE_ATTRIBUTE_DESTRUCTOR) && \
663 !defined(LIBXML_THREAD_ALLOC_ENABLED) && \
664 !defined(LIBXML_STATIC) && \
665 !defined(_WIN32)
666static void
667ATTRIBUTE_DESTRUCTOR
668xmlDestructor(void) {
669 /*
670 * Calling custom deallocation functions in a destructor can cause
671 * problems, for example with Nokogiri.
672 */
673 if (xmlFree == free)
674 xmlCleanupParser();
675}
676#endif
677
Note: See TracBrowser for help on using the repository browser.

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette