1 /* 2 3 Copyright 1992, 1998 The Open Group 4 5 Permission to use, copy, modify, distribute, and sell this software and its 6 documentation for any purpose is hereby granted without fee, provided that 7 the above copyright notice appear in all copies and that both that 8 copyright notice and this permission notice appear in supporting 9 documentation. 10 11 The above copyright notice and this permission notice shall be included in 12 all copies or substantial portions of the Software. 13 14 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 OPEN GROUP BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN 18 AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 19 CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 20 21 Except as contained in this notice, the name of The Open Group shall not be 22 used in advertising or otherwise to promote the sale, use or other dealings 23 in 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 */ 68 extern LockInfoPtr _Xi18n_lock; 69 /* in lcConv.c */ 70 extern LockInfoPtr _conv_lock; 71 72 #ifdef WIN32 73 static DWORD _X_TlsIndex = (DWORD)-1; 74 75 void _Xthread_init(void) 76 { 77 if (_X_TlsIndex == (DWORD)-1) 78 _X_TlsIndex = TlsAlloc(); 79 } 80 81 struct _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 96 static xthread_t _Xthread_self(void) 97 { 98 return xthread_self(); 99 } 100 101 static LockInfoRec global_lock; 102 static LockInfoRec i18n_lock; 103 static LockInfoRec conv_lock; 104 105 static void _XLockMutex( 106 LockInfoPtr lip 107 XTHREADS_FILE_LINE_ARGS 108 ) 109 { 110 xmutex_lock(lip->lock); 111 } 112 113 static void _XUnlockMutex( 114 LockInfoPtr lip 115 XTHREADS_FILE_LINE_ARGS 116 ) 117 { 118 xmutex_unlock(lip->lock); 119 } 120 121 static 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 131 static 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 140 static char *locking_file; 141 static int locking_line; 142 static xthread_t locking_thread; 143 static 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 148 static 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 155 int lock_hist_loc = 0; /* next slot to fill */ 156 157 static 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 209 static 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 244 if (dpy->in_ifevent == 0 || !xthread_equal(dpy->ifevent_thread, xthread_self())) 245 xmutex_unlock(dpy->lock->mutex); 246 } 247 248 249 static struct _XCVList *_XCreateCVL( 250 Display *dpy) 251 { 252 struct _XCVList *cvl; 253 254 if ((cvl = dpy->lock->free_cvls) != NULL) { 255 dpy->lock->free_cvls = cvl->next; 256 dpy->lock->num_free_cvls--; 257 } else { 258 cvl = Xmalloc(sizeof(struct _XCVList)); 259 if (!cvl) 260 return NULL; 261 cvl->cv = xcondition_malloc(); 262 if (!cvl->cv) { 263 Xfree(cvl); 264 return NULL; 265 } 266 xcondition_init(cvl->cv); 267 xcondition_set_name(cvl->cv, "Xlib read queue"); 268 } 269 cvl->next = NULL; 270 return cvl; 271 } 272 273 /* Put ourselves on the queue to read the connection. 274 Allocates and returns a queue element. */ 275 276 static struct _XCVList * 277 _XPushReader( 278 Display *dpy, 279 struct _XCVList ***tail) 280 { 281 struct _XCVList *cvl; 282 283 cvl = _XCreateCVL(dpy); 284 #ifdef XTHREADS_DEBUG 285 printf("_XPushReader called in thread %x, pushing %x\n", 286 xthread_self(), cvl); 287 #endif 288 **tail = cvl; 289 *tail = &cvl->next; 290 return cvl; 291 } 292 293 /* signal the next thread waiting to read the connection */ 294 295 static void _XPopReader( 296 Display *dpy, 297 struct _XCVList **list, 298 struct _XCVList ***tail) 299 { 300 register struct _XCVList *front = *list; 301 302 #ifdef XTHREADS_DEBUG 303 printf("_XPopReader called in thread %x, popping %x\n", 304 xthread_self(), front); 305 #endif 306 307 if (dpy->flags & XlibDisplayProcConni) 308 /* we never added ourself in the first place */ 309 return; 310 311 if (front) { /* check "front" for paranoia */ 312 *list = front->next; 313 if (*tail == &front->next) /* did we free the last elt? */ 314 *tail = list; 315 if (dpy->lock->num_free_cvls < NUM_FREE_CVLS) { 316 front->next = dpy->lock->free_cvls; 317 dpy->lock->free_cvls = front; 318 dpy->lock->num_free_cvls++; 319 } else { 320 xcondition_clear(front->cv); 321 Xfree(front->cv); 322 Xfree(front); 323 } 324 } 325 326 /* signal new front after it is in place */ 327 if ((dpy->lock->reply_first = (dpy->lock->reply_awaiters != NULL))) { 328 ConditionSignal(dpy, dpy->lock->reply_awaiters->cv); 329 } else if (dpy->lock->event_awaiters) { 330 ConditionSignal(dpy, dpy->lock->event_awaiters->cv); 331 } 332 } 333 334 static void _XConditionWait( 335 xcondition_t cv, 336 xmutex_t mutex 337 XTHREADS_FILE_LINE_ARGS 338 ) 339 { 340 #ifdef XTHREADS_WARN 341 xthread_t self = xthread_self(); 342 char *old_file = locking_file; 343 int old_line = locking_line; 344 345 #ifdef XTHREADS_DEBUG 346 printf("line %d thread %x in condition wait\n", line, self); 347 #endif 348 xthread_clear_id(locking_thread); 349 350 locking_history[lock_hist_loc].file = file; 351 locking_history[lock_hist_loc].line = line; 352 locking_history[lock_hist_loc].thread = self; 353 locking_history[lock_hist_loc].lockp = False; 354 lock_hist_loc++; 355 if (lock_hist_loc >= LOCK_HIST_SIZE) 356 lock_hist_loc = 0; 357 #endif /* XTHREADS_WARN */ 358 359 xcondition_wait(cv, mutex); 360 361 #ifdef XTHREADS_WARN 362 locking_thread = self; 363 locking_file = old_file; 364 locking_line = old_line; 365 366 locking_history[lock_hist_loc].file = file; 367 locking_history[lock_hist_loc].line = line; 368 locking_history[lock_hist_loc].thread = self; 369 locking_history[lock_hist_loc].lockp = True; 370 lock_hist_loc++; 371 if (lock_hist_loc >= LOCK_HIST_SIZE) 372 lock_hist_loc = 0; 373 #ifdef XTHREADS_DEBUG 374 printf("line %d thread %x was signaled\n", line, self); 375 #endif /* XTHREADS_DEBUG */ 376 #endif /* XTHREADS_WARN */ 377 } 378 379 static void _XConditionSignal( 380 xcondition_t cv 381 XTHREADS_FILE_LINE_ARGS 382 ) 383 { 384 #ifdef XTHREADS_WARN 385 #ifdef XTHREADS_DEBUG 386 printf("line %d thread %x is signalling\n", line, xthread_self()); 387 #endif 388 #endif 389 xcondition_signal(cv); 390 } 391 392 393 static void _XConditionBroadcast( 394 xcondition_t cv 395 XTHREADS_FILE_LINE_ARGS 396 ) 397 { 398 #ifdef XTHREADS_WARN 399 #ifdef XTHREADS_DEBUG 400 printf("line %d thread %x is broadcasting\n", line, xthread_self()); 401 #endif 402 #endif 403 xcondition_broadcast(cv); 404 } 405 406 407 static void _XFreeDisplayLock( 408 Display *dpy) 409 { 410 struct _XCVList *cvl; 411 412 if (dpy->lock != NULL) { 413 if (dpy->lock->mutex != NULL) { 414 xmutex_clear(dpy->lock->mutex); 415 xmutex_free(dpy->lock->mutex); 416 } 417 if (dpy->lock->cv != NULL) { 418 xcondition_clear(dpy->lock->cv); 419 xcondition_free(dpy->lock->cv); 420 } 421 if (dpy->lock->writers != NULL) { 422 xcondition_clear(dpy->lock->writers); 423 xcondition_free(dpy->lock->writers); 424 } 425 while ((cvl = dpy->lock->free_cvls)) { 426 dpy->lock->free_cvls = cvl->next; 427 xcondition_clear(cvl->cv); 428 Xfree(cvl->cv); 429 Xfree(cvl); 430 } 431 Xfree(dpy->lock); 432 dpy->lock = NULL; 433 } 434 if (dpy->lock_fns != NULL) { 435 Xfree(dpy->lock_fns); 436 dpy->lock_fns = NULL; 437 } 438 } 439 440 /* 441 * wait for thread with user-level display lock to release it. 442 */ 443 444 static void _XDisplayLockWait( 445 Display *dpy) 446 { 447 xthread_t self; 448 449 while (dpy->lock->locking_level > 0) { 450 self = xthread_self(); 451 if (xthread_equal(dpy->lock->locking_thread, self)) 452 break; 453 ConditionWait(dpy, dpy->lock->cv); 454 } 455 } 456 457 static void _XLockDisplay( 458 Display *dpy 459 XTHREADS_FILE_LINE_ARGS 460 ) 461 { 462 struct _XErrorThreadInfo *ti; 463 464 if (dpy->in_ifevent && xthread_equal(dpy->ifevent_thread, xthread_self())) 465 return; 466 467 #ifdef XTHREADS_WARN 468 _XLockDisplayWarn(dpy, file, line); 469 #else 470 xmutex_lock(dpy->lock->mutex); 471 #endif 472 473 if (dpy->lock->locking_level > 0) 474 _XDisplayLockWait(dpy); 475 476 /* 477 * Skip the two function calls below which may generate requests 478 * when LockDisplay is called from within _XError. 479 */ 480 for (ti = dpy->error_threads; ti; ti = ti->next) 481 if (ti->error_thread == xthread_self()) 482 return; 483 484 _XIDHandler(dpy); 485 _XSeqSyncFunction(dpy); 486 } 487 488 /* 489 * _XReply is allowed to exit from select/poll and clean up even if a 490 * user-level lock is in force, so it uses this instead of _XFancyLockDisplay. 491 */ 492 static void _XInternalLockDisplay( 493 Display *dpy, 494 Bool wskip 495 XTHREADS_FILE_LINE_ARGS 496 ) 497 { 498 if (dpy->in_ifevent && xthread_equal(dpy->ifevent_thread, xthread_self())) 499 return; 500 501 #ifdef XTHREADS_WARN 502 _XLockDisplayWarn(dpy, file, line); 503 #else 504 xmutex_lock(dpy->lock->mutex); 505 #endif 506 if (!wskip && dpy->lock->locking_level > 0) 507 _XDisplayLockWait(dpy); 508 } 509 510 static void _XUserLockDisplay( 511 register Display* dpy) 512 { 513 _XDisplayLockWait(dpy); 514 515 if (++dpy->lock->locking_level == 1) { 516 dpy->lock->lock_wait = _XDisplayLockWait; 517 dpy->lock->locking_thread = xthread_self(); 518 } 519 } 520 521 static 522 void _XUserUnlockDisplay( 523 register Display* dpy) 524 { 525 if (dpy->lock->locking_level > 0 && --dpy->lock->locking_level == 0) { 526 /* signal other threads that might be waiting in XLockDisplay */ 527 ConditionBroadcast(dpy, dpy->lock->cv); 528 dpy->lock->lock_wait = NULL; 529 xthread_clear_id(dpy->lock->locking_thread); 530 } 531 } 532 533 /* returns 0 if initialized ok, -1 if unable to allocate 534 a mutex or other memory */ 535 536 static int _XInitDisplayLock( 537 Display *dpy) 538 { 539 dpy->lock_fns = Xmalloc(sizeof(struct _XLockPtrs)); 540 if (dpy->lock_fns == NULL) 541 return -1; 542 dpy->lock = Xmalloc(sizeof(struct _XLockInfo)); 543 if (dpy->lock == NULL) { 544 _XFreeDisplayLock(dpy); 545 return -1; 546 } 547 dpy->lock->cv = xcondition_malloc(); 548 dpy->lock->mutex = xmutex_malloc(); 549 dpy->lock->writers = xcondition_malloc(); 550 if (!dpy->lock->cv || !dpy->lock->mutex || !dpy->lock->writers) { 551 _XFreeDisplayLock(dpy); 552 return -1; 553 } 554 555 dpy->lock->reply_bytes_left = 0; 556 dpy->lock->reply_was_read = False; 557 dpy->lock->reply_awaiters = NULL; 558 dpy->lock->reply_awaiters_tail = &dpy->lock->reply_awaiters; 559 dpy->lock->event_awaiters = NULL; 560 dpy->lock->event_awaiters_tail = &dpy->lock->event_awaiters; 561 dpy->lock->reply_first = False; 562 dpy->lock->locking_level = 0; 563 dpy->lock->num_free_cvls = 0; 564 dpy->lock->free_cvls = NULL; 565 xthread_clear_id(dpy->lock->locking_thread); 566 xthread_clear_id(dpy->lock->reading_thread); 567 xthread_clear_id(dpy->lock->conni_thread); 568 xmutex_init(dpy->lock->mutex); 569 xmutex_set_name(dpy->lock->mutex, "Xlib Display"); 570 xcondition_init(dpy->lock->cv); 571 xcondition_set_name(dpy->lock->cv, "XLockDisplay"); 572 xcondition_init(dpy->lock->writers); 573 xcondition_set_name(dpy->lock->writers, "Xlib wait for writable"); 574 dpy->lock_fns->lock_display = _XLockDisplay; 575 dpy->lock->internal_lock_display = _XInternalLockDisplay; 576 dpy->lock_fns->unlock_display = _XUnlockDisplay; 577 dpy->lock->user_lock_display = _XUserLockDisplay; 578 dpy->lock->user_unlock_display = _XUserUnlockDisplay; 579 dpy->lock->pop_reader = _XPopReader; 580 dpy->lock->push_reader = _XPushReader; 581 dpy->lock->condition_wait = _XConditionWait; 582 dpy->lock->condition_signal = _XConditionSignal; 583 dpy->lock->condition_broadcast = _XConditionBroadcast; 584 dpy->lock->create_cvl = _XCreateCVL; 585 dpy->lock->lock_wait = NULL; /* filled in by XLockDisplay() */ 586 587 return 0; 588 } 589 590 #ifdef __UNIXWARE__ 591 xthread_t __x11_thr_self() { return 0; } 592 xthread_t (*_x11_thr_self)() = __x11_thr_self; 593 #endif 594 595 596 Status XInitThreads(void) 597 { 598 if (_Xglobal_lock) 599 return 1; 600 #ifdef __UNIXWARE__ 601 else { 602 void *dl_handle = dlopen(NULL, RTLD_LAZY); 603 if (!dl_handle || 604 ((_x11_thr_self = (xthread_t(*)())dlsym(dl_handle,"thr_self")) == 0)) { 605 _x11_thr_self = __x11_thr_self; 606 (void) fprintf (stderr, 607 "XInitThreads called, but no libthread in the calling program!\n" ); 608 } 609 } 610 #endif /* __UNIXWARE__ */ 611 #ifdef xthread_init 612 xthread_init(); /* return value? */ 613 #endif 614 if (!(global_lock.lock = xmutex_malloc())) 615 return 0; 616 if (!(i18n_lock.lock = xmutex_malloc())) { 617 xmutex_free(global_lock.lock); 618 global_lock.lock = NULL; 619 return 0; 620 } 621 if (!(conv_lock.lock = xmutex_malloc())) { 622 xmutex_free(global_lock.lock); 623 global_lock.lock = NULL; 624 xmutex_free(i18n_lock.lock); 625 i18n_lock.lock = NULL; 626 return 0; 627 } 628 _Xglobal_lock = &global_lock; 629 xmutex_init(_Xglobal_lock->lock); 630 xmutex_set_name(_Xglobal_lock->lock, "Xlib global"); 631 _Xi18n_lock = &i18n_lock; 632 xmutex_init(_Xi18n_lock->lock); 633 xmutex_set_name(_Xi18n_lock->lock, "Xlib i18n"); 634 _conv_lock = &conv_lock; 635 xmutex_init(_conv_lock->lock); 636 xmutex_set_name(_conv_lock->lock, "Xlib conv"); 637 _XLockMutex_fn = _XLockMutex; 638 _XUnlockMutex_fn = _XUnlockMutex; 639 _XCreateMutex_fn = _XCreateMutex; 640 _XFreeMutex_fn = _XFreeMutex; 641 _XInitDisplayLock_fn = _XInitDisplayLock; 642 _XFreeDisplayLock_fn = _XFreeDisplayLock; 643 _Xthread_self_fn = _Xthread_self; 644 645 #ifdef XTHREADS_WARN 646 #ifdef XTHREADS_DEBUG 647 setlinebuf(stdout); /* for debugging messages */ 648 #endif 649 #endif 650 651 return 1; 652 } 653 654 Status XFreeThreads(void) 655 { 656 if (global_lock.lock != NULL) { 657 xmutex_free(global_lock.lock); 658 global_lock.lock = NULL; 659 } 660 if (i18n_lock.lock != NULL) { 661 xmutex_free(i18n_lock.lock); 662 i18n_lock.lock = NULL; 663 } 664 if (conv_lock.lock != NULL) { 665 xmutex_free(conv_lock.lock); 666 conv_lock.lock = NULL; 667 } 668 669 return 1; 670 } 671 672 #else /* XTHREADS */ 673 Status XInitThreads(void) 674 { 675 return 0; 676 } 677 678 Status XFreeThreads(void) 679 { 680 return 0; 681 } 682 #endif /* XTHREADS */ 683