/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 |