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