locking.c revision d4a3aaf4
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{ 460#ifdef XTHREADS 461 struct _XErrorThreadInfo *ti; 462#endif 463#ifdef XTHREADS_WARN 464 _XLockDisplayWarn(dpy, file, line); 465#else 466 xmutex_lock(dpy->lock->mutex); 467#endif 468 if (dpy->lock->locking_level > 0) 469 _XDisplayLockWait(dpy); 470#ifdef XTHREADS 471 /* 472 * Skip the two function calls below which may generate requests 473 * when LockDisplay is called from within _XError. 474 */ 475 for (ti = dpy->error_threads; ti; ti = ti->next) 476 if (ti->error_thread == xthread_self()) 477 return; 478#endif 479 _XIDHandler(dpy); 480 _XSeqSyncFunction(dpy); 481} 482 483/* 484 * _XReply is allowed to exit from select/poll and clean up even if a 485 * user-level lock is in force, so it uses this instead of _XFancyLockDisplay. 486 */ 487static void _XInternalLockDisplay( 488 Display *dpy, 489 Bool wskip 490 XTHREADS_FILE_LINE_ARGS 491 ) 492{ 493#ifdef XTHREADS_WARN 494 _XLockDisplayWarn(dpy, file, line); 495#else 496 xmutex_lock(dpy->lock->mutex); 497#endif 498 if (!wskip && dpy->lock->locking_level > 0) 499 _XDisplayLockWait(dpy); 500} 501 502static void _XUserLockDisplay( 503 register Display* dpy) 504{ 505 _XDisplayLockWait(dpy); 506 507 if (++dpy->lock->locking_level == 1) { 508 dpy->lock->lock_wait = _XDisplayLockWait; 509 dpy->lock->locking_thread = xthread_self(); 510 } 511} 512 513static 514void _XUserUnlockDisplay( 515 register Display* dpy) 516{ 517 if (dpy->lock->locking_level > 0 && --dpy->lock->locking_level == 0) { 518 /* signal other threads that might be waiting in XLockDisplay */ 519 ConditionBroadcast(dpy, dpy->lock->cv); 520 dpy->lock->lock_wait = NULL; 521 xthread_clear_id(dpy->lock->locking_thread); 522 } 523} 524 525/* returns 0 if initialized ok, -1 if unable to allocate 526 a mutex or other memory */ 527 528static int _XInitDisplayLock( 529 Display *dpy) 530{ 531 dpy->lock_fns = Xmalloc(sizeof(struct _XLockPtrs)); 532 if (dpy->lock_fns == NULL) 533 return -1; 534 dpy->lock = Xmalloc(sizeof(struct _XLockInfo)); 535 if (dpy->lock == NULL) { 536 _XFreeDisplayLock(dpy); 537 return -1; 538 } 539 dpy->lock->cv = xcondition_malloc(); 540 dpy->lock->mutex = xmutex_malloc(); 541 dpy->lock->writers = xcondition_malloc(); 542 if (!dpy->lock->cv || !dpy->lock->mutex || !dpy->lock->writers) { 543 _XFreeDisplayLock(dpy); 544 return -1; 545 } 546 547 dpy->lock->reply_bytes_left = 0; 548 dpy->lock->reply_was_read = False; 549 dpy->lock->reply_awaiters = NULL; 550 dpy->lock->reply_awaiters_tail = &dpy->lock->reply_awaiters; 551 dpy->lock->event_awaiters = NULL; 552 dpy->lock->event_awaiters_tail = &dpy->lock->event_awaiters; 553 dpy->lock->reply_first = False; 554 dpy->lock->locking_level = 0; 555 dpy->lock->num_free_cvls = 0; 556 dpy->lock->free_cvls = NULL; 557 xthread_clear_id(dpy->lock->locking_thread); 558 xthread_clear_id(dpy->lock->reading_thread); 559 xthread_clear_id(dpy->lock->conni_thread); 560 xmutex_init(dpy->lock->mutex); 561 xmutex_set_name(dpy->lock->mutex, "Xlib Display"); 562 xcondition_init(dpy->lock->cv); 563 xcondition_set_name(dpy->lock->cv, "XLockDisplay"); 564 xcondition_init(dpy->lock->writers); 565 xcondition_set_name(dpy->lock->writers, "Xlib wait for writable"); 566 dpy->lock_fns->lock_display = _XLockDisplay; 567 dpy->lock->internal_lock_display = _XInternalLockDisplay; 568 dpy->lock_fns->unlock_display = _XUnlockDisplay; 569 dpy->lock->user_lock_display = _XUserLockDisplay; 570 dpy->lock->user_unlock_display = _XUserUnlockDisplay; 571 dpy->lock->pop_reader = _XPopReader; 572 dpy->lock->push_reader = _XPushReader; 573 dpy->lock->condition_wait = _XConditionWait; 574 dpy->lock->condition_signal = _XConditionSignal; 575 dpy->lock->condition_broadcast = _XConditionBroadcast; 576 dpy->lock->create_cvl = _XCreateCVL; 577 dpy->lock->lock_wait = NULL; /* filled in by XLockDisplay() */ 578 579 return 0; 580} 581 582#ifdef __UNIXWARE__ 583xthread_t __x11_thr_self() { return 0; } 584xthread_t (*_x11_thr_self)() = __x11_thr_self; 585#endif 586 587 588Status XInitThreads(void) 589{ 590 if (_Xglobal_lock) 591 return 1; 592#ifdef __UNIXWARE__ 593 else { 594 void *dl_handle = dlopen(NULL, RTLD_LAZY); 595 if (!dl_handle || 596 ((_x11_thr_self = (xthread_t(*)())dlsym(dl_handle,"thr_self")) == 0)) { 597 _x11_thr_self = __x11_thr_self; 598 (void) fprintf (stderr, 599 "XInitThreads called, but no libthread in the calling program!\n" ); 600 } 601 } 602#endif /* __UNIXWARE__ */ 603#ifdef xthread_init 604 xthread_init(); /* return value? */ 605#endif 606 if (!(global_lock.lock = xmutex_malloc())) 607 return 0; 608 if (!(i18n_lock.lock = xmutex_malloc())) { 609 xmutex_free(global_lock.lock); 610 global_lock.lock = NULL; 611 return 0; 612 } 613 if (!(conv_lock.lock = xmutex_malloc())) { 614 xmutex_free(global_lock.lock); 615 global_lock.lock = NULL; 616 xmutex_free(i18n_lock.lock); 617 i18n_lock.lock = NULL; 618 return 0; 619 } 620 _Xglobal_lock = &global_lock; 621 xmutex_init(_Xglobal_lock->lock); 622 xmutex_set_name(_Xglobal_lock->lock, "Xlib global"); 623 _Xi18n_lock = &i18n_lock; 624 xmutex_init(_Xi18n_lock->lock); 625 xmutex_set_name(_Xi18n_lock->lock, "Xlib i18n"); 626 _conv_lock = &conv_lock; 627 xmutex_init(_conv_lock->lock); 628 xmutex_set_name(_conv_lock->lock, "Xlib conv"); 629 _XLockMutex_fn = _XLockMutex; 630 _XUnlockMutex_fn = _XUnlockMutex; 631 _XCreateMutex_fn = _XCreateMutex; 632 _XFreeMutex_fn = _XFreeMutex; 633 _XInitDisplayLock_fn = _XInitDisplayLock; 634 _XFreeDisplayLock_fn = _XFreeDisplayLock; 635 _Xthread_self_fn = _Xthread_self; 636 637#ifdef XTHREADS_WARN 638#ifdef XTHREADS_DEBUG 639 setlinebuf(stdout); /* for debugging messages */ 640#endif 641#endif 642 643 return 1; 644} 645 646#else /* XTHREADS */ 647Status XInitThreads(void) 648{ 649 return 0; 650} 651#endif /* XTHREADS */ 652