blob: be3b13162860271ac7142bc5736ebe57a0b6c375 [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
Daniel Veillardb8478642001-10-12 17:29:10 +000071 */
Daniel Veillard6f350292001-10-14 09:56:15 +000072
Daniel Veillardb8478642001-10-12 17:29:10 +000073#ifdef HAVE_PTHREAD_H
Daniel Veillardb8478642001-10-12 17:29:10 +000074static pthread_key_t globalkey;
Daniel Veillard6f350292001-10-14 09:56:15 +000075static pthread_t mainthread;
Daniel Veillarde28313b2001-12-06 14:08:31 +000076static pthread_once_t once_control = PTHREAD_ONCE_INIT;
Daniel Veillardb8478642001-10-12 17:29:10 +000077#endif
78static xmlRMutexPtr xmlLibraryLock = NULL;
Daniel Veillarde28313b2001-12-06 14:08:31 +000079static void xmlOnceInit(void);
Daniel Veillardb8478642001-10-12 17:29:10 +000080
81/**
82 * xmlMutexPtr:
83 *
84 * xmlNewMutex() is used to allocate a libxml2 token struct for use in
85 * synchronizing access to data.
86 *
87 * Returns a new simple mutex pointer or NULL in case of error
88 */
89xmlMutexPtr
90xmlNewMutex(void)
91{
92 xmlMutexPtr tok;
93
94 if ((tok = malloc(sizeof(xmlMutex))) == NULL)
95 return (NULL);
96#ifdef HAVE_PTHREAD_H
97 pthread_mutex_init(&tok->lock, NULL);
98#endif
99 return (tok);
100}
101
102/**
103 * xmlFreeMutex:
104 * @tok: the simple mutex
105 *
106 * xmlFreeMutex() is used to reclaim resources associated with a libxml2 token
107 * struct.
108 */
109void
110xmlFreeMutex(xmlMutexPtr tok)
111{
112#ifdef HAVE_PTHREAD_H
113 pthread_mutex_destroy(&tok->lock);
114#endif
115 free(tok);
116}
117
118/**
119 * xmlMutexLock:
120 * @tok: the simple mutex
121 *
122 * xmlMutexLock() is used to lock a libxml2 token.
123 */
124void
125xmlMutexLock(xmlMutexPtr tok)
126{
127#ifdef HAVE_PTHREAD_H
128 pthread_mutex_lock(&tok->lock);
129#endif
130
131}
132
133/**
134 * xmlMutexUnlock:
135 * @tok: the simple mutex
136 *
137 * xmlMutexUnlock() is used to unlock a libxml2 token.
138 */
139void
140xmlMutexUnlock(xmlMutexPtr tok)
141{
142#ifdef HAVE_PTHREAD_H
143 pthread_mutex_unlock(&tok->lock);
144#endif
145}
146
147/**
148 * xmlRNewMutex:
149 *
150 * xmlRNewMutex() is used to allocate a reentrant mutex for use in
151 * synchronizing access to data. token_r is a re-entrant lock and thus useful
152 * for synchronizing access to data structures that may be manipulated in a
153 * recursive fashion.
154 *
155 * Returns the new reentrant mutex pointer or NULL in case of error
156 */
157xmlRMutexPtr
158xmlNewRMutex(void)
159{
160 xmlRMutexPtr tok;
161
162 if ((tok = malloc(sizeof(xmlRMutex))) == NULL)
163 return (NULL);
164#ifdef HAVE_PTHREAD_H
165 pthread_mutex_init(&tok->lock, NULL);
166 tok->held = 0;
167 tok->waiters = 0;
168#endif
169 return (tok);
170}
171
172/**
173 * xmlRFreeMutex:
174 * @tok: the reentrant mutex
175 *
176 * xmlRFreeMutex() is used to reclaim resources associated with a
177 * reentrant mutex.
178 */
179void
180xmlFreeRMutex(xmlRMutexPtr tok)
181{
182#ifdef HAVE_PTHREAD_H
183 pthread_mutex_destroy(&tok->lock);
184#endif
185 free(tok);
186}
187
188/**
189 * xmlRMutexLock:
190 * @tok: the reentrant mutex
191 *
192 * xmlRMutexLock() is used to lock a libxml2 token_r.
193 */
194void
195xmlRMutexLock(xmlRMutexPtr tok)
196{
197#ifdef HAVE_PTHREAD_H
198 pthread_mutex_lock(&tok->lock);
199 if (tok->held) {
200 if (pthread_equal(tok->tid, pthread_self())) {
201 tok->held++;
202 pthread_mutex_unlock(&tok->lock);
203 return;
204 } else {
205 tok->waiters++;
206 while (tok->held)
207 pthread_cond_wait(&tok->cv, &tok->lock);
208 tok->waiters--;
209 }
210 }
211 tok->tid = pthread_self();
212 tok->held = 1;
213 pthread_mutex_unlock(&tok->lock);
214#endif
215}
216
217/**
218 * xmlRMutexUnlock:
219 * @tok: the reentrant mutex
220 *
221 * xmlRMutexUnlock() is used to unlock a libxml2 token_r.
222 */
223void
224xmlRMutexUnlock(xmlRMutexPtr tok)
225{
226#ifdef HAVE_PTHREAD_H
227 pthread_mutex_lock(&tok->lock);
228 tok->held--;
229 if (tok->held == 0) {
230 if (tok->waiters)
231 pthread_cond_signal(&tok->cv);
232 tok->tid = 0;
233 }
234 pthread_mutex_unlock(&tok->lock);
235#endif
236}
237
238/************************************************************************
239 * *
240 * Per thread global state handling *
241 * *
242 ************************************************************************/
243
Daniel Veillard8bdb91d2001-10-31 17:52:43 +0000244#ifdef LIBXML_THREAD_ENABLED
Daniel Veillardb8478642001-10-12 17:29:10 +0000245/**
246 * xmlFreeGlobalState:
247 * @state: a thread global state
248 *
249 * xmlFreeGlobalState() is called when a thread terminates with a non-NULL
250 * global state. It is is used here to reclaim memory resources.
251 */
252static void
253xmlFreeGlobalState(void *state)
254{
255 free(state);
256}
257
258/**
259 * xmlNewGlobalState:
260 *
261 * xmlNewGlobalState() allocates a global state. This structure is used to
262 * hold all data for use by a thread when supporting backwards compatibility
Daniel Veillardcbaf3992001-12-31 16:16:02 +0000263 * of libxml2 to pre-thread-safe behaviour.
Daniel Veillardb8478642001-10-12 17:29:10 +0000264 *
265 * Returns the newly allocated xmlGlobalStatePtr or NULL in case of error
266 */
267static xmlGlobalStatePtr
268xmlNewGlobalState(void)
269{
270 xmlGlobalState *gs;
271
272 gs = malloc(sizeof(xmlGlobalState));
273 if (gs == NULL)
274 return(NULL);
275
276 memset(gs, 0, sizeof(gs));
277 xmlInitializeGlobalState(gs);
278 return (gs);
279}
Daniel Veillard8bdb91d2001-10-31 17:52:43 +0000280#endif /* LIBXML_THREAD_ENABLED */
Daniel Veillardb8478642001-10-12 17:29:10 +0000281
282
283/**
284 * xmlGetGlobalState:
285 *
286 * xmlGetGlobalState() is called to retrieve the global state for a thread.
Daniel Veillardb8478642001-10-12 17:29:10 +0000287 *
288 * Returns the thread global state or NULL in case of error
289 */
290xmlGlobalStatePtr
291xmlGetGlobalState(void)
292{
293#ifdef HAVE_PTHREAD_H
294 xmlGlobalState *globalval;
295
Daniel Veillarde28313b2001-12-06 14:08:31 +0000296 pthread_once(&once_control, xmlOnceInit);
297
Daniel Veillardb8478642001-10-12 17:29:10 +0000298 if ((globalval = (xmlGlobalState *)
299 pthread_getspecific(globalkey)) == NULL) {
300 xmlGlobalState *tsd = xmlNewGlobalState();
301
302 pthread_setspecific(globalkey, tsd);
303 return (tsd);
Daniel Veillard6f350292001-10-14 09:56:15 +0000304 }
305 return (globalval);
306#else
307 return(NULL);
Daniel Veillardb8478642001-10-12 17:29:10 +0000308#endif
309}
310
311
312/************************************************************************
313 * *
314 * Library wide thread interfaces *
315 * *
316 ************************************************************************/
317
318/**
Daniel Veillard3c01b1d2001-10-17 15:58:35 +0000319 * xmlGetThreadId:
320 *
321 * xmlGetThreadId() find the current thread ID number
322 *
323 * Returns the current thread ID number
324 */
325int
326xmlGetThreadId(void)
327{
328#ifdef HAVE_PTHREAD_H
329 return((int) pthread_self());
330#else
331 return((int) 0);
332#endif
333}
334
335/**
Daniel Veillard6f350292001-10-14 09:56:15 +0000336 * xmlIsMainThread:
337 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +0000338 * xmlIsMainThread() check whether the current thread is the main thread.
Daniel Veillard6f350292001-10-14 09:56:15 +0000339 *
340 * Returns 1 if the current thread is the main thread, 0 otherwise
341 */
342int
343xmlIsMainThread(void)
344{
Daniel Veillarde28313b2001-12-06 14:08:31 +0000345#ifdef HAVE_PTHREAD_H
346 pthread_once(&once_control, xmlOnceInit);
347#endif
Daniel Veillard6f350292001-10-14 09:56:15 +0000348
349#ifdef DEBUG_THREADS
350 xmlGenericError(xmlGenericErrorContext, "xmlIsMainThread()\n");
351#endif
352#ifdef HAVE_PTHREAD_H
353 return(mainthread == pthread_self());
354#else
355 return(1);
356#endif
357}
358
359/**
Daniel Veillardb8478642001-10-12 17:29:10 +0000360 * xmlLockLibrary:
361 *
362 * xmlLockLibrary() is used to take out a re-entrant lock on the libxml2
363 * library.
364 */
365void
366xmlLockLibrary(void)
367{
Daniel Veillard6f350292001-10-14 09:56:15 +0000368#ifdef DEBUG_THREADS
369 xmlGenericError(xmlGenericErrorContext, "xmlLockLibrary()\n");
370#endif
Daniel Veillardb8478642001-10-12 17:29:10 +0000371 xmlRMutexLock(xmlLibraryLock);
372}
373
374/**
375 * xmlUnlockLibrary:
376 *
377 * xmlUnlockLibrary() is used to release a re-entrant lock on the libxml2
378 * library.
379 */
380void
381xmlUnlockLibrary(void)
382{
Daniel Veillard6f350292001-10-14 09:56:15 +0000383#ifdef DEBUG_THREADS
384 xmlGenericError(xmlGenericErrorContext, "xmlUnlockLibrary()\n");
385#endif
Daniel Veillardb8478642001-10-12 17:29:10 +0000386 xmlRMutexUnlock(xmlLibraryLock);
387}
388
389/**
390 * xmlInitThreads:
391 *
392 * xmlInitThreads() is used to to initialize all the thread related
393 * data of the libxml2 library.
394 */
395void
396xmlInitThreads(void)
397{
Daniel Veillard6f350292001-10-14 09:56:15 +0000398#ifdef DEBUG_THREADS
399 xmlGenericError(xmlGenericErrorContext, "xmlInitThreads()\n");
400#endif
Daniel Veillardb8478642001-10-12 17:29:10 +0000401}
402
403/**
404 * xmlCleanupThreads:
405 *
406 * xmlCleanupThreads() is used to to cleanup all the thread related
407 * data of the libxml2 library once processing has ended.
408 */
409void
410xmlCleanupThreads(void)
411{
Daniel Veillard6f350292001-10-14 09:56:15 +0000412#ifdef DEBUG_THREADS
413 xmlGenericError(xmlGenericErrorContext, "xmlCleanupThreads()\n");
414#endif
Daniel Veillarde28313b2001-12-06 14:08:31 +0000415}
Daniel Veillard6f350292001-10-14 09:56:15 +0000416
Daniel Veillarde28313b2001-12-06 14:08:31 +0000417/**
418 * xmlOnceInit
419 *
420 * xmlOnceInit() is used to initialize the value of mainthread for use
421 * in other routines. This function should only be called using
422 * pthread_once() in association with the once_control variable to ensure
423 * that the function is only called once. See man pthread_once for more
424 * details.
425 */
426static void
427xmlOnceInit(void) {
428#ifdef HAVE_PTHREAD_H
429 (void) pthread_key_create(&globalkey, xmlFreeGlobalState);
430 mainthread = pthread_self();
431#endif
Daniel Veillardb8478642001-10-12 17:29:10 +0000432}