locking.c revision 1ccffcbd
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/* in lcConv.c */ 70extern LockInfoPtr _conv_lock; 71 72#ifdef WIN32 73static DWORD _X_TlsIndex = (DWORD)-1; 74 75void _Xthread_init(void) 76{ 77 if (_X_TlsIndex == (DWORD)-1) 78 _X_TlsIndex = TlsAlloc(); 79} 80 81struct _xthread_waiter * 82_Xthread_waiter(void) 83{ 84 struct _xthread_waiter *me; 85 86 if (!(me = TlsGetValue(_X_TlsIndex))) { 87 me = xmalloc(sizeof(struct _xthread_waiter)); 88 me->sem = CreateSemaphore(NULL, 0, 1, NULL); 89 me->next = NULL; 90 TlsSetValue(_X_TlsIndex, me); 91 } 92 return me; 93} 94#endif /* WIN32 */ 95 96static xthread_t _Xthread_self(void) 97{ 98 return xthread_self(); 99} 100 101static LockInfoRec global_lock; 102static LockInfoRec i18n_lock; 103static LockInfoRec conv_lock; 104 105static void _XLockMutex( 106 LockInfoPtr lip 107 XTHREADS_FILE_LINE_ARGS 108 ) 109{ 110 xmutex_lock(lip->lock); 111} 112 113static void _XUnlockMutex( 114 LockInfoPtr lip 115 XTHREADS_FILE_LINE_ARGS 116 ) 117{ 118 xmutex_unlock(lip->lock); 119} 120 121static void _XCreateMutex( 122 LockInfoPtr lip) 123{ 124 lip->lock = xmutex_malloc(); 125 if (lip->lock) { 126 xmutex_init(lip->lock); 127 xmutex_set_name(lip->lock, "Xlib"); 128 } 129} 130 131static void _XFreeMutex( 132 LockInfoPtr lip) 133{ 134 xmutex_clear(lip->lock); 135 xmutex_free(lip->lock); 136 lip->lock = NULL; 137} 138 139#ifdef XTHREADS_WARN 140static char *locking_file; 141static int locking_line; 142static xthread_t locking_thread; 143static Bool xlibint_unlock = False; /* XlibInt.c may Unlock and re-Lock */ 144 145/* history that is useful to examine in a debugger */ 146#define LOCK_HIST_SIZE 21 147 148static struct { 149 Bool lockp; /* True for lock, False for unlock */ 150 xthread_t thread; 151 char *file; 152 int line; 153} locking_history[LOCK_HIST_SIZE]; 154 155int lock_hist_loc = 0; /* next slot to fill */ 156 157static void _XLockDisplayWarn( 158 Display *dpy, 159 char *file, /* source file, from macro */ 160 int line) 161{ 162 xthread_t self; 163 xthread_t old_locker; 164 165 self = xthread_self(); 166 old_locker = locking_thread; 167 if (xthread_have_id(old_locker)) { 168 if (xthread_equal(old_locker, self)) 169 printf("Xlib ERROR: %s line %d thread %x: locking display already locked at %s line %d\n", 170 file, line, self, locking_file, locking_line); 171#ifdef XTHREADS_DEBUG 172 else 173 printf("%s line %d: thread %x waiting on lock held by %s line %d thread %x\n", 174 file, line, self, 175 locking_file, locking_line, old_locker); 176#endif /* XTHREADS_DEBUG */ 177 } 178 179 xmutex_lock(dpy->lock->mutex); 180 181 if (strcmp(file, "XlibInt.c") == 0) { 182 if (!xlibint_unlock) 183 printf("Xlib ERROR: XlibInt.c line %d thread %x locking display it did not unlock\n", 184 line, self); 185 xlibint_unlock = False; 186 } 187 188#ifdef XTHREADS_DEBUG 189 /* if (old_locker && old_locker != self) */ 190 if (strcmp("XClearArea.c", file) && strcmp("XDrSegs.c", file)) /* ico */ 191 printf("%s line %d: thread %x got display lock\n", file, line, self); 192#endif /* XTHREADS_DEBUG */ 193 194 locking_thread = self; 195 if (strcmp(file, "XlibInt.c") != 0) { 196 locking_file = file; 197 locking_line = line; 198 } 199 locking_history[lock_hist_loc].file = file; 200 locking_history[lock_hist_loc].line = line; 201 locking_history[lock_hist_loc].thread = self; 202 locking_history[lock_hist_loc].lockp = True; 203 lock_hist_loc++; 204 if (lock_hist_loc >= LOCK_HIST_SIZE) 205 lock_hist_loc = 0; 206} 207#endif /* XTHREADS_WARN */ 208 209static void _XUnlockDisplay( 210 Display *dpy 211 XTHREADS_FILE_LINE_ARGS 212 ) 213{ 214#ifdef XTHREADS_WARN 215 xthread_t self = xthread_self(); 216 217#ifdef XTHREADS_DEBUG 218 if (strcmp("XClearArea.c", file) && strcmp("XDrSegs.c", file)) /* ico */ 219 printf("%s line %d: thread %x unlocking display\n", file, line, self); 220#endif /* XTHREADS_DEBUG */ 221 222 if (!xthread_have_id(locking_thread)) 223 printf("Xlib ERROR: %s line %d thread %x: unlocking display that is not locked\n", 224 file, line, self); 225 else if (strcmp(file, "XlibInt.c") == 0) 226 xlibint_unlock = True; 227#ifdef XTHREADS_DEBUG 228 else if (strcmp(file, locking_file) != 0) 229 /* not always an error because locking_file is not per-thread */ 230 printf("%s line %d: unlocking display locked from %s line %d (probably okay)\n", 231 file, line, locking_file, locking_line); 232#endif /* XTHREADS_DEBUG */ 233 xthread_clear_id(locking_thread); 234 235 locking_history[lock_hist_loc].file = file; 236 locking_history[lock_hist_loc].line = line; 237 locking_history[lock_hist_loc].thread = self; 238 locking_history[lock_hist_loc].lockp = False; 239 lock_hist_loc++; 240 if (lock_hist_loc >= LOCK_HIST_SIZE) 241 lock_hist_loc = 0; 242#endif /* XTHREADS_WARN */ 243 xmutex_unlock(dpy->lock->mutex); 244} 245 246 247static struct _XCVList *_XCreateCVL( 248 Display *dpy) 249{ 250 struct _XCVList *cvl; 251 252 if ((cvl = dpy->lock->free_cvls) != NULL) { 253 dpy->lock->free_cvls = cvl->next; 254 dpy->lock->num_free_cvls--; 255 } else { 256 cvl = Xmalloc(sizeof(struct _XCVList)); 257 if (!cvl) 258 return NULL; 259 cvl->cv = xcondition_malloc(); 260 if (!cvl->cv) { 261 Xfree(cvl); 262 return NULL; 263 } 264 xcondition_init(cvl->cv); 265 xcondition_set_name(cvl->cv, "Xlib read queue"); 266 } 267 cvl->next = NULL; 268 return cvl; 269} 270 271/* Put ourselves on the queue to read the connection. 272 Allocates and returns a queue element. */ 273 274static struct _XCVList * 275_XPushReader( 276 Display *dpy, 277 struct _XCVList ***tail) 278{ 279 struct _XCVList *cvl; 280 281 cvl = _XCreateCVL(dpy); 282#ifdef XTHREADS_DEBUG 283 printf("_XPushReader called in thread %x, pushing %x\n", 284 xthread_self(), cvl); 285#endif 286 **tail = cvl; 287 *tail = &cvl->next; 288 return cvl; 289} 290 291/* signal the next thread waiting to read the connection */ 292 293static void _XPopReader( 294 Display *dpy, 295 struct _XCVList **list, 296 struct _XCVList ***tail) 297{ 298 register struct _XCVList *front = *list; 299 300#ifdef XTHREADS_DEBUG 301 printf("_XPopReader called in thread %x, popping %x\n", 302 xthread_self(), front); 303#endif 304 305 if (dpy->flags & XlibDisplayProcConni) 306 /* we never added ourself in the first place */ 307 return; 308 309 if (front) { /* check "front" for paranoia */ 310 *list = front->next; 311 if (*tail == &front->next) /* did we free the last elt? */ 312 *tail = list; 313 if (dpy->lock->num_free_cvls < NUM_FREE_CVLS) { 314 front->next = dpy->lock->free_cvls; 315 dpy->lock->free_cvls = front; 316 dpy->lock->num_free_cvls++; 317 } else { 318 xcondition_clear(front->cv); 319 Xfree(front->cv); 320 Xfree(front); 321 } 322 } 323 324 /* signal new front after it is in place */ 325 if ((dpy->lock->reply_first = (dpy->lock->reply_awaiters != NULL))) { 326 ConditionSignal(dpy, dpy->lock->reply_awaiters->cv); 327 } else if (dpy->lock->event_awaiters) { 328 ConditionSignal(dpy, dpy->lock->event_awaiters->cv); 329 } 330} 331 332static void _XConditionWait( 333 xcondition_t cv, 334 xmutex_t mutex 335 XTHREADS_FILE_LINE_ARGS 336 ) 337{ 338#ifdef XTHREADS_WARN 339 xthread_t self = xthread_self(); 340 char *old_file = locking_file; 341 int old_line = locking_line; 342 343#ifdef XTHREADS_DEBUG 344 printf("line %d thread %x in condition wait\n", line, self); 345#endif 346 xthread_clear_id(locking_thread); 347 348 locking_history[lock_hist_loc].file = file; 349 locking_history[lock_hist_loc].line = line; 350 locking_history[lock_hist_loc].thread = self; 351 locking_history[lock_hist_loc].lockp = False; 352 lock_hist_loc++; 353 if (lock_hist_loc >= LOCK_HIST_SIZE) 354 lock_hist_loc = 0; 355#endif /* XTHREADS_WARN */ 356 357 xcondition_wait(cv, mutex); 358 359#ifdef XTHREADS_WARN 360 locking_thread = self; 361 locking_file = old_file; 362 locking_line = old_line; 363 364 locking_history[lock_hist_loc].file = file; 365 locking_history[lock_hist_loc].line = line; 366 locking_history[lock_hist_loc].thread = self; 367 locking_history[lock_hist_loc].lockp = True; 368 lock_hist_loc++; 369 if (lock_hist_loc >= LOCK_HIST_SIZE) 370 lock_hist_loc = 0; 371#ifdef XTHREADS_DEBUG 372 printf("line %d thread %x was signaled\n", line, self); 373#endif /* XTHREADS_DEBUG */ 374#endif /* XTHREADS_WARN */ 375} 376 377static void _XConditionSignal( 378 xcondition_t cv 379 XTHREADS_FILE_LINE_ARGS 380 ) 381{ 382#ifdef XTHREADS_WARN 383#ifdef XTHREADS_DEBUG 384 printf("line %d thread %x is signalling\n", line, xthread_self()); 385#endif 386#endif 387 xcondition_signal(cv); 388} 389 390 391static void _XConditionBroadcast( 392 xcondition_t cv 393 XTHREADS_FILE_LINE_ARGS 394 ) 395{ 396#ifdef XTHREADS_WARN 397#ifdef XTHREADS_DEBUG 398 printf("line %d thread %x is broadcasting\n", line, xthread_self()); 399#endif 400#endif 401 xcondition_broadcast(cv); 402} 403 404 405static void _XFreeDisplayLock( 406 Display *dpy) 407{ 408 struct _XCVList *cvl; 409 410 if (dpy->lock != NULL) { 411 if (dpy->lock->mutex != NULL) { 412 xmutex_clear(dpy->lock->mutex); 413 xmutex_free(dpy->lock->mutex); 414 } 415 if (dpy->lock->cv != NULL) { 416 xcondition_clear(dpy->lock->cv); 417 xcondition_free(dpy->lock->cv); 418 } 419 if (dpy->lock->writers != NULL) { 420 xcondition_clear(dpy->lock->writers); 421 xcondition_free(dpy->lock->writers); 422 } 423 while ((cvl = dpy->lock->free_cvls)) { 424 dpy->lock->free_cvls = cvl->next; 425 xcondition_clear(cvl->cv); 426 Xfree(cvl->cv); 427 Xfree(cvl); 428 } 429 Xfree(dpy->lock); 430 dpy->lock = NULL; 431 } 432 if (dpy->lock_fns != NULL) { 433 Xfree(dpy->lock_fns); 434 dpy->lock_fns = NULL; 435 } 436} 437 438/* 439 * wait for thread with user-level display lock to release it. 440 */ 441 442static void _XDisplayLockWait( 443 Display *dpy) 444{ 445 xthread_t self; 446 447 while (dpy->lock->locking_level > 0) { 448 self = xthread_self(); 449 if (xthread_equal(dpy->lock->locking_thread, self)) 450 break; 451 ConditionWait(dpy, dpy->lock->cv); 452 } 453} 454 455static void _XLockDisplay( 456 Display *dpy 457 XTHREADS_FILE_LINE_ARGS 458 ); 459 460static void _XIfEventLockDisplay( 461 Display *dpy 462 XTHREADS_FILE_LINE_ARGS 463 ) 464{ 465 /* assert(dpy->in_ifevent); */ 466} 467 468static void _XIfEventUnlockDisplay( 469 Display *dpy 470 XTHREADS_FILE_LINE_ARGS 471 ) 472{ 473 if (dpy->in_ifevent) 474 return; 475 476 dpy->lock_fns->lock_display = _XLockDisplay; 477 dpy->lock_fns->unlock_display = _XUnlockDisplay; 478 UnlockDisplay(dpy); 479} 480 481static void _XLockDisplay( 482 Display *dpy 483 XTHREADS_FILE_LINE_ARGS 484 ) 485{ 486#ifdef XTHREADS 487 struct _XErrorThreadInfo *ti; 488#endif 489#ifdef XTHREADS_WARN 490 _XLockDisplayWarn(dpy, file, line); 491#else 492 xmutex_lock(dpy->lock->mutex); 493#endif 494 if (dpy->lock->locking_level > 0) 495 _XDisplayLockWait(dpy); 496#ifdef XTHREADS 497 /* 498 * Skip the two function calls below which may generate requests 499 * when LockDisplay is called from within _XError. 500 */ 501 for (ti = dpy->error_threads; ti; ti = ti->next) 502 if (ti->error_thread == xthread_self()) 503 return; 504#endif 505 _XIDHandler(dpy); 506 _XSeqSyncFunction(dpy); 507 if (dpy->in_ifevent) { 508 dpy->lock_fns->lock_display = _XIfEventLockDisplay; 509 dpy->lock_fns->unlock_display = _XIfEventUnlockDisplay; 510 } 511} 512 513/* 514 * _XReply is allowed to exit from select/poll and clean up even if a 515 * user-level lock is in force, so it uses this instead of _XFancyLockDisplay. 516 */ 517static void _XInternalLockDisplay( 518 Display *dpy, 519 Bool wskip 520 XTHREADS_FILE_LINE_ARGS 521 ) 522{ 523#ifdef XTHREADS_WARN 524 _XLockDisplayWarn(dpy, file, line); 525#else 526 xmutex_lock(dpy->lock->mutex); 527#endif 528 if (!wskip && dpy->lock->locking_level > 0) 529 _XDisplayLockWait(dpy); 530} 531 532static void _XUserLockDisplay( 533 register Display* dpy) 534{ 535 _XDisplayLockWait(dpy); 536 537 if (++dpy->lock->locking_level == 1) { 538 dpy->lock->lock_wait = _XDisplayLockWait; 539 dpy->lock->locking_thread = xthread_self(); 540 } 541} 542 543static 544void _XUserUnlockDisplay( 545 register Display* dpy) 546{ 547 if (dpy->lock->locking_level > 0 && --dpy->lock->locking_level == 0) { 548 /* signal other threads that might be waiting in XLockDisplay */ 549 ConditionBroadcast(dpy, dpy->lock->cv); 550 dpy->lock->lock_wait = NULL; 551 xthread_clear_id(dpy->lock->locking_thread); 552 } 553} 554 555/* returns 0 if initialized ok, -1 if unable to allocate 556 a mutex or other memory */ 557 558static int _XInitDisplayLock( 559 Display *dpy) 560{ 561 dpy->lock_fns = Xmalloc(sizeof(struct _XLockPtrs)); 562 if (dpy->lock_fns == NULL) 563 return -1; 564 dpy->lock = Xmalloc(sizeof(struct _XLockInfo)); 565 if (dpy->lock == NULL) { 566 _XFreeDisplayLock(dpy); 567 return -1; 568 } 569 dpy->lock->cv = xcondition_malloc(); 570 dpy->lock->mutex = xmutex_malloc(); 571 dpy->lock->writers = xcondition_malloc(); 572 if (!dpy->lock->cv || !dpy->lock->mutex || !dpy->lock->writers) { 573 _XFreeDisplayLock(dpy); 574 return -1; 575 } 576 577 dpy->lock->reply_bytes_left = 0; 578 dpy->lock->reply_was_read = False; 579 dpy->lock->reply_awaiters = NULL; 580 dpy->lock->reply_awaiters_tail = &dpy->lock->reply_awaiters; 581 dpy->lock->event_awaiters = NULL; 582 dpy->lock->event_awaiters_tail = &dpy->lock->event_awaiters; 583 dpy->lock->reply_first = False; 584 dpy->lock->locking_level = 0; 585 dpy->lock->num_free_cvls = 0; 586 dpy->lock->free_cvls = NULL; 587 xthread_clear_id(dpy->lock->locking_thread); 588 xthread_clear_id(dpy->lock->reading_thread); 589 xthread_clear_id(dpy->lock->conni_thread); 590 xmutex_init(dpy->lock->mutex); 591 xmutex_set_name(dpy->lock->mutex, "Xlib Display"); 592 xcondition_init(dpy->lock->cv); 593 xcondition_set_name(dpy->lock->cv, "XLockDisplay"); 594 xcondition_init(dpy->lock->writers); 595 xcondition_set_name(dpy->lock->writers, "Xlib wait for writable"); 596 dpy->lock_fns->lock_display = _XLockDisplay; 597 dpy->lock->internal_lock_display = _XInternalLockDisplay; 598 dpy->lock_fns->unlock_display = _XUnlockDisplay; 599 dpy->lock->user_lock_display = _XUserLockDisplay; 600 dpy->lock->user_unlock_display = _XUserUnlockDisplay; 601 dpy->lock->pop_reader = _XPopReader; 602 dpy->lock->push_reader = _XPushReader; 603 dpy->lock->condition_wait = _XConditionWait; 604 dpy->lock->condition_signal = _XConditionSignal; 605 dpy->lock->condition_broadcast = _XConditionBroadcast; 606 dpy->lock->create_cvl = _XCreateCVL; 607 dpy->lock->lock_wait = NULL; /* filled in by XLockDisplay() */ 608 609 return 0; 610} 611 612#ifdef __UNIXWARE__ 613xthread_t __x11_thr_self() { return 0; } 614xthread_t (*_x11_thr_self)() = __x11_thr_self; 615#endif 616 617 618Status XInitThreads(void) 619{ 620 if (_Xglobal_lock) 621 return 1; 622#ifdef __UNIXWARE__ 623 else { 624 void *dl_handle = dlopen(NULL, RTLD_LAZY); 625 if (!dl_handle || 626 ((_x11_thr_self = (xthread_t(*)())dlsym(dl_handle,"thr_self")) == 0)) { 627 _x11_thr_self = __x11_thr_self; 628 (void) fprintf (stderr, 629 "XInitThreads called, but no libthread in the calling program!\n" ); 630 } 631 } 632#endif /* __UNIXWARE__ */ 633#ifdef xthread_init 634 xthread_init(); /* return value? */ 635#endif 636 if (!(global_lock.lock = xmutex_malloc())) 637 return 0; 638 if (!(i18n_lock.lock = xmutex_malloc())) { 639 xmutex_free(global_lock.lock); 640 global_lock.lock = NULL; 641 return 0; 642 } 643 if (!(conv_lock.lock = xmutex_malloc())) { 644 xmutex_free(global_lock.lock); 645 global_lock.lock = NULL; 646 xmutex_free(i18n_lock.lock); 647 i18n_lock.lock = NULL; 648 return 0; 649 } 650 _Xglobal_lock = &global_lock; 651 xmutex_init(_Xglobal_lock->lock); 652 xmutex_set_name(_Xglobal_lock->lock, "Xlib global"); 653 _Xi18n_lock = &i18n_lock; 654 xmutex_init(_Xi18n_lock->lock); 655 xmutex_set_name(_Xi18n_lock->lock, "Xlib i18n"); 656 _conv_lock = &conv_lock; 657 xmutex_init(_conv_lock->lock); 658 xmutex_set_name(_conv_lock->lock, "Xlib conv"); 659 _XLockMutex_fn = _XLockMutex; 660 _XUnlockMutex_fn = _XUnlockMutex; 661 _XCreateMutex_fn = _XCreateMutex; 662 _XFreeMutex_fn = _XFreeMutex; 663 _XInitDisplayLock_fn = _XInitDisplayLock; 664 _XFreeDisplayLock_fn = _XFreeDisplayLock; 665 _Xthread_self_fn = _Xthread_self; 666 667#ifdef XTHREADS_WARN 668#ifdef XTHREADS_DEBUG 669 setlinebuf(stdout); /* for debugging messages */ 670#endif 671#endif 672 673 return 1; 674} 675 676#else /* XTHREADS */ 677Status XInitThreads(void) 678{ 679 return 0; 680} 681#endif /* XTHREADS */ 682