|
1 /** |
|
2 * libxml2_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 * Portion Copyright © 2009 Nokia Corporation and/or its subsidiary(-ies). All rights reserved. |
|
9 */ |
|
10 #define IN_LIBXML |
|
11 #include "xmlenglibxml.h" |
|
12 |
|
13 #include <string.h> |
|
14 |
|
15 #include <stdapis/libxml2/libxml2_globals.h> |
|
16 |
|
17 #ifdef HAVE_SYS_TYPES_H |
|
18 #include <sys/types.h> |
|
19 #endif |
|
20 |
|
21 #ifdef HAVE_UNISTD_H |
|
22 #include <unistd.h> |
|
23 #endif |
|
24 |
|
25 #ifdef HAVE_STDLIB_H |
|
26 #include <stdlib.h> |
|
27 #endif |
|
28 |
|
29 #ifdef HAVE_PTHREAD_H |
|
30 #include <pthread.h> |
|
31 #endif |
|
32 |
|
33 |
|
34 /* #define DEBUG_THREADS */ |
|
35 |
|
36 /* |
|
37 * |
|
38 * |
|
39 * |
|
40 */ |
|
41 |
|
42 /* |
|
43 * xmlMutex are a simple mutual exception locks |
|
44 */ |
|
45 struct _xmlMutex { |
|
46 #ifdef HAVE_PTHREAD_H |
|
47 pthread_mutex_t lock; |
|
48 #else |
|
49 int empty; |
|
50 #endif |
|
51 }; |
|
52 |
|
53 /* |
|
54 * xmlRMutex are reentrant mutual exception locks |
|
55 */ |
|
56 struct _xmlRMutex { |
|
57 #ifdef HAVE_PTHREAD_H |
|
58 pthread_mutex_t lock; |
|
59 unsigned int held; |
|
60 unsigned int waiters; |
|
61 pthread_t tid; |
|
62 pthread_cond_t cv; |
|
63 #else |
|
64 int empty; |
|
65 #endif |
|
66 }; |
|
67 /* |
|
68 * This module still has some internal static data. |
|
69 * - xmlLibraryLock a global lock |
|
70 * - globalkey used for per-thread data |
|
71 */ |
|
72 |
|
73 #ifdef HAVE_PTHREAD_H |
|
74 static pthread_key_t globalkey; |
|
75 static pthread_t mainthread; |
|
76 static pthread_once_t once_control = PTHREAD_ONCE_INIT; |
|
77 #endif |
|
78 |
|
79 #ifdef LIBXML_THREAD_ENABLED |
|
80 static void xmlOnceInit(void); |
|
81 #endif |
|
82 |
|
83 /** |
|
84 * xmlNewMutex: |
|
85 * |
|
86 * xmlNewMutex() is used to allocate a libxml2 token struct for use in |
|
87 * synchronizing access to data. |
|
88 * |
|
89 * Returns a new simple mutex pointer or NULL in case of error |
|
90 * |
|
91 * OOM: possible --> NULL is returned (OOM flag is NOT SET) |
|
92 */ |
|
93 XMLPUBFUNEXPORT xmlMutexPtr |
|
94 xmlNewMutex(void) |
|
95 { |
|
96 xmlMutexPtr tok; |
|
97 |
|
98 if ((tok = (xmlMutexPtr)malloc(sizeof(xmlMutex))) == NULL) |
|
99 return (NULL); |
|
100 #ifdef HAVE_PTHREAD_H |
|
101 pthread_mutex_init(&tok->lock, NULL); |
|
102 #endif |
|
103 return (tok); |
|
104 } |
|
105 |
|
106 /** |
|
107 * xmlFreeMutex: |
|
108 * @param tok the simple mutex |
|
109 * |
|
110 * xmlFreeMutex() is used to reclaim resources associated with a libxml2 token |
|
111 * struct. |
|
112 */ |
|
113 XMLPUBFUNEXPORT void |
|
114 xmlFreeMutex(xmlMutexPtr tok) |
|
115 { |
|
116 if (tok == NULL) return; |
|
117 |
|
118 #ifdef HAVE_PTHREAD_H |
|
119 pthread_mutex_destroy(&tok->lock); |
|
120 #endif |
|
121 free(tok); |
|
122 } |
|
123 |
|
124 /** |
|
125 * xmlMutexLock: |
|
126 * @param tok the simple mutex |
|
127 * |
|
128 * xmlMutexLock() is used to lock a libxml2 token. |
|
129 */ |
|
130 XMLPUBFUNEXPORT void |
|
131 xmlMutexLock(xmlMutexPtr tok) |
|
132 { |
|
133 if (tok == NULL) |
|
134 return; |
|
135 #ifdef HAVE_PTHREAD_H |
|
136 pthread_mutex_lock(&tok->lock); |
|
137 #endif |
|
138 |
|
139 } |
|
140 |
|
141 /** |
|
142 * xmlMutexUnlock: |
|
143 * @param tok the simple mutex |
|
144 * |
|
145 * xmlMutexUnlock() is used to unlock a libxml2 token. |
|
146 */ |
|
147 XMLPUBFUNEXPORT void |
|
148 xmlMutexUnlock(xmlMutexPtr tok) |
|
149 { |
|
150 if (tok == NULL) |
|
151 return; |
|
152 #ifdef HAVE_PTHREAD_H |
|
153 pthread_mutex_unlock(&tok->lock); |
|
154 #endif |
|
155 } |
|
156 |
|
157 /** |
|
158 * xmlNewRMutex: |
|
159 * |
|
160 * xmlRNewMutex() is used to allocate a reentrant mutex for use in |
|
161 * synchronizing access to data. token_r is a re-entrant lock and thus useful |
|
162 * for synchronizing access to data structures that may be manipulated in a |
|
163 * recursive fashion. |
|
164 * |
|
165 * Returns the new reentrant mutex pointer or NULL in case of error |
|
166 */ |
|
167 XMLPUBFUNEXPORT xmlRMutexPtr |
|
168 xmlNewRMutex(void) |
|
169 { |
|
170 xmlRMutexPtr tok; |
|
171 |
|
172 if ((tok = (xmlRMutexPtr)malloc(sizeof(xmlRMutex))) == NULL) |
|
173 return (NULL); |
|
174 #ifdef HAVE_PTHREAD_H |
|
175 pthread_mutex_init(&tok->lock, NULL); |
|
176 tok->held = 0; |
|
177 tok->waiters = 0; |
|
178 pthread_cond_init(&tok->cv, NULL); |
|
179 #endif |
|
180 return (tok); |
|
181 } |
|
182 |
|
183 #ifndef XMLENGINE_EXCLUDE_UNUSED |
|
184 /** |
|
185 * xmlFreeRMutex: |
|
186 * @param tok the reentrant mutex |
|
187 * |
|
188 * xmlRFreeMutex() is used to reclaim resources associated with a |
|
189 * reentrant mutex. |
|
190 */ |
|
191 void |
|
192 xmlFreeRMutex(xmlRMutexPtr tok ATTRIBUTE_UNUSED) |
|
193 { |
|
194 #ifdef HAVE_PTHREAD_H |
|
195 pthread_mutex_destroy(&tok->lock); |
|
196 #endif |
|
197 free(tok); |
|
198 } |
|
199 #endif /* ifndef XMLENGINE_EXCLUDE_UNUSED */ |
|
200 |
|
201 /** |
|
202 * xmlRMutexLock: |
|
203 * @param tok the reentrant mutex |
|
204 * |
|
205 * xmlRMutexLock() is used to lock a libxml2 token_r. |
|
206 */ |
|
207 XMLPUBFUNEXPORT void |
|
208 xmlRMutexLock(xmlRMutexPtr tok) |
|
209 { |
|
210 if (tok == NULL) |
|
211 return; |
|
212 #ifdef HAVE_PTHREAD_H |
|
213 pthread_mutex_lock(&tok->lock); |
|
214 if (tok->held) { |
|
215 if (pthread_equal(tok->tid, pthread_self())) { |
|
216 tok->held++; |
|
217 pthread_mutex_unlock(&tok->lock); |
|
218 return; |
|
219 } else { |
|
220 tok->waiters++; |
|
221 while (tok->held) |
|
222 pthread_cond_wait(&tok->cv, &tok->lock); |
|
223 tok->waiters--; |
|
224 } |
|
225 } |
|
226 tok->tid = pthread_self(); |
|
227 tok->held = 1; |
|
228 pthread_mutex_unlock(&tok->lock); |
|
229 #endif |
|
230 } |
|
231 |
|
232 /** |
|
233 * xmlRMutexUnlock: |
|
234 * @param tok the reentrant mutex |
|
235 * |
|
236 * xmlRMutexUnlock() is used to unlock a libxml2 token_r. |
|
237 */ |
|
238 XMLPUBFUNEXPORT void |
|
239 xmlRMutexUnlock(xmlRMutexPtr tok ATTRIBUTE_UNUSED) |
|
240 { |
|
241 if (tok == NULL) |
|
242 return; |
|
243 #ifdef HAVE_PTHREAD_H |
|
244 pthread_mutex_lock(&tok->lock); |
|
245 tok->held--; |
|
246 if (tok->held == 0) { |
|
247 if (tok->waiters) |
|
248 pthread_cond_signal(&tok->cv); |
|
249 tok->tid = 0; |
|
250 } |
|
251 pthread_mutex_unlock(&tok->lock); |
|
252 #endif |
|
253 } |
|
254 |
|
255 /************************************************************************* |
|
256 * * |
|
257 * Per thread global state handling * |
|
258 * * |
|
259 ************************************************************************/ |
|
260 |
|
261 #ifdef LIBXML_THREAD_ENABLED |
|
262 /** |
|
263 * xmlFreeGlobalState: |
|
264 * @param state a thread global state |
|
265 * |
|
266 * xmlFreeGlobalState() is called when a thread terminates with a non-NULL |
|
267 * global state. It is is used here to reclaim memory resources. |
|
268 */ |
|
269 static void |
|
270 xmlFreeGlobalState(void *state) |
|
271 { |
|
272 free(state); |
|
273 } |
|
274 /** |
|
275 * xmlNewGlobalState: |
|
276 * |
|
277 * xmlNewGlobalState() allocates a global state. This structure is used to |
|
278 * hold all data for use by a thread when supporting backwards compatibility |
|
279 * of libxml2 to pre-thread-safe behaviour. |
|
280 * |
|
281 * Returns the newly allocated xmlGlobalStatePtr or NULL in case of error |
|
282 */ |
|
283 static xmlGlobalStatePtr |
|
284 xmlNewGlobalState(void) |
|
285 { |
|
286 xmlGlobalState *gs; |
|
287 |
|
288 gs = malloc(sizeof(xmlGlobalState)); |
|
289 if (gs == NULL) |
|
290 return(NULL); |
|
291 |
|
292 memset(gs, 0, sizeof(xmlGlobalState)); |
|
293 xmlInitializeGlobalState(gs); |
|
294 return (gs); |
|
295 } |
|
296 |
|
297 #endif /* LIBXML_THREAD_ENABLED */ |
|
298 |
|
299 |
|
300 /************************************************************************ |
|
301 * * |
|
302 * Library wide thread interfaces * |
|
303 * * |
|
304 ************************************************************************/ |
|
305 |
|
306 /** |
|
307 * xmlGetThreadId: |
|
308 * |
|
309 * xmlGetThreadId() find the current thread ID number |
|
310 * |
|
311 * Returns the current thread ID number |
|
312 */ |
|
313 XMLPUBFUNEXPORT int |
|
314 xmlGetThreadId(void) |
|
315 { |
|
316 #ifdef HAVE_PTHREAD_H |
|
317 return((int) pthread_self()); |
|
318 #else |
|
319 return((int) 0); |
|
320 #endif |
|
321 } |
|
322 |
|
323 /** |
|
324 * xmlIsMainThread: |
|
325 * |
|
326 * xmlIsMainThread() check whether the current thread is the main thread. |
|
327 * |
|
328 * Returns 1 if the current thread is the main thread, 0 otherwise |
|
329 */ |
|
330 XMLPUBFUNEXPORT int |
|
331 xmlIsMainThread(void) |
|
332 { |
|
333 #ifdef HAVE_PTHREAD_H |
|
334 pthread_once(&once_control, xmlOnceInit); |
|
335 #endif |
|
336 |
|
337 #ifdef DEBUG_THREADS |
|
338 xmlGenericError(xmlGenericErrorContext, "xmlIsMainThread()\n"); |
|
339 #endif |
|
340 #ifdef HAVE_PTHREAD_H |
|
341 return(mainthread == pthread_self()); |
|
342 #else |
|
343 return(1); |
|
344 #endif |
|
345 } |
|
346 |
|
347 /** |
|
348 * xmlLockLibrary: |
|
349 * |
|
350 * xmlLockLibrary() is used to take out a re-entrant lock on the libxml2 |
|
351 * library. |
|
352 */ |
|
353 XMLPUBFUNEXPORT void |
|
354 xmlLockLibrary(void) |
|
355 { |
|
356 #ifdef DEBUG_THREADS |
|
357 xmlGenericError(xmlGenericErrorContext, "xmlLockLibrary()\n"); |
|
358 #endif |
|
359 xmlRMutexLock(xmlLibraryLock); |
|
360 } |
|
361 |
|
362 /** |
|
363 * xmlUnlockLibrary: |
|
364 * |
|
365 * xmlUnlockLibrary() is used to release a re-entrant lock on the libxml2 |
|
366 * library. |
|
367 */ |
|
368 XMLPUBFUNEXPORT void |
|
369 xmlUnlockLibrary(void) |
|
370 { |
|
371 #ifdef DEBUG_THREADS |
|
372 xmlGenericError(xmlGenericErrorContext, "xmlUnlockLibrary()\n"); |
|
373 #endif |
|
374 xmlRMutexUnlock(xmlLibraryLock); |
|
375 } |
|
376 |
|
377 /** |
|
378 * xmlInitThreads: |
|
379 * |
|
380 * xmlInitThreads() is used to to initialize all the thread related |
|
381 * data of the libxml2 library. |
|
382 */ |
|
383 XMLPUBFUNEXPORT void |
|
384 xmlInitThreads(void) |
|
385 { |
|
386 #ifdef DEBUG_THREADS |
|
387 xmlGenericError(xmlGenericErrorContext, "xmlInitThreads()\n"); |
|
388 #endif |
|
389 } |
|
390 |
|
391 /** |
|
392 * xmlCleanupThreads: |
|
393 * |
|
394 * xmlCleanupThreads() is used to to cleanup all the thread related |
|
395 * data of the libxml2 library once processing has ended. |
|
396 */ |
|
397 XMLPUBFUNEXPORT void |
|
398 xmlCleanupThreads(void) |
|
399 { |
|
400 #ifdef DEBUG_THREADS |
|
401 xmlGenericError(xmlGenericErrorContext, "xmlCleanupThreads()\n"); |
|
402 #endif |
|
403 } |
|
404 |
|
405 #ifdef LIBXML_THREAD_ENABLED |
|
406 /** |
|
407 * xmlOnceInit |
|
408 * |
|
409 * xmlOnceInit() is used to initialize the value of mainthread for use |
|
410 * in other routines. This function should only be called using |
|
411 * pthread_once() in association with the once_control variable to ensure |
|
412 * that the function is only called once. See man pthread_once for more |
|
413 * details. |
|
414 */ |
|
415 static void |
|
416 xmlOnceInit(void) { |
|
417 #ifdef HAVE_PTHREAD_H |
|
418 (void) pthread_key_create(&globalkey, xmlFreeGlobalState); |
|
419 mainthread = pthread_self(); |
|
420 #endif |
|
421 |
|
422 } |
|
423 |
|
424 #endif |