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