Coverage Report

Created: 2025-06-22 06:55

/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
4
{
58
4
#ifdef HAVE_POSIX_THREADS
59
4
    pthread_mutex_init(&mutex->lock, NULL);
60
#elif defined HAVE_WIN32_THREADS
61
    InitializeCriticalSection(&mutex->cs);
62
#else
63
    (void) mutex;
64
#endif
65
4
}
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
22.0k
{
125
22.0k
    if (tok == NULL)
126
0
        return;
127
22.0k
#ifdef HAVE_POSIX_THREADS
128
    /*
129
     * This assumes that __libc_single_threaded won't change while the
130
     * lock is held.
131
     */
132
22.0k
    pthread_mutex_lock(&tok->lock);
133
#elif defined HAVE_WIN32_THREADS
134
    EnterCriticalSection(&tok->cs);
135
#endif
136
137
22.0k
}
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
22.0k
{
147
22.0k
    if (tok == NULL)
148
0
        return;
149
22.0k
#ifdef HAVE_POSIX_THREADS
150
22.0k
    pthread_mutex_unlock(&tok->lock);
151
#elif defined HAVE_WIN32_THREADS
152
    LeaveCriticalSection(&tok->cs);
153
#endif
154
22.0k
}
155
156
/**
157
 * Initialize the mutex.
158
 *
159
 * @param tok  mutex
160
 */
161
void
162
2
xmlInitRMutex(xmlRMutex *tok) {
163
2
    (void) tok;
164
165
2
#ifdef HAVE_POSIX_THREADS
166
2
    pthread_mutex_init(&tok->lock, NULL);
167
2
    tok->held = 0;
168
2
    tok->waiters = 0;
169
2
    pthread_cond_init(&tok->cv, NULL);
170
#elif defined HAVE_WIN32_THREADS
171
    InitializeCriticalSection(&tok->cs);
172
#endif
173
2
}
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
0
{
235
0
    if (tok == NULL)
236
0
        return;
237
0
#ifdef HAVE_POSIX_THREADS
238
0
    pthread_mutex_lock(&tok->lock);
239
0
    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
0
    tok->tid = pthread_self();
252
0
    tok->held = 1;
253
0
    pthread_mutex_unlock(&tok->lock);
254
#elif defined HAVE_WIN32_THREADS
255
    EnterCriticalSection(&tok->cs);
256
#endif
257
0
}
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
0
{
267
0
    if (tok == NULL)
268
0
        return;
269
0
#ifdef HAVE_POSIX_THREADS
270
0
    pthread_mutex_lock(&tok->lock);
271
0
    tok->held--;
272
0
    if (tok->held == 0) {
273
0
        if (tok->waiters)
274
0
            pthread_cond_signal(&tok->cv);
275
0
        memset(&tok->tid, 0, sizeof(tok->tid));
276
0
    }
277
0
    pthread_mutex_unlock(&tok->lock);
278
#elif defined HAVE_WIN32_THREADS
279
    LeaveCriticalSection(&tok->cs);
280
#endif
281
0
}
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
1
xmlInitThreadsInternal(void) {
331
1
    xmlInitRMutex(&xmlLibraryLock);
332
1
}
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
1
xmlInitParserInternal(void) {
357
    /*
358
     * Note that the initialization code must not make memory allocations.
359
     */
360
1
    xmlInitRandom(); /* Required by xmlInitGlobalsInternal */
361
1
    xmlInitMemoryInternal();
362
1
    xmlInitThreadsInternal();
363
1
    xmlInitGlobalsInternal();
364
1
    xmlInitDictInternal();
365
1
    xmlInitEncodingInternal();
366
1
#if defined(LIBXML_XPATH_ENABLED)
367
1
    xmlInitXPathInternal();
368
1
#endif
369
1
    xmlInitIOCallbacks();
370
1
#ifdef LIBXML_CATALOG_ENABLED
371
1
    xmlInitCatalogInternal();
372
1
#endif
373
374
1
    xmlParserInitialized = 1;
375
1
}
376
377
#if defined(HAVE_WIN32_THREADS)
378
static BOOL WINAPI
379
xmlInitParserWinWrapper(INIT_ONCE *initOnce ATTRIBUTE_UNUSED,
380
                        void *parameter ATTRIBUTE_UNUSED,
381
                        void **context ATTRIBUTE_UNUSED) {
382
    xmlInitParserInternal();
383
    return(TRUE);
384
}
385
#endif
386
387
/**
388
 * Initialization function for the XML parser.
389
 *
390
 * For older versions, it's recommended to call this function once
391
 * from the main thread before using the library in multithreaded
392
 * programs.
393
 *
394
 * Since 2.14.0, there's no distinction between threads. It should
395
 * be unnecessary to call this function.
396
 */
397
void
398
1.78M
xmlInitParser(void) {
399
1.78M
#ifdef HAVE_POSIX_THREADS
400
1.78M
    pthread_once(&onceControl, xmlInitParserInternal);
401
#elif defined(HAVE_WIN32_THREADS)
402
    InitOnceExecuteOnce(&onceControl, xmlInitParserWinWrapper, NULL, NULL);
403
#else
404
    if (onceControl == 0) {
405
        xmlInitParserInternal();
406
        onceControl = 1;
407
    }
408
#endif
409
1.78M
}
410
411
/**
412
 * Free global memory allocations.
413
 *
414
 * This function is named somewhat misleadingly. It does not clean up
415
 * parser state but frees global memory allocated by various components
416
 * of the library.
417
 *
418
 * Since 2.9.11, cleanup is performed automatically on most platforms
419
 * and there's no need at all for manual cleanup. This includes all
420
 * compilers and platforms that support GCC-style destructor attributes
421
 * as well as Windows DLLs.
422
 *
423
 * This function should only be used to avoid false positives from
424
 * memory leak checkers if automatic cleanup isn't possible, for
425
 * example with static builds on MSVC.
426
 *
427
 * WARNING: xmlCleanupParser is not thread-safe. If this function is
428
 * called and any threads that could make calls into libxml2 are
429
 * still running, memory corruption is likely to occur.
430
 *
431
 * No library calls must be made (from any thread) after calling this
432
 * function. In general, *this function should only be called right
433
 * before the whole process exits.* Calling this function too early
434
 * will lead to memory corruption.
435
 */
436
void
437
0
xmlCleanupParser(void) {
438
    /*
439
     * Unfortunately, some users call this function to fix memory
440
     * leaks on unload with versions before 2.9.11. This can result
441
     * in the library being reinitialized, so this use case must
442
     * be supported.
443
     */
444
0
    if (!xmlParserInitialized)
445
0
        return;
446
447
0
    xmlCleanupCharEncodingHandlers();
448
0
#ifdef LIBXML_CATALOG_ENABLED
449
0
    xmlCatalogCleanup();
450
0
    xmlCleanupCatalogInternal();
451
0
#endif
452
0
#ifdef LIBXML_SCHEMAS_ENABLED
453
0
    xmlSchemaCleanupTypes();
454
0
#endif
455
0
#ifdef LIBXML_RELAXNG_ENABLED
456
0
    xmlRelaxNGCleanupTypes();
457
0
#endif
458
459
0
    xmlCleanupDictInternal();
460
0
    xmlCleanupRandom();
461
0
    xmlCleanupGlobalsInternal();
462
0
    xmlCleanupThreadsInternal();
463
464
    /*
465
     * Must come after all cleanup functions that call xmlFree which
466
     * uses xmlMemMutex in debug mode.
467
     */
468
0
    xmlCleanupMemoryInternal();
469
470
0
    xmlParserInitialized = 0;
471
472
    /*
473
     * This is a bit sketchy but should make reinitialization work.
474
     */
475
0
#ifdef HAVE_POSIX_THREADS
476
0
    {
477
0
        pthread_once_t tmp = PTHREAD_ONCE_INIT;
478
0
        memcpy(&onceControl, &tmp, sizeof(tmp));
479
0
    }
480
#elif defined(HAVE_WIN32_THREADS)
481
    {
482
        INIT_ONCE tmp = INIT_ONCE_STATIC_INIT;
483
        memcpy(&onceControl, &tmp, sizeof(tmp));
484
    }
485
#else
486
    onceControl = 0;
487
#endif
488
0
}
489
490
#if defined(HAVE_FUNC_ATTRIBUTE_DESTRUCTOR) && \
491
    !defined(LIBXML_THREAD_ALLOC_ENABLED) && \
492
    !defined(LIBXML_STATIC) && \
493
    !defined(_WIN32)
494
static void
495
ATTRIBUTE_DESTRUCTOR
496
0
xmlDestructor(void) {
497
    /*
498
     * Calling custom deallocation functions in a destructor can cause
499
     * problems, for example with Nokogiri.
500
     */
501
0
    if (xmlFree == free)
502
0
        xmlCleanupParser();
503
0
}
504
#endif