locking.c revision 88de56cc
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 = (struct _xthread_waiter *)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 = (struct _XCVList *)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((char *)front->cv); 316 Xfree((char *)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((char *)cvl->cv); 423 Xfree((char *)cvl); 424 } 425 Xfree((char *)dpy->lock); 426 dpy->lock = NULL; 427 } 428 if (dpy->lock_fns != NULL) { 429 Xfree((char *)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 if (++dpy->lock->locking_level == 1) { 490 dpy->lock->lock_wait = _XDisplayLockWait; 491 dpy->lock->locking_thread = xthread_self(); 492 } 493} 494 495static 496void _XUserUnlockDisplay( 497 register Display* dpy) 498{ 499 if (dpy->lock->locking_level > 0 && --dpy->lock->locking_level == 0) { 500 /* signal other threads that might be waiting in XLockDisplay */ 501 ConditionBroadcast(dpy, dpy->lock->cv); 502 dpy->lock->lock_wait = NULL; 503 xthread_clear_id(dpy->lock->locking_thread); 504 } 505} 506 507/* returns 0 if initialized ok, -1 if unable to allocate 508 a mutex or other memory */ 509 510static int _XInitDisplayLock( 511 Display *dpy) 512{ 513 dpy->lock_fns = (struct _XLockPtrs*)Xmalloc(sizeof(struct _XLockPtrs)); 514 if (dpy->lock_fns == NULL) 515 return -1; 516 dpy->lock = (struct _XLockInfo *)Xmalloc(sizeof(struct _XLockInfo)); 517 if (dpy->lock == NULL) { 518 _XFreeDisplayLock(dpy); 519 return -1; 520 } 521 dpy->lock->cv = xcondition_malloc(); 522 dpy->lock->mutex = xmutex_malloc(); 523 dpy->lock->writers = xcondition_malloc(); 524 if (!dpy->lock->cv || !dpy->lock->mutex || !dpy->lock->writers) { 525 _XFreeDisplayLock(dpy); 526 return -1; 527 } 528 529 dpy->lock->reply_bytes_left = 0; 530 dpy->lock->reply_was_read = False; 531 dpy->lock->reply_awaiters = NULL; 532 dpy->lock->reply_awaiters_tail = &dpy->lock->reply_awaiters; 533 dpy->lock->event_awaiters = NULL; 534 dpy->lock->event_awaiters_tail = &dpy->lock->event_awaiters; 535 dpy->lock->reply_first = False; 536 dpy->lock->locking_level = 0; 537 dpy->lock->num_free_cvls = 0; 538 dpy->lock->free_cvls = NULL; 539 xthread_clear_id(dpy->lock->locking_thread); 540 xthread_clear_id(dpy->lock->reading_thread); 541 xthread_clear_id(dpy->lock->conni_thread); 542 xmutex_init(dpy->lock->mutex); 543 xmutex_set_name(dpy->lock->mutex, "Xlib Display"); 544 xcondition_init(dpy->lock->cv); 545 xcondition_set_name(dpy->lock->cv, "XLockDisplay"); 546 xcondition_init(dpy->lock->writers); 547 xcondition_set_name(dpy->lock->writers, "Xlib wait for writable"); 548 dpy->lock_fns->lock_display = _XLockDisplay; 549 dpy->lock->internal_lock_display = _XInternalLockDisplay; 550 dpy->lock_fns->unlock_display = _XUnlockDisplay; 551 dpy->lock->user_lock_display = _XUserLockDisplay; 552 dpy->lock->user_unlock_display = _XUserUnlockDisplay; 553 dpy->lock->pop_reader = _XPopReader; 554 dpy->lock->push_reader = _XPushReader; 555 dpy->lock->condition_wait = _XConditionWait; 556 dpy->lock->condition_signal = _XConditionSignal; 557 dpy->lock->condition_broadcast = _XConditionBroadcast; 558 dpy->lock->create_cvl = _XCreateCVL; 559 dpy->lock->lock_wait = NULL; /* filled in by XLockDisplay() */ 560 561 return 0; 562} 563 564#ifdef __UNIXWARE__ 565xthread_t __x11_thr_self() { return 0; } 566xthread_t (*_x11_thr_self)() = __x11_thr_self; 567#endif 568 569 570Status XInitThreads(void) 571{ 572 if (_Xglobal_lock) 573 return 1; 574#ifdef __UNIXWARE__ 575 else { 576 void *dl_handle = dlopen(NULL, RTLD_LAZY); 577 if (!dl_handle || 578 ((_x11_thr_self = (xthread_t(*)())dlsym(dl_handle,"thr_self")) == 0)) { 579 _x11_thr_self = __x11_thr_self; 580 (void) fprintf (stderr, 581 "XInitThreads called, but no libthread in the calling program!\n" ); 582 } 583 } 584#endif /* __UNIXWARE__ */ 585#ifdef xthread_init 586 xthread_init(); /* return value? */ 587#endif 588 if (!(global_lock.lock = xmutex_malloc())) 589 return 0; 590 if (!(i18n_lock.lock = xmutex_malloc())) { 591 xmutex_free(global_lock.lock); 592 global_lock.lock = NULL; 593 return 0; 594 } 595 _Xglobal_lock = &global_lock; 596 xmutex_init(_Xglobal_lock->lock); 597 xmutex_set_name(_Xglobal_lock->lock, "Xlib global"); 598 _Xi18n_lock = &i18n_lock; 599 xmutex_init(_Xi18n_lock->lock); 600 xmutex_set_name(_Xi18n_lock->lock, "Xlib i18n"); 601 _XLockMutex_fn = _XLockMutex; 602 _XUnlockMutex_fn = _XUnlockMutex; 603 _XCreateMutex_fn = _XCreateMutex; 604 _XFreeMutex_fn = _XFreeMutex; 605 _XInitDisplayLock_fn = _XInitDisplayLock; 606 _XFreeDisplayLock_fn = _XFreeDisplayLock; 607 _Xthread_self_fn = _Xthread_self; 608 609#ifdef XTHREADS_WARN 610#ifdef XTHREADS_DEBUG 611 setlinebuf(stdout); /* for debugging messages */ 612#endif 613#endif 614 615 return 1; 616} 617 618#else /* XTHREADS */ 619Status XInitThreads(void) 620{ 621 return 0; 622} 623#endif /* XTHREADS */ 624