blob: cde26f4c9702586490f21be2716bc53dbf1a05c0 [file] [log] [blame]
Daniel Veillardb8478642001-10-12 17:29:10 +00001/**
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#include "libxml.h"
11
12#include <string.h>
13
14#include <libxml/threads.h>
15#include <libxml/globals.h>
16
17#ifdef HAVE_SYS_TYPES_H
18#include <sys/types.h>
19#endif
20#ifdef HAVE_UNISTD_H
21#include <unistd.h>
22#endif
23#ifdef HAVE_STDLIB_H
24#include <stdlib.h>
25#endif
26#ifdef HAVE_PTHREAD_H
27#include <pthread.h>
28#endif
29
30#if defined(SOLARIS)
31#include <note.h>
32#endif
33
Daniel Veillard6f350292001-10-14 09:56:15 +000034/* #define DEBUG_THREADS */
Daniel Veillardb8478642001-10-12 17:29:10 +000035
36/*
37 * TODO: this module still uses malloc/free and not xmlMalloc/xmlFree
38 * to avoid some crazyness since xmlMalloc/xmlFree may actually
39 * be hosted on allocated blocks needing them for the allocation ...
40 */
41
42/*
43 * xmlMutex are a simple mutual exception locks
44 */
45struct _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 */
56struct _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 * - keylock protecting globalkey
72 * - keyonce to mark initialization of globalkey
73 */
Daniel Veillard6f350292001-10-14 09:56:15 +000074
75static int initialized = 0;
Daniel Veillardb8478642001-10-12 17:29:10 +000076#ifdef HAVE_PTHREAD_H
Daniel Veillard5ee57fc2001-10-15 10:46:16 +000077static pthread_mutex_t keylock = PTHREAD_MUTEX_INITIALIZER;
Daniel Veillardb8478642001-10-12 17:29:10 +000078static pthread_key_t globalkey;
79static int keyonce = 0;
Daniel Veillard6f350292001-10-14 09:56:15 +000080static pthread_t mainthread;
Daniel Veillardb8478642001-10-12 17:29:10 +000081#endif
82static xmlRMutexPtr xmlLibraryLock = NULL;
83
84#if defined(SOLARIS)
85NOTE(DATA_READABLE_WITHOUT_LOCK(keyonce))
86#endif
87
88/**
89 * xmlMutexPtr:
90 *
91 * xmlNewMutex() is used to allocate a libxml2 token struct for use in
92 * synchronizing access to data.
93 *
94 * Returns a new simple mutex pointer or NULL in case of error
95 */
96xmlMutexPtr
97xmlNewMutex(void)
98{
99 xmlMutexPtr tok;
100
101 if ((tok = malloc(sizeof(xmlMutex))) == NULL)
102 return (NULL);
103#ifdef HAVE_PTHREAD_H
104 pthread_mutex_init(&tok->lock, NULL);
105#endif
106 return (tok);
107}
108
109/**
110 * xmlFreeMutex:
111 * @tok: the simple mutex
112 *
113 * xmlFreeMutex() is used to reclaim resources associated with a libxml2 token
114 * struct.
115 */
116void
117xmlFreeMutex(xmlMutexPtr tok)
118{
119#ifdef HAVE_PTHREAD_H
120 pthread_mutex_destroy(&tok->lock);
121#endif
122 free(tok);
123}
124
125/**
126 * xmlMutexLock:
127 * @tok: the simple mutex
128 *
129 * xmlMutexLock() is used to lock a libxml2 token.
130 */
131void
132xmlMutexLock(xmlMutexPtr tok)
133{
134#ifdef HAVE_PTHREAD_H
135 pthread_mutex_lock(&tok->lock);
136#endif
137
138}
139
140/**
141 * xmlMutexUnlock:
142 * @tok: the simple mutex
143 *
144 * xmlMutexUnlock() is used to unlock a libxml2 token.
145 */
146void
147xmlMutexUnlock(xmlMutexPtr tok)
148{
149#ifdef HAVE_PTHREAD_H
150 pthread_mutex_unlock(&tok->lock);
151#endif
152}
153
154/**
155 * xmlRNewMutex:
156 *
157 * xmlRNewMutex() is used to allocate a reentrant mutex for use in
158 * synchronizing access to data. token_r is a re-entrant lock and thus useful
159 * for synchronizing access to data structures that may be manipulated in a
160 * recursive fashion.
161 *
162 * Returns the new reentrant mutex pointer or NULL in case of error
163 */
164xmlRMutexPtr
165xmlNewRMutex(void)
166{
167 xmlRMutexPtr tok;
168
169 if ((tok = malloc(sizeof(xmlRMutex))) == NULL)
170 return (NULL);
171#ifdef HAVE_PTHREAD_H
172 pthread_mutex_init(&tok->lock, NULL);
173 tok->held = 0;
174 tok->waiters = 0;
175#endif
176 return (tok);
177}
178
179/**
180 * xmlRFreeMutex:
181 * @tok: the reentrant mutex
182 *
183 * xmlRFreeMutex() is used to reclaim resources associated with a
184 * reentrant mutex.
185 */
186void
187xmlFreeRMutex(xmlRMutexPtr tok)
188{
189#ifdef HAVE_PTHREAD_H
190 pthread_mutex_destroy(&tok->lock);
191#endif
192 free(tok);
193}
194
195/**
196 * xmlRMutexLock:
197 * @tok: the reentrant mutex
198 *
199 * xmlRMutexLock() is used to lock a libxml2 token_r.
200 */
201void
202xmlRMutexLock(xmlRMutexPtr tok)
203{
204#ifdef HAVE_PTHREAD_H
205 pthread_mutex_lock(&tok->lock);
206 if (tok->held) {
207 if (pthread_equal(tok->tid, pthread_self())) {
208 tok->held++;
209 pthread_mutex_unlock(&tok->lock);
210 return;
211 } else {
212 tok->waiters++;
213 while (tok->held)
214 pthread_cond_wait(&tok->cv, &tok->lock);
215 tok->waiters--;
216 }
217 }
218 tok->tid = pthread_self();
219 tok->held = 1;
220 pthread_mutex_unlock(&tok->lock);
221#endif
222}
223
224/**
225 * xmlRMutexUnlock:
226 * @tok: the reentrant mutex
227 *
228 * xmlRMutexUnlock() is used to unlock a libxml2 token_r.
229 */
230void
231xmlRMutexUnlock(xmlRMutexPtr tok)
232{
233#ifdef HAVE_PTHREAD_H
234 pthread_mutex_lock(&tok->lock);
235 tok->held--;
236 if (tok->held == 0) {
237 if (tok->waiters)
238 pthread_cond_signal(&tok->cv);
239 tok->tid = 0;
240 }
241 pthread_mutex_unlock(&tok->lock);
242#endif
243}
244
245/************************************************************************
246 * *
247 * Per thread global state handling *
248 * *
249 ************************************************************************/
250
Daniel Veillard8bdb91d2001-10-31 17:52:43 +0000251#ifdef LIBXML_THREAD_ENABLED
Daniel Veillardb8478642001-10-12 17:29:10 +0000252/**
253 * xmlFreeGlobalState:
254 * @state: a thread global state
255 *
256 * xmlFreeGlobalState() is called when a thread terminates with a non-NULL
257 * global state. It is is used here to reclaim memory resources.
258 */
259static void
260xmlFreeGlobalState(void *state)
261{
262 free(state);
263}
264
265/**
266 * xmlNewGlobalState:
267 *
268 * xmlNewGlobalState() allocates a global state. This structure is used to
269 * hold all data for use by a thread when supporting backwards compatibility
270 * of libmxml2 to pre-thread-safe behaviour.
271 *
272 * Returns the newly allocated xmlGlobalStatePtr or NULL in case of error
273 */
274static xmlGlobalStatePtr
275xmlNewGlobalState(void)
276{
277 xmlGlobalState *gs;
278
279 gs = malloc(sizeof(xmlGlobalState));
280 if (gs == NULL)
281 return(NULL);
282
283 memset(gs, 0, sizeof(gs));
284 xmlInitializeGlobalState(gs);
285 return (gs);
286}
Daniel Veillard8bdb91d2001-10-31 17:52:43 +0000287#endif /* LIBXML_THREAD_ENABLED */
Daniel Veillardb8478642001-10-12 17:29:10 +0000288
289
290/**
291 * xmlGetGlobalState:
292 *
293 * xmlGetGlobalState() is called to retrieve the global state for a thread.
294 * keyonce will only be set once during a library invocation and is used
295 * to create globalkey, the key used to store each thread's TSD.
296 *
297 * Note: it should not be called for the "main" thread as this thread uses
298 * the existing global variables defined in the library.
299 *
300 * Returns the thread global state or NULL in case of error
301 */
302xmlGlobalStatePtr
303xmlGetGlobalState(void)
304{
305#ifdef HAVE_PTHREAD_H
306 xmlGlobalState *globalval;
307
308 if (keyonce == 0) {
309 (void) pthread_mutex_lock(&keylock);
310 if (keyonce == 0) {
311 keyonce++;
312 (void) pthread_key_create(&globalkey, xmlFreeGlobalState);
313 }
314 (void) pthread_mutex_unlock(&keylock);
315 }
316 if ((globalval = (xmlGlobalState *)
317 pthread_getspecific(globalkey)) == NULL) {
318 xmlGlobalState *tsd = xmlNewGlobalState();
319
320 pthread_setspecific(globalkey, tsd);
321 return (tsd);
Daniel Veillard6f350292001-10-14 09:56:15 +0000322 }
323 return (globalval);
324#else
325 return(NULL);
Daniel Veillardb8478642001-10-12 17:29:10 +0000326#endif
327}
328
329
330/************************************************************************
331 * *
332 * Library wide thread interfaces *
333 * *
334 ************************************************************************/
335
336/**
Daniel Veillard3c01b1d2001-10-17 15:58:35 +0000337 * xmlGetThreadId:
338 *
339 * xmlGetThreadId() find the current thread ID number
340 *
341 * Returns the current thread ID number
342 */
343int
344xmlGetThreadId(void)
345{
346#ifdef HAVE_PTHREAD_H
347 return((int) pthread_self());
348#else
349 return((int) 0);
350#endif
351}
352
353/**
Daniel Veillard6f350292001-10-14 09:56:15 +0000354 * xmlIsMainThread:
355 *
356 * xmlIsMainThread() check wether the current thread is the main thread.
357 *
358 * Returns 1 if the current thread is the main thread, 0 otherwise
359 */
360int
361xmlIsMainThread(void)
362{
363 if (!initialized)
364 xmlInitThreads();
365
366#ifdef DEBUG_THREADS
367 xmlGenericError(xmlGenericErrorContext, "xmlIsMainThread()\n");
368#endif
369#ifdef HAVE_PTHREAD_H
370 return(mainthread == pthread_self());
371#else
372 return(1);
373#endif
374}
375
376/**
Daniel Veillardb8478642001-10-12 17:29:10 +0000377 * xmlLockLibrary:
378 *
379 * xmlLockLibrary() is used to take out a re-entrant lock on the libxml2
380 * library.
381 */
382void
383xmlLockLibrary(void)
384{
Daniel Veillard6f350292001-10-14 09:56:15 +0000385#ifdef DEBUG_THREADS
386 xmlGenericError(xmlGenericErrorContext, "xmlLockLibrary()\n");
387#endif
Daniel Veillardb8478642001-10-12 17:29:10 +0000388 xmlRMutexLock(xmlLibraryLock);
389}
390
391/**
392 * xmlUnlockLibrary:
393 *
394 * xmlUnlockLibrary() is used to release a re-entrant lock on the libxml2
395 * library.
396 */
397void
398xmlUnlockLibrary(void)
399{
Daniel Veillard6f350292001-10-14 09:56:15 +0000400#ifdef DEBUG_THREADS
401 xmlGenericError(xmlGenericErrorContext, "xmlUnlockLibrary()\n");
402#endif
Daniel Veillardb8478642001-10-12 17:29:10 +0000403 xmlRMutexUnlock(xmlLibraryLock);
404}
405
406/**
407 * xmlInitThreads:
408 *
409 * xmlInitThreads() is used to to initialize all the thread related
410 * data of the libxml2 library.
411 */
412void
413xmlInitThreads(void)
414{
Daniel Veillard6f350292001-10-14 09:56:15 +0000415 if (initialized != 0)
416 return;
417
418#ifdef DEBUG_THREADS
419 xmlGenericError(xmlGenericErrorContext, "xmlInitThreads()\n");
420#endif
421
422#ifdef HAVE_PTHREAD_H
423 mainthread = pthread_self();
424#endif
425
426 initialized = 1;
Daniel Veillardb8478642001-10-12 17:29:10 +0000427}
428
429/**
430 * xmlCleanupThreads:
431 *
432 * xmlCleanupThreads() is used to to cleanup all the thread related
433 * data of the libxml2 library once processing has ended.
434 */
435void
436xmlCleanupThreads(void)
437{
Daniel Veillard6f350292001-10-14 09:56:15 +0000438 if (initialized == 0)
439 return;
440
441#ifdef DEBUG_THREADS
442 xmlGenericError(xmlGenericErrorContext, "xmlCleanupThreads()\n");
443#endif
444
445 initialized = 0;
Daniel Veillardb8478642001-10-12 17:29:10 +0000446}