blob: 9b0a3464c07a908979c0592d97dd28a1d462c133 [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
34
35/*
36 * TODO: this module still uses malloc/free and not xmlMalloc/xmlFree
37 * to avoid some crazyness since xmlMalloc/xmlFree may actually
38 * be hosted on allocated blocks needing them for the allocation ...
39 */
40
41/*
42 * xmlMutex are a simple mutual exception locks
43 */
44struct _xmlMutex {
45#ifdef HAVE_PTHREAD_H
46 pthread_mutex_t lock;
47#else
48 int empty;
49#endif
50};
51
52/*
53 * xmlRMutex are reentrant mutual exception locks
54 */
55struct _xmlRMutex {
56#ifdef HAVE_PTHREAD_H
57 pthread_mutex_t lock;
58 unsigned int held;
59 unsigned int waiters;
60 pthread_t tid;
61 pthread_cond_t cv;
62#else
63 int empty;
64#endif
65};
66/*
67 * This module still has some internal static data.
68 * - xmlLibraryLock a global lock
69 * - globalkey used for per-thread data
70 * - keylock protecting globalkey
71 * - keyonce to mark initialization of globalkey
72 */
73#ifdef HAVE_PTHREAD_H
74static pthread_mutex_t keylock;
75static pthread_key_t globalkey;
76static int keyonce = 0;
77#endif
78static xmlRMutexPtr xmlLibraryLock = NULL;
79
80#if defined(SOLARIS)
81NOTE(DATA_READABLE_WITHOUT_LOCK(keyonce))
82#endif
83
84/**
85 * xmlMutexPtr:
86 *
87 * xmlNewMutex() is used to allocate a libxml2 token struct for use in
88 * synchronizing access to data.
89 *
90 * Returns a new simple mutex pointer or NULL in case of error
91 */
92xmlMutexPtr
93xmlNewMutex(void)
94{
95 xmlMutexPtr tok;
96
97 if ((tok = malloc(sizeof(xmlMutex))) == NULL)
98 return (NULL);
99#ifdef HAVE_PTHREAD_H
100 pthread_mutex_init(&tok->lock, NULL);
101#endif
102 return (tok);
103}
104
105/**
106 * xmlFreeMutex:
107 * @tok: the simple mutex
108 *
109 * xmlFreeMutex() is used to reclaim resources associated with a libxml2 token
110 * struct.
111 */
112void
113xmlFreeMutex(xmlMutexPtr tok)
114{
115#ifdef HAVE_PTHREAD_H
116 pthread_mutex_destroy(&tok->lock);
117#endif
118 free(tok);
119}
120
121/**
122 * xmlMutexLock:
123 * @tok: the simple mutex
124 *
125 * xmlMutexLock() is used to lock a libxml2 token.
126 */
127void
128xmlMutexLock(xmlMutexPtr tok)
129{
130#ifdef HAVE_PTHREAD_H
131 pthread_mutex_lock(&tok->lock);
132#endif
133
134}
135
136/**
137 * xmlMutexUnlock:
138 * @tok: the simple mutex
139 *
140 * xmlMutexUnlock() is used to unlock a libxml2 token.
141 */
142void
143xmlMutexUnlock(xmlMutexPtr tok)
144{
145#ifdef HAVE_PTHREAD_H
146 pthread_mutex_unlock(&tok->lock);
147#endif
148}
149
150/**
151 * xmlRNewMutex:
152 *
153 * xmlRNewMutex() is used to allocate a reentrant mutex for use in
154 * synchronizing access to data. token_r is a re-entrant lock and thus useful
155 * for synchronizing access to data structures that may be manipulated in a
156 * recursive fashion.
157 *
158 * Returns the new reentrant mutex pointer or NULL in case of error
159 */
160xmlRMutexPtr
161xmlNewRMutex(void)
162{
163 xmlRMutexPtr tok;
164
165 if ((tok = malloc(sizeof(xmlRMutex))) == NULL)
166 return (NULL);
167#ifdef HAVE_PTHREAD_H
168 pthread_mutex_init(&tok->lock, NULL);
169 tok->held = 0;
170 tok->waiters = 0;
171#endif
172 return (tok);
173}
174
175/**
176 * xmlRFreeMutex:
177 * @tok: the reentrant mutex
178 *
179 * xmlRFreeMutex() is used to reclaim resources associated with a
180 * reentrant mutex.
181 */
182void
183xmlFreeRMutex(xmlRMutexPtr tok)
184{
185#ifdef HAVE_PTHREAD_H
186 pthread_mutex_destroy(&tok->lock);
187#endif
188 free(tok);
189}
190
191/**
192 * xmlRMutexLock:
193 * @tok: the reentrant mutex
194 *
195 * xmlRMutexLock() is used to lock a libxml2 token_r.
196 */
197void
198xmlRMutexLock(xmlRMutexPtr tok)
199{
200#ifdef HAVE_PTHREAD_H
201 pthread_mutex_lock(&tok->lock);
202 if (tok->held) {
203 if (pthread_equal(tok->tid, pthread_self())) {
204 tok->held++;
205 pthread_mutex_unlock(&tok->lock);
206 return;
207 } else {
208 tok->waiters++;
209 while (tok->held)
210 pthread_cond_wait(&tok->cv, &tok->lock);
211 tok->waiters--;
212 }
213 }
214 tok->tid = pthread_self();
215 tok->held = 1;
216 pthread_mutex_unlock(&tok->lock);
217#endif
218}
219
220/**
221 * xmlRMutexUnlock:
222 * @tok: the reentrant mutex
223 *
224 * xmlRMutexUnlock() is used to unlock a libxml2 token_r.
225 */
226void
227xmlRMutexUnlock(xmlRMutexPtr tok)
228{
229#ifdef HAVE_PTHREAD_H
230 pthread_mutex_lock(&tok->lock);
231 tok->held--;
232 if (tok->held == 0) {
233 if (tok->waiters)
234 pthread_cond_signal(&tok->cv);
235 tok->tid = 0;
236 }
237 pthread_mutex_unlock(&tok->lock);
238#endif
239}
240
241/************************************************************************
242 * *
243 * Per thread global state handling *
244 * *
245 ************************************************************************/
246
247/**
248 * xmlFreeGlobalState:
249 * @state: a thread global state
250 *
251 * xmlFreeGlobalState() is called when a thread terminates with a non-NULL
252 * global state. It is is used here to reclaim memory resources.
253 */
254static void
255xmlFreeGlobalState(void *state)
256{
257 free(state);
258}
259
260/**
261 * xmlNewGlobalState:
262 *
263 * xmlNewGlobalState() allocates a global state. This structure is used to
264 * hold all data for use by a thread when supporting backwards compatibility
265 * of libmxml2 to pre-thread-safe behaviour.
266 *
267 * Returns the newly allocated xmlGlobalStatePtr or NULL in case of error
268 */
269static xmlGlobalStatePtr
270xmlNewGlobalState(void)
271{
272 xmlGlobalState *gs;
273
274 gs = malloc(sizeof(xmlGlobalState));
275 if (gs == NULL)
276 return(NULL);
277
278 memset(gs, 0, sizeof(gs));
279 xmlInitializeGlobalState(gs);
280 return (gs);
281}
282
283
284/**
285 * xmlGetGlobalState:
286 *
287 * xmlGetGlobalState() is called to retrieve the global state for a thread.
288 * keyonce will only be set once during a library invocation and is used
289 * to create globalkey, the key used to store each thread's TSD.
290 *
291 * Note: it should not be called for the "main" thread as this thread uses
292 * the existing global variables defined in the library.
293 *
294 * Returns the thread global state or NULL in case of error
295 */
296xmlGlobalStatePtr
297xmlGetGlobalState(void)
298{
299#ifdef HAVE_PTHREAD_H
300 xmlGlobalState *globalval;
301
302 if (keyonce == 0) {
303 (void) pthread_mutex_lock(&keylock);
304 if (keyonce == 0) {
305 keyonce++;
306 (void) pthread_key_create(&globalkey, xmlFreeGlobalState);
307 }
308 (void) pthread_mutex_unlock(&keylock);
309 }
310 if ((globalval = (xmlGlobalState *)
311 pthread_getspecific(globalkey)) == NULL) {
312 xmlGlobalState *tsd = xmlNewGlobalState();
313
314 pthread_setspecific(globalkey, tsd);
315 return (tsd);
316 } else
317 return (globalval);
318#endif
319}
320
321
322/************************************************************************
323 * *
324 * Library wide thread interfaces *
325 * *
326 ************************************************************************/
327
328/**
329 * xmlLockLibrary:
330 *
331 * xmlLockLibrary() is used to take out a re-entrant lock on the libxml2
332 * library.
333 */
334void
335xmlLockLibrary(void)
336{
337 xmlRMutexLock(xmlLibraryLock);
338}
339
340/**
341 * xmlUnlockLibrary:
342 *
343 * xmlUnlockLibrary() is used to release a re-entrant lock on the libxml2
344 * library.
345 */
346void
347xmlUnlockLibrary(void)
348{
349 xmlRMutexUnlock(xmlLibraryLock);
350}
351
352/**
353 * xmlInitThreads:
354 *
355 * xmlInitThreads() is used to to initialize all the thread related
356 * data of the libxml2 library.
357 */
358void
359xmlInitThreads(void)
360{
361}
362
363/**
364 * xmlCleanupThreads:
365 *
366 * xmlCleanupThreads() is used to to cleanup all the thread related
367 * data of the libxml2 library once processing has ended.
368 */
369void
370xmlCleanupThreads(void)
371{
372}