Coverage Report

Created: 2024-01-18 09:16

/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
 * 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/globals.h>
18
19
#ifdef LIBXML_THREAD_ENABLED
20
  #ifdef HAVE_PTHREAD_H
21
    #include <pthread.h>
22
    #define HAVE_POSIX_THREADS
23
  #elif defined(_WIN32)
24
    #define WIN32_LEAN_AND_MEAN
25
    #include <windows.h>
26
    #ifndef HAVE_COMPILER_TLS
27
      #include <process.h>
28
    #endif
29
    #define HAVE_WIN32_THREADS
30
  #endif
31
#endif
32
33
#if defined(SOLARIS)
34
#include <note.h>
35
#endif
36
37
#include "private/dict.h"
38
#include "private/threads.h"
39
40
/* #define DEBUG_THREADS */
41
42
#ifdef HAVE_POSIX_THREADS
43
44
#if defined(__GNUC__) && defined(__linux__)
45
46
static int libxml_is_threaded = -1;
47
48
#define XML_PTHREAD_WEAK
49
50
#pragma weak pthread_once
51
#pragma weak pthread_getspecific
52
#pragma weak pthread_setspecific
53
#pragma weak pthread_key_create
54
#pragma weak pthread_key_delete
55
#pragma weak pthread_mutex_init
56
#pragma weak pthread_mutex_destroy
57
#pragma weak pthread_mutex_lock
58
#pragma weak pthread_mutex_unlock
59
#pragma weak pthread_cond_init
60
#pragma weak pthread_cond_destroy
61
#pragma weak pthread_cond_wait
62
#pragma weak pthread_equal
63
#pragma weak pthread_self
64
#pragma weak pthread_key_create
65
#pragma weak pthread_key_delete
66
#pragma weak pthread_cond_signal
67
68
#else /* __GNUC__, __GLIBC__, __linux__ */
69
70
static int libxml_is_threaded = 1;
71
72
#endif /* __GNUC__, __GLIBC__, __linux__ */
73
74
#endif /* HAVE_POSIX_THREADS */
75
76
/*
77
 * TODO: this module still uses malloc/free and not xmlMalloc/xmlFree
78
 *       to avoid some craziness since xmlMalloc/xmlFree may actually
79
 *       be hosted on allocated blocks needing them for the allocation ...
80
 */
81
82
/*
83
 * xmlMutex are a simple mutual exception locks
84
 */
85
struct _xmlMutex {
86
#ifdef HAVE_POSIX_THREADS
87
    pthread_mutex_t lock;
88
#elif defined HAVE_WIN32_THREADS
89
    CRITICAL_SECTION cs;
90
#else
91
    int empty;
92
#endif
93
};
94
95
/*
96
 * xmlRMutex are reentrant mutual exception locks
97
 */
98
struct _xmlRMutex {
99
#ifdef HAVE_POSIX_THREADS
100
    pthread_mutex_t lock;
101
    unsigned int held;
102
    unsigned int waiters;
103
    pthread_t tid;
104
    pthread_cond_t cv;
105
#elif defined HAVE_WIN32_THREADS
106
    CRITICAL_SECTION cs;
107
#else
108
    int empty;
109
#endif
110
};
111
112
/*
113
 * This module still has some internal static data.
114
 *   - xmlLibraryLock a global lock
115
 *   - globalkey used for per-thread data
116
 */
117
118
#ifdef HAVE_POSIX_THREADS
119
static pthread_key_t globalkey;
120
static pthread_t mainthread;
121
static pthread_once_t once_control = PTHREAD_ONCE_INIT;
122
static pthread_once_t once_control_init = PTHREAD_ONCE_INIT;
123
static pthread_mutex_t global_init_lock = PTHREAD_MUTEX_INITIALIZER;
124
#elif defined HAVE_WIN32_THREADS
125
#if defined(HAVE_COMPILER_TLS)
126
static __declspec(thread) xmlGlobalState tlstate;
127
static __declspec(thread) int tlstate_inited = 0;
128
#else /* HAVE_COMPILER_TLS */
129
static DWORD globalkey = TLS_OUT_OF_INDEXES;
130
#endif /* HAVE_COMPILER_TLS */
131
static DWORD mainthread;
132
static struct {
133
    DWORD done;
134
    LONG control;
135
} run_once = { 0, 0};
136
static volatile LPCRITICAL_SECTION global_init_lock = NULL;
137
#endif
138
139
static xmlRMutexPtr xmlLibraryLock = NULL;
140
141
#ifdef LIBXML_THREAD_ENABLED
142
static void xmlOnceInit(void);
143
#endif
144
145
/**
146
 * xmlNewMutex:
147
 *
148
 * xmlNewMutex() is used to allocate a libxml2 token struct for use in
149
 * synchronizing access to data.
150
 *
151
 * Returns a new simple mutex pointer or NULL in case of error
152
 */
153
xmlMutexPtr
154
xmlNewMutex(void)
155
9.33k
{
156
9.33k
    xmlMutexPtr tok;
157
158
9.33k
    if ((tok = malloc(sizeof(xmlMutex))) == NULL)
159
0
        return (NULL);
160
9.33k
#ifdef HAVE_POSIX_THREADS
161
9.33k
    if (libxml_is_threaded != 0)
162
9.33k
        pthread_mutex_init(&tok->lock, NULL);
163
#elif defined HAVE_WIN32_THREADS
164
    InitializeCriticalSection(&tok->cs);
165
#endif
166
9.33k
    return (tok);
167
9.33k
}
168
169
/**
170
 * xmlFreeMutex:
171
 * @tok:  the simple mutex
172
 *
173
 * xmlFreeMutex() is used to reclaim resources associated with a libxml2 token
174
 * struct.
175
 */
176
void
177
xmlFreeMutex(xmlMutexPtr tok)
178
0
{
179
0
    if (tok == NULL)
180
0
        return;
181
182
0
#ifdef HAVE_POSIX_THREADS
183
0
    if (libxml_is_threaded != 0)
184
0
        pthread_mutex_destroy(&tok->lock);
185
#elif defined HAVE_WIN32_THREADS
186
    DeleteCriticalSection(&tok->cs);
187
#endif
188
0
    free(tok);
189
0
}
190
191
/**
192
 * xmlMutexLock:
193
 * @tok:  the simple mutex
194
 *
195
 * xmlMutexLock() is used to lock a libxml2 token.
196
 */
197
void
198
xmlMutexLock(xmlMutexPtr tok)
199
6.92M
{
200
6.92M
    if (tok == NULL)
201
0
        return;
202
6.92M
#ifdef HAVE_POSIX_THREADS
203
6.92M
    if (libxml_is_threaded != 0)
204
6.92M
        pthread_mutex_lock(&tok->lock);
205
#elif defined HAVE_WIN32_THREADS
206
    EnterCriticalSection(&tok->cs);
207
#endif
208
209
6.92M
}
210
211
/**
212
 * xmlMutexUnlock:
213
 * @tok:  the simple mutex
214
 *
215
 * xmlMutexUnlock() is used to unlock a libxml2 token.
216
 */
217
void
218
xmlMutexUnlock(xmlMutexPtr tok)
219
6.92M
{
220
6.92M
    if (tok == NULL)
221
0
        return;
222
6.92M
#ifdef HAVE_POSIX_THREADS
223
6.92M
    if (libxml_is_threaded != 0)
224
6.92M
        pthread_mutex_unlock(&tok->lock);
225
#elif defined HAVE_WIN32_THREADS
226
    LeaveCriticalSection(&tok->cs);
227
#endif
228
6.92M
}
229
230
/**
231
 * xmlNewRMutex:
232
 *
233
 * xmlRNewMutex() is used to allocate a reentrant mutex for use in
234
 * synchronizing access to data. token_r is a re-entrant lock and thus useful
235
 * for synchronizing access to data structures that may be manipulated in a
236
 * recursive fashion.
237
 *
238
 * Returns the new reentrant mutex pointer or NULL in case of error
239
 */
240
xmlRMutexPtr
241
xmlNewRMutex(void)
242
3.11k
{
243
3.11k
    xmlRMutexPtr tok;
244
245
3.11k
    if ((tok = malloc(sizeof(xmlRMutex))) == NULL)
246
0
        return (NULL);
247
3.11k
#ifdef HAVE_POSIX_THREADS
248
3.11k
    if (libxml_is_threaded != 0) {
249
3.11k
        pthread_mutex_init(&tok->lock, NULL);
250
3.11k
        tok->held = 0;
251
3.11k
        tok->waiters = 0;
252
3.11k
        pthread_cond_init(&tok->cv, NULL);
253
3.11k
    }
254
#elif defined HAVE_WIN32_THREADS
255
    InitializeCriticalSection(&tok->cs);
256
#endif
257
3.11k
    return (tok);
258
3.11k
}
259
260
/**
261
 * xmlFreeRMutex:
262
 * @tok:  the reentrant mutex
263
 *
264
 * xmlRFreeMutex() is used to reclaim resources associated with a
265
 * reentrant mutex.
266
 */
267
void
268
xmlFreeRMutex(xmlRMutexPtr tok ATTRIBUTE_UNUSED)
269
0
{
270
0
    if (tok == NULL)
271
0
        return;
272
0
#ifdef HAVE_POSIX_THREADS
273
0
    if (libxml_is_threaded != 0) {
274
0
        pthread_mutex_destroy(&tok->lock);
275
0
        pthread_cond_destroy(&tok->cv);
276
0
    }
277
#elif defined HAVE_WIN32_THREADS
278
    DeleteCriticalSection(&tok->cs);
279
#endif
280
0
    free(tok);
281
0
}
282
283
/**
284
 * xmlRMutexLock:
285
 * @tok:  the reentrant mutex
286
 *
287
 * xmlRMutexLock() is used to lock a libxml2 token_r.
288
 */
289
void
290
xmlRMutexLock(xmlRMutexPtr tok)
291
3.11k
{
292
3.11k
    if (tok == NULL)
293
0
        return;
294
3.11k
#ifdef HAVE_POSIX_THREADS
295
3.11k
    if (libxml_is_threaded == 0)
296
0
        return;
297
298
3.11k
    pthread_mutex_lock(&tok->lock);
299
3.11k
    if (tok->held) {
300
0
        if (pthread_equal(tok->tid, pthread_self())) {
301
0
            tok->held++;
302
0
            pthread_mutex_unlock(&tok->lock);
303
0
            return;
304
0
        } else {
305
0
            tok->waiters++;
306
0
            while (tok->held)
307
0
                pthread_cond_wait(&tok->cv, &tok->lock);
308
0
            tok->waiters--;
309
0
        }
310
0
    }
311
3.11k
    tok->tid = pthread_self();
312
3.11k
    tok->held = 1;
313
3.11k
    pthread_mutex_unlock(&tok->lock);
314
#elif defined HAVE_WIN32_THREADS
315
    EnterCriticalSection(&tok->cs);
316
#endif
317
3.11k
}
318
319
/**
320
 * xmlRMutexUnlock:
321
 * @tok:  the reentrant mutex
322
 *
323
 * xmlRMutexUnlock() is used to unlock a libxml2 token_r.
324
 */
325
void
326
xmlRMutexUnlock(xmlRMutexPtr tok ATTRIBUTE_UNUSED)
327
3.11k
{
328
3.11k
    if (tok == NULL)
329
0
        return;
330
3.11k
#ifdef HAVE_POSIX_THREADS
331
3.11k
    if (libxml_is_threaded == 0)
332
0
        return;
333
334
3.11k
    pthread_mutex_lock(&tok->lock);
335
3.11k
    tok->held--;
336
3.11k
    if (tok->held == 0) {
337
3.11k
        if (tok->waiters)
338
0
            pthread_cond_signal(&tok->cv);
339
3.11k
        memset(&tok->tid, 0, sizeof(tok->tid));
340
3.11k
    }
341
3.11k
    pthread_mutex_unlock(&tok->lock);
342
#elif defined HAVE_WIN32_THREADS
343
    LeaveCriticalSection(&tok->cs);
344
#endif
345
3.11k
}
346
347
/**
348
 * xmlGlobalInitMutexLock
349
 *
350
 * Makes sure that the global initialization mutex is initialized and
351
 * locks it.
352
 */
353
void
354
__xmlGlobalInitMutexLock(void)
355
3.11k
{
356
    /* Make sure the global init lock is initialized and then lock it. */
357
3.11k
#ifdef HAVE_POSIX_THREADS
358
    /* The mutex is statically initialized, so we just lock it. */
359
3.11k
#ifdef XML_PTHREAD_WEAK
360
3.11k
    if (pthread_mutex_lock == NULL)
361
0
        return;
362
3.11k
#endif /* XML_PTHREAD_WEAK */
363
3.11k
    pthread_mutex_lock(&global_init_lock);
364
#elif defined HAVE_WIN32_THREADS
365
    LPCRITICAL_SECTION cs;
366
367
    /* Create a new critical section */
368
    if (global_init_lock == NULL) {
369
        cs = malloc(sizeof(CRITICAL_SECTION));
370
        if (cs == NULL) {
371
            xmlGenericError(xmlGenericErrorContext,
372
                            "xmlGlobalInitMutexLock: out of memory\n");
373
            return;
374
        }
375
        InitializeCriticalSection(cs);
376
377
        /* Swap it into the global_init_lock */
378
#ifdef InterlockedCompareExchangePointer
379
        InterlockedCompareExchangePointer((void **) &global_init_lock,
380
                                          cs, NULL);
381
#else /* Use older void* version */
382
        InterlockedCompareExchange((void **) &global_init_lock,
383
                                   (void *) cs, NULL);
384
#endif /* InterlockedCompareExchangePointer */
385
386
        /* If another thread successfully recorded its critical
387
         * section in the global_init_lock then discard the one
388
         * allocated by this thread. */
389
        if (global_init_lock != cs) {
390
            DeleteCriticalSection(cs);
391
            free(cs);
392
        }
393
    }
394
395
    /* Lock the chosen critical section */
396
    EnterCriticalSection(global_init_lock);
397
#endif
398
3.11k
}
399
400
void
401
__xmlGlobalInitMutexUnlock(void)
402
3.11k
{
403
3.11k
#ifdef HAVE_POSIX_THREADS
404
3.11k
#ifdef XML_PTHREAD_WEAK
405
3.11k
    if (pthread_mutex_unlock == NULL)
406
0
        return;
407
3.11k
#endif /* XML_PTHREAD_WEAK */
408
3.11k
    pthread_mutex_unlock(&global_init_lock);
409
#elif defined HAVE_WIN32_THREADS
410
    if (global_init_lock != NULL) {
411
  LeaveCriticalSection(global_init_lock);
412
    }
413
#endif
414
3.11k
}
415
416
/**
417
 * xmlGlobalInitMutexDestroy
418
 *
419
 * Makes sure that the global initialization mutex is destroyed before
420
 * application termination.
421
 */
422
void
423
__xmlGlobalInitMutexDestroy(void)
424
0
{
425
0
#ifdef HAVE_POSIX_THREADS
426
#elif defined HAVE_WIN32_THREADS
427
    if (global_init_lock != NULL) {
428
        DeleteCriticalSection(global_init_lock);
429
        free(global_init_lock);
430
        global_init_lock = NULL;
431
    }
432
#endif
433
0
}
434
435
/************************************************************************
436
 *                  *
437
 *      Per thread global state handling    *
438
 *                  *
439
 ************************************************************************/
440
441
#ifdef LIBXML_THREAD_ENABLED
442
#ifdef xmlLastError
443
#undef xmlLastError
444
#endif
445
446
/**
447
 * xmlFreeGlobalState:
448
 * @state:  a thread global state
449
 *
450
 * xmlFreeGlobalState() is called when a thread terminates with a non-NULL
451
 * global state. It is is used here to reclaim memory resources.
452
 */
453
static void
454
xmlFreeGlobalState(void *state)
455
0
{
456
0
    xmlGlobalState *gs = (xmlGlobalState *) state;
457
458
    /* free any memory allocated in the thread's xmlLastError */
459
0
    xmlResetError(&(gs->xmlLastError));
460
0
    free(state);
461
0
}
462
463
/**
464
 * xmlNewGlobalState:
465
 *
466
 * xmlNewGlobalState() allocates a global state. This structure is used to
467
 * hold all data for use by a thread when supporting backwards compatibility
468
 * of libxml2 to pre-thread-safe behaviour.
469
 *
470
 * Returns the newly allocated xmlGlobalStatePtr or NULL in case of error
471
 */
472
static xmlGlobalStatePtr
473
xmlNewGlobalState(void)
474
0
{
475
0
    xmlGlobalState *gs;
476
477
0
    gs = malloc(sizeof(xmlGlobalState));
478
0
    if (gs == NULL) {
479
0
  xmlGenericError(xmlGenericErrorContext,
480
0
      "xmlGetGlobalState: out of memory\n");
481
0
        return (NULL);
482
0
    }
483
484
0
    memset(gs, 0, sizeof(xmlGlobalState));
485
0
    xmlInitializeGlobalState(gs);
486
0
    return (gs);
487
0
}
488
#endif /* LIBXML_THREAD_ENABLED */
489
490
#ifdef HAVE_POSIX_THREADS
491
#elif defined HAVE_WIN32_THREADS
492
#if !defined(HAVE_COMPILER_TLS)
493
#if defined(LIBXML_STATIC) && !defined(LIBXML_STATIC_FOR_DLL)
494
typedef struct _xmlGlobalStateCleanupHelperParams {
495
    HANDLE thread;
496
    void *memory;
497
} xmlGlobalStateCleanupHelperParams;
498
499
static void XMLCDECL
500
xmlGlobalStateCleanupHelper(void *p)
501
{
502
    xmlGlobalStateCleanupHelperParams *params =
503
        (xmlGlobalStateCleanupHelperParams *) p;
504
    WaitForSingleObject(params->thread, INFINITE);
505
    CloseHandle(params->thread);
506
    xmlFreeGlobalState(params->memory);
507
    free(params);
508
    _endthread();
509
}
510
#else /* LIBXML_STATIC && !LIBXML_STATIC_FOR_DLL */
511
512
typedef struct _xmlGlobalStateCleanupHelperParams {
513
    void *memory;
514
    struct _xmlGlobalStateCleanupHelperParams *prev;
515
    struct _xmlGlobalStateCleanupHelperParams *next;
516
} xmlGlobalStateCleanupHelperParams;
517
518
static xmlGlobalStateCleanupHelperParams *cleanup_helpers_head = NULL;
519
static CRITICAL_SECTION cleanup_helpers_cs;
520
521
#endif /* LIBXMLSTATIC && !LIBXML_STATIC_FOR_DLL */
522
#endif /* HAVE_COMPILER_TLS */
523
#endif /* HAVE_WIN32_THREADS */
524
525
/**
526
 * xmlGetGlobalState:
527
 *
528
 * xmlGetGlobalState() is called to retrieve the global state for a thread.
529
 *
530
 * Returns the thread global state or NULL in case of error
531
 */
532
xmlGlobalStatePtr
533
xmlGetGlobalState(void)
534
0
{
535
0
#ifdef HAVE_POSIX_THREADS
536
0
    xmlGlobalState *globalval;
537
538
0
    if (libxml_is_threaded == 0)
539
0
        return (NULL);
540
541
0
    pthread_once(&once_control, xmlOnceInit);
542
543
0
    if ((globalval = (xmlGlobalState *)
544
0
         pthread_getspecific(globalkey)) == NULL) {
545
0
        xmlGlobalState *tsd = xmlNewGlobalState();
546
0
  if (tsd == NULL)
547
0
      return(NULL);
548
549
0
        pthread_setspecific(globalkey, tsd);
550
0
        return (tsd);
551
0
    }
552
0
    return (globalval);
553
#elif defined HAVE_WIN32_THREADS
554
#if defined(HAVE_COMPILER_TLS)
555
    if (!tlstate_inited) {
556
        tlstate_inited = 1;
557
        xmlInitializeGlobalState(&tlstate);
558
    }
559
    return &tlstate;
560
#else /* HAVE_COMPILER_TLS */
561
    xmlGlobalState *globalval;
562
    xmlGlobalStateCleanupHelperParams *p;
563
564
    xmlOnceInit();
565
#if defined(LIBXML_STATIC) && !defined(LIBXML_STATIC_FOR_DLL)
566
    globalval = (xmlGlobalState *) TlsGetValue(globalkey);
567
#else
568
    p = (xmlGlobalStateCleanupHelperParams *) TlsGetValue(globalkey);
569
    globalval = (xmlGlobalState *) (p ? p->memory : NULL);
570
#endif
571
    if (globalval == NULL) {
572
        xmlGlobalState *tsd = xmlNewGlobalState();
573
574
        if (tsd == NULL)
575
      return(NULL);
576
        p = (xmlGlobalStateCleanupHelperParams *)
577
            malloc(sizeof(xmlGlobalStateCleanupHelperParams));
578
  if (p == NULL) {
579
            xmlGenericError(xmlGenericErrorContext,
580
                            "xmlGetGlobalState: out of memory\n");
581
            xmlFreeGlobalState(tsd);
582
      return(NULL);
583
  }
584
        p->memory = tsd;
585
#if defined(LIBXML_STATIC) && !defined(LIBXML_STATIC_FOR_DLL)
586
        DuplicateHandle(GetCurrentProcess(), GetCurrentThread(),
587
                        GetCurrentProcess(), &p->thread, 0, TRUE,
588
                        DUPLICATE_SAME_ACCESS);
589
        TlsSetValue(globalkey, tsd);
590
        _beginthread(xmlGlobalStateCleanupHelper, 0, p);
591
#else
592
        EnterCriticalSection(&cleanup_helpers_cs);
593
        if (cleanup_helpers_head != NULL) {
594
            cleanup_helpers_head->prev = p;
595
        }
596
        p->next = cleanup_helpers_head;
597
        p->prev = NULL;
598
        cleanup_helpers_head = p;
599
        TlsSetValue(globalkey, p);
600
        LeaveCriticalSection(&cleanup_helpers_cs);
601
#endif
602
603
        return (tsd);
604
    }
605
    return (globalval);
606
#endif /* HAVE_COMPILER_TLS */
607
#else
608
    return (NULL);
609
#endif
610
0
}
611
612
/************************************************************************
613
 *                  *
614
 *      Library wide thread interfaces      *
615
 *                  *
616
 ************************************************************************/
617
618
/**
619
 * xmlGetThreadId:
620
 *
621
 * xmlGetThreadId() find the current thread ID number
622
 * Note that this is likely to be broken on some platforms using pthreads
623
 * as the specification doesn't mandate pthread_t to be an integer type
624
 *
625
 * Returns the current thread ID number
626
 */
627
int
628
xmlGetThreadId(void)
629
0
{
630
0
#ifdef HAVE_POSIX_THREADS
631
0
    pthread_t id;
632
0
    int ret;
633
634
0
    if (libxml_is_threaded == 0)
635
0
        return (0);
636
0
    id = pthread_self();
637
    /* horrible but preserves compat, see warning above */
638
0
    memcpy(&ret, &id, sizeof(ret));
639
0
    return (ret);
640
#elif defined HAVE_WIN32_THREADS
641
    return GetCurrentThreadId();
642
#else
643
    return ((int) 0);
644
#endif
645
0
}
646
647
/**
648
 * xmlIsMainThread:
649
 *
650
 * xmlIsMainThread() check whether the current thread is the main thread.
651
 *
652
 * Returns 1 if the current thread is the main thread, 0 otherwise
653
 */
654
int
655
xmlIsMainThread(void)
656
391M
{
657
391M
#ifdef HAVE_POSIX_THREADS
658
391M
    if (libxml_is_threaded == -1)
659
0
        xmlInitThreads();
660
391M
    if (libxml_is_threaded == 0)
661
0
        return (1);
662
391M
    pthread_once(&once_control, xmlOnceInit);
663
#elif defined HAVE_WIN32_THREADS
664
    xmlOnceInit();
665
#endif
666
667
#ifdef DEBUG_THREADS
668
    xmlGenericError(xmlGenericErrorContext, "xmlIsMainThread()\n");
669
#endif
670
391M
#ifdef HAVE_POSIX_THREADS
671
391M
    return (pthread_equal(mainthread,pthread_self()));
672
#elif defined HAVE_WIN32_THREADS
673
    return (mainthread == GetCurrentThreadId());
674
#else
675
    return (1);
676
#endif
677
391M
}
678
679
/**
680
 * xmlLockLibrary:
681
 *
682
 * xmlLockLibrary() is used to take out a re-entrant lock on the libxml2
683
 * library.
684
 */
685
void
686
xmlLockLibrary(void)
687
0
{
688
#ifdef DEBUG_THREADS
689
    xmlGenericError(xmlGenericErrorContext, "xmlLockLibrary()\n");
690
#endif
691
0
    xmlRMutexLock(xmlLibraryLock);
692
0
}
693
694
/**
695
 * xmlUnlockLibrary:
696
 *
697
 * xmlUnlockLibrary() is used to release a re-entrant lock on the libxml2
698
 * library.
699
 */
700
void
701
xmlUnlockLibrary(void)
702
0
{
703
#ifdef DEBUG_THREADS
704
    xmlGenericError(xmlGenericErrorContext, "xmlUnlockLibrary()\n");
705
#endif
706
0
    xmlRMutexUnlock(xmlLibraryLock);
707
0
}
708
709
/**
710
 * xmlInitThreads:
711
 *
712
 * DEPRECATED: This function will be made private. Call xmlInitParser to
713
 * initialize the library.
714
 *
715
 * xmlInitThreads() is used to to initialize all the thread related
716
 * data of the libxml2 library.
717
 */
718
void
719
xmlInitThreads(void)
720
3.11k
{
721
3.11k
#ifdef HAVE_POSIX_THREADS
722
3.11k
#ifdef XML_PTHREAD_WEAK
723
3.11k
    if (libxml_is_threaded == -1) {
724
3.11k
        if ((pthread_once != NULL) &&
725
3.11k
            (pthread_getspecific != NULL) &&
726
3.11k
            (pthread_setspecific != NULL) &&
727
3.11k
            (pthread_key_create != NULL) &&
728
3.11k
            (pthread_key_delete != NULL) &&
729
3.11k
            (pthread_mutex_init != NULL) &&
730
3.11k
            (pthread_mutex_destroy != NULL) &&
731
3.11k
            (pthread_mutex_lock != NULL) &&
732
3.11k
            (pthread_mutex_unlock != NULL) &&
733
3.11k
            (pthread_cond_init != NULL) &&
734
3.11k
            (pthread_cond_destroy != NULL) &&
735
3.11k
            (pthread_cond_wait != NULL) &&
736
3.11k
            (pthread_equal != NULL) &&
737
3.11k
            (pthread_self != NULL) &&
738
3.11k
            (pthread_cond_signal != NULL)) {
739
3.11k
            libxml_is_threaded = 1;
740
741
/* fprintf(stderr, "Running multithreaded\n"); */
742
3.11k
        } else {
743
744
/* fprintf(stderr, "Running without multithread\n"); */
745
0
            libxml_is_threaded = 0;
746
0
        }
747
3.11k
    }
748
3.11k
#endif /* XML_PTHREAD_WEAK */
749
3.11k
#endif
750
3.11k
}
751
752
/**
753
 * xmlCleanupThreads:
754
 *
755
 * DEPRECATED: This function will be made private. Call xmlCleanupParser
756
 * to free global state but see the warnings there. xmlCleanupParser
757
 * should be only called once at program exit. In most cases, you don't
758
 * have call cleanup functions at all.
759
 *
760
 * xmlCleanupThreads() is used to to cleanup all the thread related
761
 * data of the libxml2 library once processing has ended.
762
 *
763
 * WARNING: if your application is multithreaded or has plugin support
764
 *          calling this may crash the application if another thread or
765
 *          a plugin is still using libxml2. It's sometimes very hard to
766
 *          guess if libxml2 is in use in the application, some libraries
767
 *          or plugins may use it without notice. In case of doubt abstain
768
 *          from calling this function or do it just before calling exit()
769
 *          to avoid leak reports from valgrind !
770
 */
771
void
772
xmlCleanupThreads(void)
773
0
{
774
#ifdef DEBUG_THREADS
775
    xmlGenericError(xmlGenericErrorContext, "xmlCleanupThreads()\n");
776
#endif
777
0
#ifdef HAVE_POSIX_THREADS
778
0
    if (libxml_is_threaded != 0)
779
0
        pthread_key_delete(globalkey);
780
0
    once_control = once_control_init;
781
#elif defined(HAVE_WIN32_THREADS)
782
#if !defined(HAVE_COMPILER_TLS)
783
    if (globalkey != TLS_OUT_OF_INDEXES) {
784
#if !defined(LIBXML_STATIC) || defined(LIBXML_STATIC_FOR_DLL)
785
        xmlGlobalStateCleanupHelperParams *p;
786
787
        EnterCriticalSection(&cleanup_helpers_cs);
788
        p = cleanup_helpers_head;
789
        while (p != NULL) {
790
            xmlGlobalStateCleanupHelperParams *temp = p;
791
792
            p = p->next;
793
            xmlFreeGlobalState(temp->memory);
794
            free(temp);
795
        }
796
        cleanup_helpers_head = 0;
797
        LeaveCriticalSection(&cleanup_helpers_cs);
798
#endif
799
        TlsFree(globalkey);
800
        globalkey = TLS_OUT_OF_INDEXES;
801
    }
802
#if !defined(LIBXML_STATIC) || defined(LIBXML_STATIC_FOR_DLL)
803
    DeleteCriticalSection(&cleanup_helpers_cs);
804
#endif
805
#endif
806
    run_once.done = 0;
807
    run_once.control = 0;
808
#endif
809
0
}
810
811
#ifdef LIBXML_THREAD_ENABLED
812
813
/**
814
 * xmlOnceInit
815
 *
816
 * xmlOnceInit() is used to initialize the value of mainthread for use
817
 * in other routines. This function should only be called using
818
 * pthread_once() in association with the once_control variable to ensure
819
 * that the function is only called once. See man pthread_once for more
820
 * details.
821
 */
822
static void
823
xmlOnceInit(void)
824
3.11k
{
825
3.11k
#ifdef HAVE_POSIX_THREADS
826
3.11k
    (void) pthread_key_create(&globalkey, xmlFreeGlobalState);
827
3.11k
    mainthread = pthread_self();
828
3.11k
    __xmlInitializeDict();
829
#elif defined(HAVE_WIN32_THREADS)
830
    if (!run_once.done) {
831
        if (InterlockedIncrement(&run_once.control) == 1) {
832
#if !defined(HAVE_COMPILER_TLS)
833
#if !defined(LIBXML_STATIC) || defined(LIBXML_STATIC_FOR_DLL)
834
            InitializeCriticalSection(&cleanup_helpers_cs);
835
#endif
836
            globalkey = TlsAlloc();
837
#endif
838
            mainthread = GetCurrentThreadId();
839
      __xmlInitializeDict();
840
            run_once.done = 1;
841
        } else {
842
            /* Another thread is working; give up our slice and
843
             * wait until they're done. */
844
            while (!run_once.done)
845
                Sleep(0);
846
        }
847
    }
848
#endif
849
3.11k
}
850
#endif
851
852
/**
853
 * DllMain:
854
 * @hinstDLL: handle to DLL instance
855
 * @fdwReason: Reason code for entry
856
 * @lpvReserved: generic pointer (depends upon reason code)
857
 *
858
 * Entry point for Windows library. It is being used to free thread-specific
859
 * storage.
860
 *
861
 * Returns TRUE always
862
 */
863
#ifdef HAVE_POSIX_THREADS
864
#elif defined(HAVE_WIN32_THREADS) && !defined(HAVE_COMPILER_TLS) && (!defined(LIBXML_STATIC) || defined(LIBXML_STATIC_FOR_DLL))
865
#if defined(LIBXML_STATIC_FOR_DLL)
866
int XMLCALL
867
xmlDllMain(ATTRIBUTE_UNUSED void *hinstDLL, unsigned long fdwReason,
868
           ATTRIBUTE_UNUSED void *lpvReserved)
869
#else
870
/* declare to avoid "no previous prototype for 'DllMain'" warning */
871
/* Note that we do NOT want to include this function declaration in
872
   a public header because it's meant to be called by Windows itself,
873
   not a program that uses this library.  This also has to be exported. */
874
875
XMLPUBFUN BOOL WINAPI
876
DllMain (HINSTANCE hinstDLL,
877
         DWORD     fdwReason,
878
         LPVOID    lpvReserved);
879
880
BOOL WINAPI
881
DllMain(ATTRIBUTE_UNUSED HINSTANCE hinstDLL, DWORD fdwReason,
882
        ATTRIBUTE_UNUSED LPVOID lpvReserved)
883
#endif
884
{
885
    switch (fdwReason) {
886
        case DLL_THREAD_DETACH:
887
            if (globalkey != TLS_OUT_OF_INDEXES) {
888
                xmlGlobalState *globalval = NULL;
889
                xmlGlobalStateCleanupHelperParams *p =
890
                    (xmlGlobalStateCleanupHelperParams *)
891
                    TlsGetValue(globalkey);
892
                globalval = (xmlGlobalState *) (p ? p->memory : NULL);
893
                if (globalval) {
894
                    xmlFreeGlobalState(globalval);
895
                    TlsSetValue(globalkey, NULL);
896
                }
897
                if (p) {
898
                    EnterCriticalSection(&cleanup_helpers_cs);
899
                    if (p == cleanup_helpers_head)
900
                        cleanup_helpers_head = p->next;
901
                    else
902
                        p->prev->next = p->next;
903
                    if (p->next != NULL)
904
                        p->next->prev = p->prev;
905
                    LeaveCriticalSection(&cleanup_helpers_cs);
906
                    free(p);
907
                }
908
            }
909
            break;
910
    }
911
    return TRUE;
912
}
913
#endif