threads.c revision 6f3502918609dff91f59b163febdd44696d4e247
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#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
34/* #define DEBUG_THREADS */
35
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 */
74
75static int initialized = 0;
76#ifdef HAVE_PTHREAD_H
77static pthread_mutex_t	keylock  = PTHREAD_MUTEX_INITIALIZER;
78static pthread_key_t	globalkey;
79static int		keyonce = 0;
80static pthread_t	mainthread;
81#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);
320    }
321    return (globalval);
322#else
323    return(NULL);
324#endif
325}
326
327
328/************************************************************************
329 *									*
330 *			Library wide thread interfaces			*
331 *									*
332 ************************************************************************/
333
334/**
335 * 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/**
358 * xmlLockLibrary:
359 *
360 * xmlLockLibrary() is used to take out a re-entrant lock on the libxml2
361 * library.
362 */
363void
364xmlLockLibrary(void)
365{
366#ifdef DEBUG_THREADS
367    xmlGenericError(xmlGenericErrorContext, "xmlLockLibrary()\n");
368#endif
369    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{
381#ifdef DEBUG_THREADS
382    xmlGenericError(xmlGenericErrorContext, "xmlUnlockLibrary()\n");
383#endif
384    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{
396    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;
408}
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{
419    if (initialized == 0)
420        return;
421
422#ifdef DEBUG_THREADS
423    xmlGenericError(xmlGenericErrorContext, "xmlCleanupThreads()\n");
424#endif
425
426    initialized = 0;
427}
428