threads.c revision 8bdb91dd5c6c6c89cd1668ba78b1048239ca6ca6
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#ifdef LIBXML_THREAD_ENABLED 252/** 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} 287#endif /* LIBXML_THREAD_ENABLED */ 288 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); 322 } 323 return (globalval); 324#else 325 return(NULL); 326#endif 327} 328 329 330/************************************************************************ 331 * * 332 * Library wide thread interfaces * 333 * * 334 ************************************************************************/ 335 336/** 337 * 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/** 354 * 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/** 377 * xmlLockLibrary: 378 * 379 * xmlLockLibrary() is used to take out a re-entrant lock on the libxml2 380 * library. 381 */ 382void 383xmlLockLibrary(void) 384{ 385#ifdef DEBUG_THREADS 386 xmlGenericError(xmlGenericErrorContext, "xmlLockLibrary()\n"); 387#endif 388 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{ 400#ifdef DEBUG_THREADS 401 xmlGenericError(xmlGenericErrorContext, "xmlUnlockLibrary()\n"); 402#endif 403 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{ 415 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; 427} 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{ 438 if (initialized == 0) 439 return; 440 441#ifdef DEBUG_THREADS 442 xmlGenericError(xmlGenericErrorContext, "xmlCleanupThreads()\n"); 443#endif 444 445 initialized = 0; 446} 447