locking.c revision 818534a1
1/* 2 3Copyright 1992, 1998 The Open Group 4 5Permission to use, copy, modify, distribute, and sell this software and its 6documentation for any purpose is hereby granted without fee, provided that 7the above copyright notice appear in all copies and that both that 8copyright notice and this permission notice appear in supporting 9documentation. 10 11The above copyright notice and this permission notice shall be included in 12all copies or substantial portions of the Software. 13 14THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17OPEN GROUP BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN 18AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 19CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 20 21Except as contained in this notice, the name of The Open Group shall not be 22used in advertising or otherwise to promote the sale, use or other dealings 23in this Software without prior written authorization from The Open Group. 24 25*/ 26 27/* 28 * Author: Stephen Gildea, MIT X Consortium 29 * 30 * locking.c - multi-thread locking routines implemented in C Threads 31 */ 32 33#ifdef HAVE_CONFIG_H 34#include <config.h> 35#endif 36#include "Xlibint.h" 37#undef _XLockMutex 38#undef _XUnlockMutex 39#undef _XCreateMutex 40#undef _XFreeMutex 41 42#ifdef XTHREADS 43 44#ifdef __UNIXWARE__ 45#include <dlfcn.h> 46#endif 47 48#include "Xprivate.h" 49#include "locking.h" 50#ifdef XTHREADS_WARN 51#include <stdio.h> /* for warn/debug stuff */ 52#endif 53 54/* Additional arguments for source code location lock call was made from */ 55#if defined(XTHREADS_WARN) || defined(XTHREADS_FILE_LINE) 56# define XTHREADS_FILE_LINE_ARGS \ 57 , \ 58 char* file, /* source file, from macro */ \ 59 int line 60#else 61# define XTHREADS_FILE_LINE_ARGS /* None */ 62#endif 63 64 65#define NUM_FREE_CVLS 4 66 67/* in lcWrap.c */ 68extern LockInfoPtr _Xi18n_lock; 69 70#ifdef WIN32 71static DWORD _X_TlsIndex = (DWORD)-1; 72 73void _Xthread_init(void) 74{ 75 if (_X_TlsIndex == (DWORD)-1) 76 _X_TlsIndex = TlsAlloc(); 77} 78 79struct _xthread_waiter * 80_Xthread_waiter(void) 81{ 82 struct _xthread_waiter *me; 83 84 if (!(me = TlsGetValue(_X_TlsIndex))) { 85 me = xmalloc(sizeof(struct _xthread_waiter)); 86 me->sem = CreateSemaphore(NULL, 0, 1, NULL); 87 me->next = NULL; 88 TlsSetValue(_X_TlsIndex, me); 89 } 90 return me; 91} 92#endif /* WIN32 */ 93 94static xthread_t _Xthread_self(void) 95{ 96 return xthread_self(); 97} 98 99static LockInfoRec global_lock; 100static LockInfoRec i18n_lock; 101 102static void _XLockMutex( 103 LockInfoPtr lip 104 XTHREADS_FILE_LINE_ARGS 105 ) 106{ 107 xmutex_lock(lip->lock); 108} 109 110static void _XUnlockMutex( 111 LockInfoPtr lip 112 XTHREADS_FILE_LINE_ARGS 113 ) 114{ 115 xmutex_unlock(lip->lock); 116} 117 118static void _XCreateMutex( 119 LockInfoPtr lip) 120{ 121 lip->lock = xmutex_malloc(); 122 if (lip->lock) { 123 xmutex_init(lip->lock); 124 xmutex_set_name(lip->lock, "Xlib"); 125 } 126} 127 128static void _XFreeMutex( 129 LockInfoPtr lip) 130{ 131 xmutex_clear(lip->lock); 132 xmutex_free(lip->lock); 133} 134 135#ifdef XTHREADS_WARN 136static char *locking_file; 137static int locking_line; 138static xthread_t locking_thread; 139static Bool xlibint_unlock = False; /* XlibInt.c may Unlock and re-Lock */ 140 141/* history that is useful to examine in a debugger */ 142#define LOCK_HIST_SIZE 21 143 144static struct { 145 Bool lockp; /* True for lock, False for unlock */ 146 xthread_t thread; 147 char *file; 148 int line; 149} locking_history[LOCK_HIST_SIZE]; 150 151int lock_hist_loc = 0; /* next slot to fill */ 152 153static void _XLockDisplayWarn( 154 Display *dpy, 155 char *file, /* source file, from macro */ 156 int line) 157{ 158 xthread_t self; 159 xthread_t old_locker; 160 161 self = xthread_self(); 162 old_locker = locking_thread; 163 if (xthread_have_id(old_locker)) { 164 if (xthread_equal(old_locker, self)) 165 printf("Xlib ERROR: %s line %d thread %x: locking display already locked at %s line %d\n", 166 file, line, self, locking_file, locking_line); 167#ifdef XTHREADS_DEBUG 168 else 169 printf("%s line %d: thread %x waiting on lock held by %s line %d thread %x\n", 170 file, line, self, 171 locking_file, locking_line, old_locker); 172#endif /* XTHREADS_DEBUG */ 173 } 174 175 xmutex_lock(dpy->lock->mutex); 176 177 if (strcmp(file, "XlibInt.c") == 0) { 178 if (!xlibint_unlock) 179 printf("Xlib ERROR: XlibInt.c line %d thread %x locking display it did not unlock\n", 180 line, self); 181 xlibint_unlock = False; 182 } 183 184#ifdef XTHREADS_DEBUG 185 /* if (old_locker && old_locker != self) */ 186 if (strcmp("XClearArea.c", file) && strcmp("XDrSegs.c", file)) /* ico */ 187 printf("%s line %d: thread %x got display lock\n", file, line, self); 188#endif /* XTHREADS_DEBUG */ 189 190 locking_thread = self; 191 if (strcmp(file, "XlibInt.c") != 0) { 192 locking_file = file; 193 locking_line = line; 194 } 195 locking_history[lock_hist_loc].file = file; 196 locking_history[lock_hist_loc].line = line; 197 locking_history[lock_hist_loc].thread = self; 198 locking_history[lock_hist_loc].lockp = True; 199 lock_hist_loc++; 200 if (lock_hist_loc >= LOCK_HIST_SIZE) 201 lock_hist_loc = 0; 202} 203#endif /* XTHREADS_WARN */ 204 205static void _XUnlockDisplay( 206 Display *dpy 207 XTHREADS_FILE_LINE_ARGS 208 ) 209{ 210#ifdef XTHREADS_WARN 211 xthread_t self = xthread_self(); 212 213#ifdef XTHREADS_DEBUG 214 if (strcmp("XClearArea.c", file) && strcmp("XDrSegs.c", file)) /* ico */ 215 printf("%s line %d: thread %x unlocking display\n", file, line, self); 216#endif /* XTHREADS_DEBUG */ 217 218 if (!xthread_have_id(locking_thread)) 219 printf("Xlib ERROR: %s line %d thread %x: unlocking display that is not locked\n", 220 file, line, self); 221 else if (strcmp(file, "XlibInt.c") == 0) 222 xlibint_unlock = True; 223#ifdef XTHREADS_DEBUG 224 else if (strcmp(file, locking_file) != 0) 225 /* not always an error because locking_file is not per-thread */ 226 printf("%s line %d: unlocking display locked from %s line %d (probably okay)\n", 227 file, line, locking_file, locking_line); 228#endif /* XTHREADS_DEBUG */ 229 xthread_clear_id(locking_thread); 230 231 locking_history[lock_hist_loc].file = file; 232 locking_history[lock_hist_loc].line = line; 233 locking_history[lock_hist_loc].thread = self; 234 locking_history[lock_hist_loc].lockp = False; 235 lock_hist_loc++; 236 if (lock_hist_loc >= LOCK_HIST_SIZE) 237 lock_hist_loc = 0; 238#endif /* XTHREADS_WARN */ 239 xmutex_unlock(dpy->lock->mutex); 240} 241 242 243static struct _XCVList *_XCreateCVL( 244 Display *dpy) 245{ 246 struct _XCVList *cvl; 247 248 if ((cvl = dpy->lock->free_cvls) != NULL) { 249 dpy->lock->free_cvls = cvl->next; 250 dpy->lock->num_free_cvls--; 251 } else { 252 cvl = Xmalloc(sizeof(struct _XCVList)); 253 if (!cvl) 254 return NULL; 255 cvl->cv = xcondition_malloc(); 256 if (!cvl->cv) { 257 Xfree(cvl); 258 return NULL; 259 } 260 xcondition_init(cvl->cv); 261 xcondition_set_name(cvl->cv, "Xlib read queue"); 262 } 263 cvl->next = NULL; 264 return cvl; 265} 266 267/* Put ourselves on the queue to read the connection. 268 Allocates and returns a queue element. */ 269 270static struct _XCVList * 271_XPushReader( 272 Display *dpy, 273 struct _XCVList ***tail) 274{ 275 struct _XCVList *cvl; 276 277 cvl = _XCreateCVL(dpy); 278#ifdef XTHREADS_DEBUG 279 printf("_XPushReader called in thread %x, pushing %x\n", 280 xthread_self(), cvl); 281#endif 282 **tail = cvl; 283 *tail = &cvl->next; 284 return cvl; 285} 286 287/* signal the next thread waiting to read the connection */ 288 289static void _XPopReader( 290 Display *dpy, 291 struct _XCVList **list, 292 struct _XCVList ***tail) 293{ 294 register struct _XCVList *front = *list; 295 296#ifdef XTHREADS_DEBUG 297 printf("_XPopReader called in thread %x, popping %x\n", 298 xthread_self(), front); 299#endif 300 301 if (dpy->flags & XlibDisplayProcConni) 302 /* we never added ourself in the first place */ 303 return; 304 305 if (front) { /* check "front" for paranoia */ 306 *list = front->next; 307 if (*tail == &front->next) /* did we free the last elt? */ 308 *tail = list; 309 if (dpy->lock->num_free_cvls < NUM_FREE_CVLS) { 310 front->next = dpy->lock->free_cvls; 311 dpy->lock->free_cvls = front; 312 dpy->lock->num_free_cvls++; 313 } else { 314 xcondition_clear(front->cv); 315 Xfree(front->cv); 316 Xfree(front); 317 } 318 } 319 320 /* signal new front after it is in place */ 321 if ((dpy->lock->reply_first = (dpy->lock->reply_awaiters != NULL))) { 322 ConditionSignal(dpy, dpy->lock->reply_awaiters->cv); 323 } else if (dpy->lock->event_awaiters) { 324 ConditionSignal(dpy, dpy->lock->event_awaiters->cv); 325 } 326} 327 328static void _XConditionWait( 329 xcondition_t cv, 330 xmutex_t mutex 331 XTHREADS_FILE_LINE_ARGS 332 ) 333{ 334#ifdef XTHREADS_WARN 335 xthread_t self = xthread_self(); 336 char *old_file = locking_file; 337 int old_line = locking_line; 338 339#ifdef XTHREADS_DEBUG 340 printf("line %d thread %x in condition wait\n", line, self); 341#endif 342 xthread_clear_id(locking_thread); 343 344 locking_history[lock_hist_loc].file = file; 345 locking_history[lock_hist_loc].line = line; 346 locking_history[lock_hist_loc].thread = self; 347 locking_history[lock_hist_loc].lockp = False; 348 lock_hist_loc++; 349 if (lock_hist_loc >= LOCK_HIST_SIZE) 350 lock_hist_loc = 0; 351#endif /* XTHREADS_WARN */ 352 353 xcondition_wait(cv, mutex); 354 355#ifdef XTHREADS_WARN 356 locking_thread = self; 357 locking_file = old_file; 358 locking_line = old_line; 359 360 locking_history[lock_hist_loc].file = file; 361 locking_history[lock_hist_loc].line = line; 362 locking_history[lock_hist_loc].thread = self; 363 locking_history[lock_hist_loc].lockp = True; 364 lock_hist_loc++; 365 if (lock_hist_loc >= LOCK_HIST_SIZE) 366 lock_hist_loc = 0; 367#ifdef XTHREADS_DEBUG 368 printf("line %d thread %x was signaled\n", line, self); 369#endif /* XTHREADS_DEBUG */ 370#endif /* XTHREADS_WARN */ 371} 372 373static void _XConditionSignal( 374 xcondition_t cv 375 XTHREADS_FILE_LINE_ARGS 376 ) 377{ 378#ifdef XTHREADS_WARN 379#ifdef XTHREADS_DEBUG 380 printf("line %d thread %x is signalling\n", line, xthread_self()); 381#endif 382#endif 383 xcondition_signal(cv); 384} 385 386 387static void _XConditionBroadcast( 388 xcondition_t cv 389 XTHREADS_FILE_LINE_ARGS 390 ) 391{ 392#ifdef XTHREADS_WARN 393#ifdef XTHREADS_DEBUG 394 printf("line %d thread %x is broadcasting\n", line, xthread_self()); 395#endif 396#endif 397 xcondition_broadcast(cv); 398} 399 400 401static void _XFreeDisplayLock( 402 Display *dpy) 403{ 404 struct _XCVList *cvl; 405 406 if (dpy->lock != NULL) { 407 if (dpy->lock->mutex != NULL) { 408 xmutex_clear(dpy->lock->mutex); 409 xmutex_free(dpy->lock->mutex); 410 } 411 if (dpy->lock->cv != NULL) { 412 xcondition_clear(dpy->lock->cv); 413 xcondition_free(dpy->lock->cv); 414 } 415 if (dpy->lock->writers != NULL) { 416 xcondition_clear(dpy->lock->writers); 417 xcondition_free(dpy->lock->writers); 418 } 419 while ((cvl = dpy->lock->free_cvls)) { 420 dpy->lock->free_cvls = cvl->next; 421 xcondition_clear(cvl->cv); 422 Xfree(cvl->cv); 423 Xfree(cvl); 424 } 425 Xfree(dpy->lock); 426 dpy->lock = NULL; 427 } 428 if (dpy->lock_fns != NULL) { 429 Xfree(dpy->lock_fns); 430 dpy->lock_fns = NULL; 431 } 432} 433 434/* 435 * wait for thread with user-level display lock to release it. 436 */ 437 438static void _XDisplayLockWait( 439 Display *dpy) 440{ 441 xthread_t self; 442 443 while (dpy->lock->locking_level > 0) { 444 self = xthread_self(); 445 if (xthread_equal(dpy->lock->locking_thread, self)) 446 break; 447 ConditionWait(dpy, dpy->lock->cv); 448 } 449} 450 451static void _XLockDisplay( 452 Display *dpy 453 XTHREADS_FILE_LINE_ARGS 454 ) 455{ 456#ifdef XTHREADS_WARN 457 _XLockDisplayWarn(dpy, file, line); 458#else 459 xmutex_lock(dpy->lock->mutex); 460#endif 461 if (dpy->lock->locking_level > 0) 462 _XDisplayLockWait(dpy); 463 _XIDHandler(dpy); 464 _XSeqSyncFunction(dpy); 465} 466 467/* 468 * _XReply is allowed to exit from select/poll and clean up even if a 469 * user-level lock is in force, so it uses this instead of _XFancyLockDisplay. 470 */ 471static void _XInternalLockDisplay( 472 Display *dpy, 473 Bool wskip 474 XTHREADS_FILE_LINE_ARGS 475 ) 476{ 477#ifdef XTHREADS_WARN 478 _XLockDisplayWarn(dpy, file, line); 479#else 480 xmutex_lock(dpy->lock->mutex); 481#endif 482 if (!wskip && dpy->lock->locking_level > 0) 483 _XDisplayLockWait(dpy); 484} 485 486static void _XUserLockDisplay( 487 register Display* dpy) 488{ 489 _XDisplayLockWait(dpy); 490 491 if (++dpy->lock->locking_level == 1) { 492 dpy->lock->lock_wait = _XDisplayLockWait; 493 dpy->lock->locking_thread = xthread_self(); 494 } 495} 496 497static 498void _XUserUnlockDisplay( 499 register Display* dpy) 500{ 501 if (dpy->lock->locking_level > 0 && --dpy->lock->locking_level == 0) { 502 /* signal other threads that might be waiting in XLockDisplay */ 503 ConditionBroadcast(dpy, dpy->lock->cv); 504 dpy->lock->lock_wait = NULL; 505 xthread_clear_id(dpy->lock->locking_thread); 506 } 507} 508 509/* returns 0 if initialized ok, -1 if unable to allocate 510 a mutex or other memory */ 511 512static int _XInitDisplayLock( 513 Display *dpy) 514{ 515 dpy->lock_fns = Xmalloc(sizeof(struct _XLockPtrs)); 516 if (dpy->lock_fns == NULL) 517 return -1; 518 dpy->lock = Xmalloc(sizeof(struct _XLockInfo)); 519 if (dpy->lock == NULL) { 520 _XFreeDisplayLock(dpy); 521 return -1; 522 } 523 dpy->lock->cv = xcondition_malloc(); 524 dpy->lock->mutex = xmutex_malloc(); 525 dpy->lock->writers = xcondition_malloc(); 526 if (!dpy->lock->cv || !dpy->lock->mutex || !dpy->lock->writers) { 527 _XFreeDisplayLock(dpy); 528 return -1; 529 } 530 531 dpy->lock->reply_bytes_left = 0; 532 dpy->lock->reply_was_read = False; 533 dpy->lock->reply_awaiters = NULL; 534 dpy->lock->reply_awaiters_tail = &dpy->lock->reply_awaiters; 535 dpy->lock->event_awaiters = NULL; 536 dpy->lock->event_awaiters_tail = &dpy->lock->event_awaiters; 537 dpy->lock->reply_first = False; 538 dpy->lock->locking_level = 0; 539 dpy->lock->num_free_cvls = 0; 540 dpy->lock->free_cvls = NULL; 541 xthread_clear_id(dpy->lock->locking_thread); 542 xthread_clear_id(dpy->lock->reading_thread); 543 xthread_clear_id(dpy->lock->conni_thread); 544 xmutex_init(dpy->lock->mutex); 545 xmutex_set_name(dpy->lock->mutex, "Xlib Display"); 546 xcondition_init(dpy->lock->cv); 547 xcondition_set_name(dpy->lock->cv, "XLockDisplay"); 548 xcondition_init(dpy->lock->writers); 549 xcondition_set_name(dpy->lock->writers, "Xlib wait for writable"); 550 dpy->lock_fns->lock_display = _XLockDisplay; 551 dpy->lock->internal_lock_display = _XInternalLockDisplay; 552 dpy->lock_fns->unlock_display = _XUnlockDisplay; 553 dpy->lock->user_lock_display = _XUserLockDisplay; 554 dpy->lock->user_unlock_display = _XUserUnlockDisplay; 555 dpy->lock->pop_reader = _XPopReader; 556 dpy->lock->push_reader = _XPushReader; 557 dpy->lock->condition_wait = _XConditionWait; 558 dpy->lock->condition_signal = _XConditionSignal; 559 dpy->lock->condition_broadcast = _XConditionBroadcast; 560 dpy->lock->create_cvl = _XCreateCVL; 561 dpy->lock->lock_wait = NULL; /* filled in by XLockDisplay() */ 562 563 return 0; 564} 565 566#ifdef __UNIXWARE__ 567xthread_t __x11_thr_self() { return 0; } 568xthread_t (*_x11_thr_self)() = __x11_thr_self; 569#endif 570 571 572Status XInitThreads(void) 573{ 574 if (_Xglobal_lock) 575 return 1; 576#ifdef __UNIXWARE__ 577 else { 578 void *dl_handle = dlopen(NULL, RTLD_LAZY); 579 if (!dl_handle || 580 ((_x11_thr_self = (xthread_t(*)())dlsym(dl_handle,"thr_self")) == 0)) { 581 _x11_thr_self = __x11_thr_self; 582 (void) fprintf (stderr, 583 "XInitThreads called, but no libthread in the calling program!\n" ); 584 } 585 } 586#endif /* __UNIXWARE__ */ 587#ifdef xthread_init 588 xthread_init(); /* return value? */ 589#endif 590 if (!(global_lock.lock = xmutex_malloc())) 591 return 0; 592 if (!(i18n_lock.lock = xmutex_malloc())) { 593 xmutex_free(global_lock.lock); 594 global_lock.lock = NULL; 595 return 0; 596 } 597 _Xglobal_lock = &global_lock; 598 xmutex_init(_Xglobal_lock->lock); 599 xmutex_set_name(_Xglobal_lock->lock, "Xlib global"); 600 _Xi18n_lock = &i18n_lock; 601 xmutex_init(_Xi18n_lock->lock); 602 xmutex_set_name(_Xi18n_lock->lock, "Xlib i18n"); 603 _XLockMutex_fn = _XLockMutex; 604 _XUnlockMutex_fn = _XUnlockMutex; 605 _XCreateMutex_fn = _XCreateMutex; 606 _XFreeMutex_fn = _XFreeMutex; 607 _XInitDisplayLock_fn = _XInitDisplayLock; 608 _XFreeDisplayLock_fn = _XFreeDisplayLock; 609 _Xthread_self_fn = _Xthread_self; 610 611#ifdef XTHREADS_WARN 612#ifdef XTHREADS_DEBUG 613 setlinebuf(stdout); /* for debugging messages */ 614#endif 615#endif 616 617 return 1; 618} 619 620#else /* XTHREADS */ 621Status XInitThreads(void) 622{ 623 return 0; 624} 625#endif /* XTHREADS */ 626