3 Copyright 1992, 1998 The Open Group
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
11 The above copyright notice and this permission notice shall be included in
12 all copies or substantial portions of the Software.
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.
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.
28 * Author: Stephen Gildea, MIT X Consortium
30 * locking.c - multi-thread locking routines implemented in C Threads
51 #include <stdio.h> /* for warn/debug stuff */
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 \
58 char* file, /* source file, from macro */ \
61 # define XTHREADS_FILE_LINE_ARGS /* None */
65 #define NUM_FREE_CVLS 4
68 extern LockInfoPtr _Xi18n_lock;
71 static DWORD _X_TlsIndex = (DWORD)-1;
73 void _Xthread_init(void)
75 if (_X_TlsIndex == (DWORD)-1)
76 _X_TlsIndex = TlsAlloc();
79 struct _xthread_waiter *
82 struct _xthread_waiter *me;
84 if (!(me = TlsGetValue(_X_TlsIndex))) {
85 me = (struct _xthread_waiter *)xmalloc(sizeof(struct _xthread_waiter));
86 me->sem = CreateSemaphore(NULL, 0, 1, NULL);
88 TlsSetValue(_X_TlsIndex, me);
94 static xthread_t _Xthread_self(void)
96 return xthread_self();
99 static LockInfoRec global_lock;
100 static LockInfoRec i18n_lock;
102 static void _XLockMutex(
104 XTHREADS_FILE_LINE_ARGS
107 xmutex_lock(lip->lock);
110 static void _XUnlockMutex(
112 XTHREADS_FILE_LINE_ARGS
115 xmutex_unlock(lip->lock);
118 static void _XCreateMutex(
121 lip->lock = xmutex_malloc();
123 xmutex_init(lip->lock);
124 xmutex_set_name(lip->lock, "Xlib");
128 static void _XFreeMutex(
131 xmutex_clear(lip->lock);
132 xmutex_free(lip->lock);
136 static char *locking_file;
137 static int locking_line;
138 static xthread_t locking_thread;
139 static Bool xlibint_unlock = False; /* XlibInt.c may Unlock and re-Lock */
141 /* history that is useful to examine in a debugger */
142 #define LOCK_HIST_SIZE 21
145 Bool lockp; /* True for lock, False for unlock */
149 } locking_history[LOCK_HIST_SIZE];
151 int lock_hist_loc = 0; /* next slot to fill */
153 static void _XLockDisplayWarn(
155 char *file, /* source file, from macro */
159 xthread_t old_locker;
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
169 printf("%s line %d: thread %x waiting on lock held by %s line %d thread %x\n",
171 locking_file, locking_line, old_locker);
172 #endif /* XTHREADS_DEBUG */
175 xmutex_lock(dpy->lock->mutex);
177 if (strcmp(file, "XlibInt.c") == 0) {
179 printf("Xlib ERROR: XlibInt.c line %d thread %x locking display it did not unlock\n",
181 xlibint_unlock = False;
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 */
190 locking_thread = self;
191 if (strcmp(file, "XlibInt.c") != 0) {
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;
200 if (lock_hist_loc >= LOCK_HIST_SIZE)
203 #endif /* XTHREADS_WARN */
205 static void _XUnlockDisplay(
207 XTHREADS_FILE_LINE_ARGS
211 xthread_t self = xthread_self();
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 */
218 if (!xthread_have_id(locking_thread))
219 printf("Xlib ERROR: %s line %d thread %x: unlocking display that is not locked\n",
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);
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;
236 if (lock_hist_loc >= LOCK_HIST_SIZE)
238 #endif /* XTHREADS_WARN */
239 xmutex_unlock(dpy->lock->mutex);
243 static struct _XCVList *_XCreateCVL(
246 struct _XCVList *cvl;
248 if ((cvl = dpy->lock->free_cvls) != NULL) {
249 dpy->lock->free_cvls = cvl->next;
250 dpy->lock->num_free_cvls--;
252 cvl = (struct _XCVList *)Xmalloc(sizeof(struct _XCVList));
255 cvl->cv = xcondition_malloc();
260 xcondition_init(cvl->cv);
261 xcondition_set_name(cvl->cv, "Xlib read queue");
267 /* Put ourselves on the queue to read the connection.
268 Allocates and returns a queue element. */
270 static struct _XCVList *
273 struct _XCVList ***tail)
275 struct _XCVList *cvl;
277 cvl = _XCreateCVL(dpy);
278 #ifdef XTHREADS_DEBUG
279 printf("_XPushReader called in thread %x, pushing %x\n",
280 xthread_self(), cvl);
287 /* signal the next thread waiting to read the connection */
289 static void _XPopReader(
291 struct _XCVList **list,
292 struct _XCVList ***tail)
294 register struct _XCVList *front = *list;
296 #ifdef XTHREADS_DEBUG
297 printf("_XPopReader called in thread %x, popping %x\n",
298 xthread_self(), front);
301 if (dpy->flags & XlibDisplayProcConni)
302 /* we never added ourself in the first place */
305 if (front) { /* check "front" for paranoia */
307 if (*tail == &front->next) /* did we free the last elt? */
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++;
314 xcondition_clear(front->cv);
315 Xfree((char *)front->cv);
316 Xfree((char *)front);
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);
328 static void _XConditionWait(
331 XTHREADS_FILE_LINE_ARGS
335 xthread_t self = xthread_self();
336 char *old_file = locking_file;
337 int old_line = locking_line;
339 #ifdef XTHREADS_DEBUG
340 printf("line %d thread %x in condition wait\n", line, self);
342 xthread_clear_id(locking_thread);
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;
349 if (lock_hist_loc >= LOCK_HIST_SIZE)
351 #endif /* XTHREADS_WARN */
353 xcondition_wait(cv, mutex);
356 locking_thread = self;
357 locking_file = old_file;
358 locking_line = old_line;
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;
365 if (lock_hist_loc >= LOCK_HIST_SIZE)
367 #ifdef XTHREADS_DEBUG
368 printf("line %d thread %x was signaled\n", line, self);
369 #endif /* XTHREADS_DEBUG */
370 #endif /* XTHREADS_WARN */
373 static void _XConditionSignal(
375 XTHREADS_FILE_LINE_ARGS
379 #ifdef XTHREADS_DEBUG
380 printf("line %d thread %x is signalling\n", line, xthread_self());
383 xcondition_signal(cv);
387 static void _XConditionBroadcast(
389 XTHREADS_FILE_LINE_ARGS
393 #ifdef XTHREADS_DEBUG
394 printf("line %d thread %x is broadcasting\n", line, xthread_self());
397 xcondition_broadcast(cv);
401 static void _XFreeDisplayLock(
404 struct _XCVList *cvl;
406 if (dpy->lock != NULL) {
407 if (dpy->lock->mutex != NULL) {
408 xmutex_clear(dpy->lock->mutex);
409 xmutex_free(dpy->lock->mutex);
411 if (dpy->lock->cv != NULL) {
412 xcondition_clear(dpy->lock->cv);
413 xcondition_free(dpy->lock->cv);
415 if (dpy->lock->writers != NULL) {
416 xcondition_clear(dpy->lock->writers);
417 xcondition_free(dpy->lock->writers);
419 while ((cvl = dpy->lock->free_cvls)) {
420 dpy->lock->free_cvls = cvl->next;
421 xcondition_clear(cvl->cv);
422 Xfree((char *)cvl->cv);
425 Xfree((char *)dpy->lock);
428 if (dpy->lock_fns != NULL) {
429 Xfree((char *)dpy->lock_fns);
430 dpy->lock_fns = NULL;
435 * wait for thread with user-level display lock to release it.
438 static void _XDisplayLockWait(
443 while (dpy->lock->locking_level > 0) {
444 self = xthread_self();
445 if (xthread_equal(dpy->lock->locking_thread, self))
447 ConditionWait(dpy, dpy->lock->cv);
451 static void _XLockDisplay(
453 XTHREADS_FILE_LINE_ARGS
457 _XLockDisplayWarn(dpy, file, line);
459 xmutex_lock(dpy->lock->mutex);
461 if (dpy->lock->locking_level > 0)
462 _XDisplayLockWait(dpy);
464 _XSeqSyncFunction(dpy);
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.
471 static void _XInternalLockDisplay(
474 XTHREADS_FILE_LINE_ARGS
478 _XLockDisplayWarn(dpy, file, line);
480 xmutex_lock(dpy->lock->mutex);
482 if (!wskip && dpy->lock->locking_level > 0)
483 _XDisplayLockWait(dpy);
486 static void _XUserLockDisplay(
487 register Display* dpy)
489 _XDisplayLockWait(dpy);
491 if (++dpy->lock->locking_level == 1) {
492 dpy->lock->lock_wait = _XDisplayLockWait;
493 dpy->lock->locking_thread = xthread_self();
498 void _XUserUnlockDisplay(
499 register Display* dpy)
501 if (dpy->lock->locking_level > 0 && --dpy->lock->locking_level == 0) {
502 /* signal other threads that might be waiting in XLockDisplay */
503 ConditionBroadcast(dpy, dpy->lock->cv);
504 dpy->lock->lock_wait = NULL;
505 xthread_clear_id(dpy->lock->locking_thread);
509 /* returns 0 if initialized ok, -1 if unable to allocate
510 a mutex or other memory */
512 static int _XInitDisplayLock(
515 dpy->lock_fns = (struct _XLockPtrs*)Xmalloc(sizeof(struct _XLockPtrs));
516 if (dpy->lock_fns == NULL)
518 dpy->lock = (struct _XLockInfo *)Xmalloc(sizeof(struct _XLockInfo));
519 if (dpy->lock == NULL) {
520 _XFreeDisplayLock(dpy);
523 dpy->lock->cv = xcondition_malloc();
524 dpy->lock->mutex = xmutex_malloc();
525 dpy->lock->writers = xcondition_malloc();
526 if (!dpy->lock->cv || !dpy->lock->mutex || !dpy->lock->writers) {
527 _XFreeDisplayLock(dpy);
531 dpy->lock->reply_bytes_left = 0;
532 dpy->lock->reply_was_read = False;
533 dpy->lock->reply_awaiters = NULL;
534 dpy->lock->reply_awaiters_tail = &dpy->lock->reply_awaiters;
535 dpy->lock->event_awaiters = NULL;
536 dpy->lock->event_awaiters_tail = &dpy->lock->event_awaiters;
537 dpy->lock->reply_first = False;
538 dpy->lock->locking_level = 0;
539 dpy->lock->num_free_cvls = 0;
540 dpy->lock->free_cvls = NULL;
541 xthread_clear_id(dpy->lock->locking_thread);
542 xthread_clear_id(dpy->lock->reading_thread);
543 xthread_clear_id(dpy->lock->conni_thread);
544 xmutex_init(dpy->lock->mutex);
545 xmutex_set_name(dpy->lock->mutex, "Xlib Display");
546 xcondition_init(dpy->lock->cv);
547 xcondition_set_name(dpy->lock->cv, "XLockDisplay");
548 xcondition_init(dpy->lock->writers);
549 xcondition_set_name(dpy->lock->writers, "Xlib wait for writable");
550 dpy->lock_fns->lock_display = _XLockDisplay;
551 dpy->lock->internal_lock_display = _XInternalLockDisplay;
552 dpy->lock_fns->unlock_display = _XUnlockDisplay;
553 dpy->lock->user_lock_display = _XUserLockDisplay;
554 dpy->lock->user_unlock_display = _XUserUnlockDisplay;
555 dpy->lock->pop_reader = _XPopReader;
556 dpy->lock->push_reader = _XPushReader;
557 dpy->lock->condition_wait = _XConditionWait;
558 dpy->lock->condition_signal = _XConditionSignal;
559 dpy->lock->condition_broadcast = _XConditionBroadcast;
560 dpy->lock->create_cvl = _XCreateCVL;
561 dpy->lock->lock_wait = NULL; /* filled in by XLockDisplay() */
567 xthread_t __x11_thr_self() { return 0; }
568 xthread_t (*_x11_thr_self)() = __x11_thr_self;
572 Status XInitThreads(void)
578 void *dl_handle = dlopen(NULL, RTLD_LAZY);
580 ((_x11_thr_self = (xthread_t(*)())dlsym(dl_handle,"thr_self")) == 0)) {
581 _x11_thr_self = __x11_thr_self;
582 (void) fprintf (stderr,
583 "XInitThreads called, but no libthread in the calling program!\n" );
586 #endif /* __UNIXWARE__ */
588 xthread_init(); /* return value? */
590 if (!(global_lock.lock = xmutex_malloc()))
592 if (!(i18n_lock.lock = xmutex_malloc())) {
593 xmutex_free(global_lock.lock);
594 global_lock.lock = NULL;
597 _Xglobal_lock = &global_lock;
598 xmutex_init(_Xglobal_lock->lock);
599 xmutex_set_name(_Xglobal_lock->lock, "Xlib global");
600 _Xi18n_lock = &i18n_lock;
601 xmutex_init(_Xi18n_lock->lock);
602 xmutex_set_name(_Xi18n_lock->lock, "Xlib i18n");
603 _XLockMutex_fn = _XLockMutex;
604 _XUnlockMutex_fn = _XUnlockMutex;
605 _XCreateMutex_fn = _XCreateMutex;
606 _XFreeMutex_fn = _XFreeMutex;
607 _XInitDisplayLock_fn = _XInitDisplayLock;
608 _XFreeDisplayLock_fn = _XFreeDisplayLock;
609 _Xthread_self_fn = _Xthread_self;
612 #ifdef XTHREADS_DEBUG
613 setlinebuf(stdout); /* for debugging messages */
621 Status XInitThreads(void)
625 #endif /* XTHREADS */