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