blob: 49ada346997716552a47a22660b63e88b3798ffb [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 Veillard6f350292001-10-14 09:56:15 +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
251/**
252 * xmlFreeGlobalState:
253 * @state: a thread global state
254 *
255 * xmlFreeGlobalState() is called when a thread terminates with a non-NULL
256 * global state. It is is used here to reclaim memory resources.
257 */
258static void
259xmlFreeGlobalState(void *state)
260{
261 free(state);
262}
263
264/**
265 * xmlNewGlobalState:
266 *
267 * xmlNewGlobalState() allocates a global state. This structure is used to
268 * hold all data for use by a thread when supporting backwards compatibility
269 * of libmxml2 to pre-thread-safe behaviour.
270 *
271 * Returns the newly allocated xmlGlobalStatePtr or NULL in case of error
272 */
273static xmlGlobalStatePtr
274xmlNewGlobalState(void)
275{
276 xmlGlobalState *gs;
277
278 gs = malloc(sizeof(xmlGlobalState));
279 if (gs == NULL)
280 return(NULL);
281
282 memset(gs, 0, sizeof(gs));
283 xmlInitializeGlobalState(gs);
284 return (gs);
285}
286
287
288/**
289 * xmlGetGlobalState:
290 *
291 * xmlGetGlobalState() is called to retrieve the global state for a thread.
292 * keyonce will only be set once during a library invocation and is used
293 * to create globalkey, the key used to store each thread's TSD.
294 *
295 * Note: it should not be called for the "main" thread as this thread uses
296 * the existing global variables defined in the library.
297 *
298 * Returns the thread global state or NULL in case of error
299 */
300xmlGlobalStatePtr
301xmlGetGlobalState(void)
302{
303#ifdef HAVE_PTHREAD_H
304 xmlGlobalState *globalval;
305
306 if (keyonce == 0) {
307 (void) pthread_mutex_lock(&keylock);
308 if (keyonce == 0) {
309 keyonce++;
310 (void) pthread_key_create(&globalkey, xmlFreeGlobalState);
311 }
312 (void) pthread_mutex_unlock(&keylock);
313 }
314 if ((globalval = (xmlGlobalState *)
315 pthread_getspecific(globalkey)) == NULL) {
316 xmlGlobalState *tsd = xmlNewGlobalState();
317
318 pthread_setspecific(globalkey, tsd);
319 return (tsd);
Daniel Veillard6f350292001-10-14 09:56:15 +0000320 }
321 return (globalval);
322#else
323 return(NULL);
Daniel Veillardb8478642001-10-12 17:29:10 +0000324#endif
325}
326
327
328/************************************************************************
329 * *
330 * Library wide thread interfaces *
331 * *
332 ************************************************************************/
333
334/**
Daniel Veillard6f350292001-10-14 09:56:15 +0000335 * xmlIsMainThread:
336 *
337 * xmlIsMainThread() check wether the current thread is the main thread.
338 *
339 * Returns 1 if the current thread is the main thread, 0 otherwise
340 */
341int
342xmlIsMainThread(void)
343{
344 if (!initialized)
345 xmlInitThreads();
346
347#ifdef DEBUG_THREADS
348 xmlGenericError(xmlGenericErrorContext, "xmlIsMainThread()\n");
349#endif
350#ifdef HAVE_PTHREAD_H
351 return(mainthread == pthread_self());
352#else
353 return(1);
354#endif
355}
356
357/**
Daniel Veillardb8478642001-10-12 17:29:10 +0000358 * xmlLockLibrary:
359 *
360 * xmlLockLibrary() is used to take out a re-entrant lock on the libxml2
361 * library.
362 */
363void
364xmlLockLibrary(void)
365{
Daniel Veillard6f350292001-10-14 09:56:15 +0000366#ifdef DEBUG_THREADS
367 xmlGenericError(xmlGenericErrorContext, "xmlLockLibrary()\n");
368#endif
Daniel Veillardb8478642001-10-12 17:29:10 +0000369 xmlRMutexLock(xmlLibraryLock);
370}
371
372/**
373 * xmlUnlockLibrary:
374 *
375 * xmlUnlockLibrary() is used to release a re-entrant lock on the libxml2
376 * library.
377 */
378void
379xmlUnlockLibrary(void)
380{
Daniel Veillard6f350292001-10-14 09:56:15 +0000381#ifdef DEBUG_THREADS
382 xmlGenericError(xmlGenericErrorContext, "xmlUnlockLibrary()\n");
383#endif
Daniel Veillardb8478642001-10-12 17:29:10 +0000384 xmlRMutexUnlock(xmlLibraryLock);
385}
386
387/**
388 * xmlInitThreads:
389 *
390 * xmlInitThreads() is used to to initialize all the thread related
391 * data of the libxml2 library.
392 */
393void
394xmlInitThreads(void)
395{
Daniel Veillard6f350292001-10-14 09:56:15 +0000396 if (initialized != 0)
397 return;
398
399#ifdef DEBUG_THREADS
400 xmlGenericError(xmlGenericErrorContext, "xmlInitThreads()\n");
401#endif
402
403#ifdef HAVE_PTHREAD_H
404 mainthread = pthread_self();
405#endif
406
407 initialized = 1;
Daniel Veillardb8478642001-10-12 17:29:10 +0000408}
409
410/**
411 * xmlCleanupThreads:
412 *
413 * xmlCleanupThreads() is used to to cleanup all the thread related
414 * data of the libxml2 library once processing has ended.
415 */
416void
417xmlCleanupThreads(void)
418{
Daniel Veillard6f350292001-10-14 09:56:15 +0000419 if (initialized == 0)
420 return;
421
422#ifdef DEBUG_THREADS
423 xmlGenericError(xmlGenericErrorContext, "xmlCleanupThreads()\n");
424#endif
425
426 initialized = 0;
Daniel Veillardb8478642001-10-12 17:29:10 +0000427}