Coverage Report

Created: 2025-07-07 10:01

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