Coverage Report

Created: 2025-07-01 06:27

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