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