threads.c revision 8b2c7f10f16dbf1366c7beb89e8c5ab1c4062a1f
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#define IN_LIBXML 11#include "libxml.h" 12 13#include <string.h> 14 15#include <libxml/threads.h> 16#include <libxml/globals.h> 17 18#ifdef HAVE_SYS_TYPES_H 19#include <sys/types.h> 20#endif 21#ifdef HAVE_UNISTD_H 22#include <unistd.h> 23#endif 24#ifdef HAVE_STDLIB_H 25#include <stdlib.h> 26#endif 27#ifdef HAVE_PTHREAD_H 28#include <pthread.h> 29#endif 30 31#ifdef HAVE_WIN32_THREADS 32#include <windows.h> 33#ifndef HAVE_COMPILER_TLS 34#include <process.h> 35#endif 36#endif 37 38#if defined(SOLARIS) 39#include <note.h> 40#endif 41 42/* #define DEBUG_THREADS */ 43 44/* 45 * TODO: this module still uses malloc/free and not xmlMalloc/xmlFree 46 * to avoid some crazyness since xmlMalloc/xmlFree may actually 47 * be hosted on allocated blocks needing them for the allocation ... 48 */ 49 50/* 51 * xmlMutex are a simple mutual exception locks 52 */ 53struct _xmlMutex { 54#ifdef HAVE_PTHREAD_H 55 pthread_mutex_t lock; 56#elif defined HAVE_WIN32_THREADS 57 HANDLE mutex; 58#else 59 int empty; 60#endif 61}; 62 63/* 64 * xmlRMutex are reentrant mutual exception locks 65 */ 66struct _xmlRMutex { 67#ifdef HAVE_PTHREAD_H 68 pthread_mutex_t lock; 69 unsigned int held; 70 unsigned int waiters; 71 pthread_t tid; 72 pthread_cond_t cv; 73#elif defined HAVE_WIN32_THREADS 74 CRITICAL_SECTION cs; 75 unsigned int count; 76#else 77 int empty; 78#endif 79}; 80/* 81 * This module still has some internal static data. 82 * - xmlLibraryLock a global lock 83 * - globalkey used for per-thread data 84 */ 85 86#ifdef HAVE_PTHREAD_H 87static pthread_key_t globalkey; 88static pthread_t mainthread; 89static pthread_once_t once_control = PTHREAD_ONCE_INIT; 90#elif defined HAVE_WIN32_THREADS 91#if defined(HAVE_COMPILER_TLS) 92static __declspec(thread) xmlGlobalState tlstate; 93static __declspec(thread) int tlstate_inited = 0; 94#else /* HAVE_COMPILER_TLS */ 95static DWORD globalkey; 96#endif /* HAVE_COMPILER_TLS */ 97static DWORD mainthread; 98static int run_once_init = 1; 99#endif /* HAVE_WIN32_THREADS */ 100 101static xmlRMutexPtr xmlLibraryLock = NULL; 102static void xmlOnceInit(void); 103 104/** 105 * xmlMutexPtr: 106 * 107 * xmlNewMutex() is used to allocate a libxml2 token struct for use in 108 * synchronizing access to data. 109 * 110 * Returns a new simple mutex pointer or NULL in case of error 111 */ 112xmlMutexPtr 113xmlNewMutex(void) 114{ 115 xmlMutexPtr tok; 116 117 if ((tok = malloc(sizeof(xmlMutex))) == NULL) 118 return (NULL); 119#ifdef HAVE_PTHREAD_H 120 pthread_mutex_init(&tok->lock, NULL); 121#elif defined HAVE_WIN32_THREADS 122 tok->mutex = CreateMutex(NULL, FALSE, NULL); 123#endif 124 return (tok); 125} 126 127/** 128 * xmlFreeMutex: 129 * @tok: the simple mutex 130 * 131 * xmlFreeMutex() is used to reclaim resources associated with a libxml2 token 132 * struct. 133 */ 134void 135xmlFreeMutex(xmlMutexPtr tok) 136{ 137#ifdef HAVE_PTHREAD_H 138 pthread_mutex_destroy(&tok->lock); 139#elif defined HAVE_WIN32_THREADS 140 CloseHandle(tok->mutex); 141#endif 142 free(tok); 143} 144 145/** 146 * xmlMutexLock: 147 * @tok: the simple mutex 148 * 149 * xmlMutexLock() is used to lock a libxml2 token. 150 */ 151void 152xmlMutexLock(xmlMutexPtr tok ATTRIBUTE_UNUSED) 153{ 154#ifdef HAVE_PTHREAD_H 155 pthread_mutex_lock(&tok->lock); 156#elif defined HAVE_WIN32_THREADS 157 WaitForSingleObject(tok->mutex, INFINITE); 158#endif 159 160} 161 162/** 163 * xmlMutexUnlock: 164 * @tok: the simple mutex 165 * 166 * xmlMutexUnlock() is used to unlock a libxml2 token. 167 */ 168void 169xmlMutexUnlock(xmlMutexPtr tok ATTRIBUTE_UNUSED) 170{ 171#ifdef HAVE_PTHREAD_H 172 pthread_mutex_unlock(&tok->lock); 173#elif defined HAVE_WIN32_THREADS 174 ReleaseMutex(tok->mutex); 175#endif 176} 177 178/** 179 * xmlRNewMutex: 180 * 181 * xmlRNewMutex() is used to allocate a reentrant mutex for use in 182 * synchronizing access to data. token_r is a re-entrant lock and thus useful 183 * for synchronizing access to data structures that may be manipulated in a 184 * recursive fashion. 185 * 186 * Returns the new reentrant mutex pointer or NULL in case of error 187 */ 188xmlRMutexPtr 189xmlNewRMutex(void) 190{ 191 xmlRMutexPtr tok; 192 193 if ((tok = malloc(sizeof(xmlRMutex))) == NULL) 194 return (NULL); 195#ifdef HAVE_PTHREAD_H 196 pthread_mutex_init(&tok->lock, NULL); 197 tok->held = 0; 198 tok->waiters = 0; 199#elif defined HAVE_WIN32_THREADS 200 InitializeCriticalSection(&tok->cs); 201 tok->count = 0; 202#endif 203 return (tok); 204} 205 206/** 207 * xmlRFreeMutex: 208 * @tok: the reentrant mutex 209 * 210 * xmlRFreeMutex() is used to reclaim resources associated with a 211 * reentrant mutex. 212 */ 213void 214xmlFreeRMutex(xmlRMutexPtr tok ATTRIBUTE_UNUSED) 215{ 216#ifdef HAVE_PTHREAD_H 217 pthread_mutex_destroy(&tok->lock); 218#elif defined HAVE_WIN32_THREADS 219 DeleteCriticalSection(&tok->cs); 220#endif 221 free(tok); 222} 223 224/** 225 * xmlRMutexLock: 226 * @tok: the reentrant mutex 227 * 228 * xmlRMutexLock() is used to lock a libxml2 token_r. 229 */ 230void 231xmlRMutexLock(xmlRMutexPtr tok ATTRIBUTE_UNUSED) 232{ 233#ifdef HAVE_PTHREAD_H 234 pthread_mutex_lock(&tok->lock); 235 if (tok->held) { 236 if (pthread_equal(tok->tid, pthread_self())) { 237 tok->held++; 238 pthread_mutex_unlock(&tok->lock); 239 return; 240 } else { 241 tok->waiters++; 242 while (tok->held) 243 pthread_cond_wait(&tok->cv, &tok->lock); 244 tok->waiters--; 245 } 246 } 247 tok->tid = pthread_self(); 248 tok->held = 1; 249 pthread_mutex_unlock(&tok->lock); 250#elif defined HAVE_WIN32_THREADS 251 EnterCriticalSection(&tok->cs); 252 ++tok->count; 253#endif 254} 255 256/** 257 * xmlRMutexUnlock: 258 * @tok: the reentrant mutex 259 * 260 * xmlRMutexUnlock() is used to unlock a libxml2 token_r. 261 */ 262void 263xmlRMutexUnlock(xmlRMutexPtr tok ATTRIBUTE_UNUSED) 264{ 265#ifdef HAVE_PTHREAD_H 266 pthread_mutex_lock(&tok->lock); 267 tok->held--; 268 if (tok->held == 0) { 269 if (tok->waiters) 270 pthread_cond_signal(&tok->cv); 271 tok->tid = 0; 272 } 273 pthread_mutex_unlock(&tok->lock); 274#elif defined HAVE_WIN32_THREADS 275 if (!--tok->count) 276 LeaveCriticalSection(&tok->cs); 277#endif 278} 279 280/************************************************************************ 281 * * 282 * Per thread global state handling * 283 * * 284 ************************************************************************/ 285 286#ifdef LIBXML_THREAD_ENABLED 287/** 288 * xmlFreeGlobalState: 289 * @state: a thread global state 290 * 291 * xmlFreeGlobalState() is called when a thread terminates with a non-NULL 292 * global state. It is is used here to reclaim memory resources. 293 */ 294static void 295xmlFreeGlobalState(void *state) 296{ 297 free(state); 298} 299 300/** 301 * xmlNewGlobalState: 302 * 303 * xmlNewGlobalState() allocates a global state. This structure is used to 304 * hold all data for use by a thread when supporting backwards compatibility 305 * of libxml2 to pre-thread-safe behaviour. 306 * 307 * Returns the newly allocated xmlGlobalStatePtr or NULL in case of error 308 */ 309static xmlGlobalStatePtr 310xmlNewGlobalState(void) 311{ 312 xmlGlobalState *gs; 313 314 gs = malloc(sizeof(xmlGlobalState)); 315 if (gs == NULL) 316 return(NULL); 317 318 memset(gs, 0, sizeof(xmlGlobalState)); 319 xmlInitializeGlobalState(gs); 320 return (gs); 321} 322#endif /* LIBXML_THREAD_ENABLED */ 323 324 325/** 326 * xmlGetGlobalState: 327 * 328 * xmlGetGlobalState() is called to retrieve the global state for a thread. 329 * 330 * Returns the thread global state or NULL in case of error 331 */ 332 333#ifdef HAVE_WIN32_THREADS 334#if !defined(HAVE_COMPILER_TLS) 335typedef struct _xmlGlobalStateCleanupHelperParams 336{ 337 HANDLE thread; 338 void *memory; 339} xmlGlobalStateCleanupHelperParams; 340 341void xmlGlobalStateCleanupHelper (void *p) 342{ 343 xmlGlobalStateCleanupHelperParams *params = (xmlGlobalStateCleanupHelperParams *) p; 344 WaitForSingleObject(params->thread, INFINITE); 345 CloseHandle(params->thread); 346 xmlFreeGlobalState(params->memory); 347 free(params); 348 _endthread(); 349} 350#endif /* HAVE_COMPILER_TLS */ 351#endif /* HAVE_WIN32_THREADS */ 352 353xmlGlobalStatePtr 354xmlGetGlobalState(void) 355{ 356#ifdef HAVE_PTHREAD_H 357 xmlGlobalState *globalval; 358 359 pthread_once(&once_control, xmlOnceInit); 360 361 if ((globalval = (xmlGlobalState *) 362 pthread_getspecific(globalkey)) == NULL) { 363 xmlGlobalState *tsd = xmlNewGlobalState(); 364 365 pthread_setspecific(globalkey, tsd); 366 return (tsd); 367 } 368 return (globalval); 369#elif defined HAVE_WIN32_THREADS 370#if defined(HAVE_COMPILER_TLS) 371 if (!tlstate_inited) { 372 tlstate_inited = 1; 373 xmlInitializeGlobalState(&tlstate); 374 } 375 return &tlstate; 376#else /* HAVE_COMPILER_TLS */ 377 xmlGlobalState *globalval; 378 379 if (run_once_init) { 380 run_once_init = 0; 381 xmlOnceInit(); 382 } 383 if ((globalval = (xmlGlobalState *) TlsGetValue(globalkey)) == NULL) { 384 xmlGlobalState *tsd = xmlNewGlobalState(); 385 xmlGlobalStateCleanupHelperParams *p = 386 (xmlGlobalStateCleanupHelperParams *) malloc(sizeof(xmlGlobalStateCleanupHelperParams)); 387 p->memory = tsd; 388 DuplicateHandle(GetCurrentProcess(), GetCurrentThread(), 389 GetCurrentProcess(), &p->thread, 0, TRUE, DUPLICATE_SAME_ACCESS); 390 TlsSetValue(globalkey, tsd); 391 _beginthread(xmlGlobalStateCleanupHelper, 0, p); 392 393 return (tsd); 394 } 395 return (globalval); 396#endif /* HAVE_COMPILER_TLS */ 397#else 398 return(NULL); 399#endif 400} 401 402/************************************************************************ 403 * * 404 * Library wide thread interfaces * 405 * * 406 ************************************************************************/ 407 408/** 409 * xmlGetThreadId: 410 * 411 * xmlGetThreadId() find the current thread ID number 412 * 413 * Returns the current thread ID number 414 */ 415int 416xmlGetThreadId(void) 417{ 418#ifdef HAVE_PTHREAD_H 419 return((int) pthread_self()); 420#elif defined HAVE_WIN32_THREADS 421 return GetCurrentThreadId(); 422#else 423 return((int) 0); 424#endif 425} 426 427/** 428 * xmlIsMainThread: 429 * 430 * xmlIsMainThread() check whether the current thread is the main thread. 431 * 432 * Returns 1 if the current thread is the main thread, 0 otherwise 433 */ 434int 435xmlIsMainThread(void) 436{ 437#ifdef HAVE_PTHREAD_H 438 pthread_once(&once_control, xmlOnceInit); 439#elif defined HAVE_WIN32_THREADS 440 if (run_once_init) { 441 run_once_init = 0; 442 xmlOnceInit (); 443 } 444#endif 445 446#ifdef DEBUG_THREADS 447 xmlGenericError(xmlGenericErrorContext, "xmlIsMainThread()\n"); 448#endif 449#ifdef HAVE_PTHREAD_H 450 return(mainthread == pthread_self()); 451#elif defined HAVE_WIN32_THREADS 452 return(mainthread == GetCurrentThreadId ()); 453#else 454 return(1); 455#endif 456} 457 458/** 459 * xmlLockLibrary: 460 * 461 * xmlLockLibrary() is used to take out a re-entrant lock on the libxml2 462 * library. 463 */ 464void 465xmlLockLibrary(void) 466{ 467#ifdef DEBUG_THREADS 468 xmlGenericError(xmlGenericErrorContext, "xmlLockLibrary()\n"); 469#endif 470 xmlRMutexLock(xmlLibraryLock); 471} 472 473/** 474 * xmlUnlockLibrary: 475 * 476 * xmlUnlockLibrary() is used to release a re-entrant lock on the libxml2 477 * library. 478 */ 479void 480xmlUnlockLibrary(void) 481{ 482#ifdef DEBUG_THREADS 483 xmlGenericError(xmlGenericErrorContext, "xmlUnlockLibrary()\n"); 484#endif 485 xmlRMutexUnlock(xmlLibraryLock); 486} 487 488/** 489 * xmlInitThreads: 490 * 491 * xmlInitThreads() is used to to initialize all the thread related 492 * data of the libxml2 library. 493 */ 494void 495xmlInitThreads(void) 496{ 497#ifdef DEBUG_THREADS 498 xmlGenericError(xmlGenericErrorContext, "xmlInitThreads()\n"); 499#endif 500} 501 502/** 503 * xmlCleanupThreads: 504 * 505 * xmlCleanupThreads() is used to to cleanup all the thread related 506 * data of the libxml2 library once processing has ended. 507 */ 508void 509xmlCleanupThreads(void) 510{ 511#ifdef DEBUG_THREADS 512 xmlGenericError(xmlGenericErrorContext, "xmlCleanupThreads()\n"); 513#endif 514} 515 516/** 517 * xmlOnceInit 518 * 519 * xmlOnceInit() is used to initialize the value of mainthread for use 520 * in other routines. This function should only be called using 521 * pthread_once() in association with the once_control variable to ensure 522 * that the function is only called once. See man pthread_once for more 523 * details. 524 */ 525static void 526xmlOnceInit(void) { 527#ifdef HAVE_PTHREAD_H 528 (void) pthread_key_create(&globalkey, xmlFreeGlobalState); 529 mainthread = pthread_self(); 530#endif 531 532#if defined(HAVE_WIN32_THREADS) 533#if !defined(HAVE_COMPILER_TLS) 534 globalkey = TlsAlloc(); 535#endif 536 mainthread = GetCurrentThreadId(); 537#endif 538} 539