/* Copyright 1992, 1998 The Open Group Permission to use, copy, modify, distribute, and sell this software and its documentation for any purpose is hereby granted without fee, provided that the above copyright notice appear in all copies and that both that copyright notice and this permission notice appear in supporting documentation. The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE OPEN GROUP BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. Except as contained in this notice, the name of The Open Group shall not be used in advertising or otherwise to promote the sale, use or other dealings in this Software without prior written authorization from The Open Group. */ /* * Author: Stephen Gildea, MIT X Consortium * * locking.c - multi-thread locking routines implemented in C Threads */ #ifdef HAVE_CONFIG_H #include <config.h> #endif #include "Xlibint.h" #undef _XLockMutex #undef _XUnlockMutex #undef _XCreateMutex #undef _XFreeMutex #ifdef XTHREADS #ifdef __UNIXWARE__ #include <dlfcn.h> #endif #include "Xprivate.h" #include "locking.h" #ifdef XTHREADS_WARN #include <stdio.h> /* for warn/debug stuff */ #endif /* Additional arguments for source code location lock call was made from */ #if defined(XTHREADS_WARN) || defined(XTHREADS_FILE_LINE) # define XTHREADS_FILE_LINE_ARGS \ , \ char* file, /* source file, from macro */ \ int line #else # define XTHREADS_FILE_LINE_ARGS /* None */ #endif #define NUM_FREE_CVLS 4 /* in lcWrap.c */ extern LockInfoPtr _Xi18n_lock; #ifdef WIN32 static DWORD _X_TlsIndex = (DWORD)-1; void _Xthread_init(void) { if (_X_TlsIndex == (DWORD)-1) _X_TlsIndex = TlsAlloc(); } struct _xthread_waiter * _Xthread_waiter(void) { struct _xthread_waiter *me; if (!(me = TlsGetValue(_X_TlsIndex))) { me = (struct _xthread_waiter *)xmalloc(sizeof(struct _xthread_waiter)); me->sem = CreateSemaphore(NULL, 0, 1, NULL); me->next = NULL; TlsSetValue(_X_TlsIndex, me); } return me; } #endif /* WIN32 */ static xthread_t _Xthread_self(void) { return xthread_self(); } static LockInfoRec global_lock; static LockInfoRec i18n_lock; static void _XLockMutex( LockInfoPtr lip XTHREADS_FILE_LINE_ARGS ) { xmutex_lock(lip->lock); } static void _XUnlockMutex( LockInfoPtr lip XTHREADS_FILE_LINE_ARGS ) { xmutex_unlock(lip->lock); } static void _XCreateMutex( LockInfoPtr lip) { lip->lock = xmutex_malloc(); if (lip->lock) { xmutex_init(lip->lock); xmutex_set_name(lip->lock, "Xlib"); } } static void _XFreeMutex( LockInfoPtr lip) { xmutex_clear(lip->lock); xmutex_free(lip->lock); } #ifdef XTHREADS_WARN static char *locking_file; static int locking_line; static xthread_t locking_thread; static Bool xlibint_unlock = False; /* XlibInt.c may Unlock and re-Lock */ /* history that is useful to examine in a debugger */ #define LOCK_HIST_SIZE 21 static struct { Bool lockp; /* True for lock, False for unlock */ xthread_t thread; char *file; int line; } locking_history[LOCK_HIST_SIZE]; int lock_hist_loc = 0; /* next slot to fill */ static void _XLockDisplayWarn( Display *dpy, char *file, /* source file, from macro */ int line) { xthread_t self; xthread_t old_locker; self = xthread_self(); old_locker = locking_thread; if (xthread_have_id(old_locker)) { if (xthread_equal(old_locker, self)) printf("Xlib ERROR: %s line %d thread %x: locking display already locked at %s line %d\n", file, line, self, locking_file, locking_line); #ifdef XTHREADS_DEBUG else printf("%s line %d: thread %x waiting on lock held by %s line %d thread %x\n", file, line, self, locking_file, locking_line, old_locker); #endif /* XTHREADS_DEBUG */ } xmutex_lock(dpy->lock->mutex); if (strcmp(file, "XlibInt.c") == 0) { if (!xlibint_unlock) printf("Xlib ERROR: XlibInt.c line %d thread %x locking display it did not unlock\n", line, self); xlibint_unlock = False; } #ifdef XTHREADS_DEBUG /* if (old_locker && old_locker != self) */ if (strcmp("XClearArea.c", file) && strcmp("XDrSegs.c", file)) /* ico */ printf("%s line %d: thread %x got display lock\n", file, line, self); #endif /* XTHREADS_DEBUG */ locking_thread = self; if (strcmp(file, "XlibInt.c") != 0) { locking_file = file; locking_line = line; } locking_history[lock_hist_loc].file = file; locking_history[lock_hist_loc].line = line; locking_history[lock_hist_loc].thread = self; locking_history[lock_hist_loc].lockp = True; lock_hist_loc++; if (lock_hist_loc >= LOCK_HIST_SIZE) lock_hist_loc = 0; } #endif /* XTHREADS_WARN */ static void _XUnlockDisplay( Display *dpy XTHREADS_FILE_LINE_ARGS ) { #ifdef XTHREADS_WARN xthread_t self = xthread_self(); #ifdef XTHREADS_DEBUG if (strcmp("XClearArea.c", file) && strcmp("XDrSegs.c", file)) /* ico */ printf("%s line %d: thread %x unlocking display\n", file, line, self); #endif /* XTHREADS_DEBUG */ if (!xthread_have_id(locking_thread)) printf("Xlib ERROR: %s line %d thread %x: unlocking display that is not locked\n", file, line, self); else if (strcmp(file, "XlibInt.c") == 0) xlibint_unlock = True; #ifdef XTHREADS_DEBUG else if (strcmp(file, locking_file) != 0) /* not always an error because locking_file is not per-thread */ printf("%s line %d: unlocking display locked from %s line %d (probably okay)\n", file, line, locking_file, locking_line); #endif /* XTHREADS_DEBUG */ xthread_clear_id(locking_thread); locking_history[lock_hist_loc].file = file; locking_history[lock_hist_loc].line = line; locking_history[lock_hist_loc].thread = self; locking_history[lock_hist_loc].lockp = False; lock_hist_loc++; if (lock_hist_loc >= LOCK_HIST_SIZE) lock_hist_loc = 0; #endif /* XTHREADS_WARN */ xmutex_unlock(dpy->lock->mutex); } static struct _XCVList *_XCreateCVL( Display *dpy) { struct _XCVList *cvl; if ((cvl = dpy->lock->free_cvls) != NULL) { dpy->lock->free_cvls = cvl->next; dpy->lock->num_free_cvls--; } else { cvl = (struct _XCVList *)Xmalloc(sizeof(struct _XCVList)); if (!cvl) return NULL; cvl->cv = xcondition_malloc(); if (!cvl->cv) { Xfree(cvl); return NULL; } xcondition_init(cvl->cv); xcondition_set_name(cvl->cv, "Xlib read queue"); } cvl->next = NULL; return cvl; } /* Put ourselves on the queue to read the connection. Allocates and returns a queue element. */ static struct _XCVList * _XPushReader( Display *dpy, struct _XCVList ***tail) { struct _XCVList *cvl; cvl = _XCreateCVL(dpy); #ifdef XTHREADS_DEBUG printf("_XPushReader called in thread %x, pushing %x\n", xthread_self(), cvl); #endif **tail = cvl; *tail = &cvl->next; return cvl; } /* signal the next thread waiting to read the connection */ static void _XPopReader( Display *dpy, struct _XCVList **list, struct _XCVList ***tail) { register struct _XCVList *front = *list; #ifdef XTHREADS_DEBUG printf("_XPopReader called in thread %x, popping %x\n", xthread_self(), front); #endif if (dpy->flags & XlibDisplayProcConni) /* we never added ourself in the first place */ return; if (front) { /* check "front" for paranoia */ *list = front->next; if (*tail == &front->next) /* did we free the last elt? */ *tail = list; if (dpy->lock->num_free_cvls < NUM_FREE_CVLS) { front->next = dpy->lock->free_cvls; dpy->lock->free_cvls = front; dpy->lock->num_free_cvls++; } else { xcondition_clear(front->cv); Xfree((char *)front->cv); Xfree((char *)front); } } /* signal new front after it is in place */ if ((dpy->lock->reply_first = (dpy->lock->reply_awaiters != NULL))) { ConditionSignal(dpy, dpy->lock->reply_awaiters->cv); } else if (dpy->lock->event_awaiters) { ConditionSignal(dpy, dpy->lock->event_awaiters->cv); } } static void _XConditionWait( xcondition_t cv, xmutex_t mutex XTHREADS_FILE_LINE_ARGS ) { #ifdef XTHREADS_WARN xthread_t self = xthread_self(); char *old_file = locking_file; int old_line = locking_line; #ifdef XTHREADS_DEBUG printf("line %d thread %x in condition wait\n", line, self); #endif xthread_clear_id(locking_thread); locking_history[lock_hist_loc].file = file; locking_history[lock_hist_loc].line = line; locking_history[lock_hist_loc].thread = self; locking_history[lock_hist_loc].lockp = False; lock_hist_loc++; if (lock_hist_loc >= LOCK_HIST_SIZE) lock_hist_loc = 0; #endif /* XTHREADS_WARN */ xcondition_wait(cv, mutex); #ifdef XTHREADS_WARN locking_thread = self; locking_file = old_file; locking_line = old_line; locking_history[lock_hist_loc].file = file; locking_history[lock_hist_loc].line = line; locking_history[lock_hist_loc].thread = self; locking_history[lock_hist_loc].lockp = True; lock_hist_loc++; if (lock_hist_loc >= LOCK_HIST_SIZE) lock_hist_loc = 0; #ifdef XTHREADS_DEBUG printf("line %d thread %x was signaled\n", line, self); #endif /* XTHREADS_DEBUG */ #endif /* XTHREADS_WARN */ } static void _XConditionSignal( xcondition_t cv XTHREADS_FILE_LINE_ARGS ) { #ifdef XTHREADS_WARN #ifdef XTHREADS_DEBUG printf("line %d thread %x is signalling\n", line, xthread_self()); #endif #endif xcondition_signal(cv); } static void _XConditionBroadcast( xcondition_t cv XTHREADS_FILE_LINE_ARGS ) { #ifdef XTHREADS_WARN #ifdef XTHREADS_DEBUG printf("line %d thread %x is broadcasting\n", line, xthread_self()); #endif #endif xcondition_broadcast(cv); } static void _XFreeDisplayLock( Display *dpy) { struct _XCVList *cvl; if (dpy->lock != NULL) { if (dpy->lock->mutex != NULL) { xmutex_clear(dpy->lock->mutex); xmutex_free(dpy->lock->mutex); } if (dpy->lock->cv != NULL) { xcondition_clear(dpy->lock->cv); xcondition_free(dpy->lock->cv); } if (dpy->lock->writers != NULL) { xcondition_clear(dpy->lock->writers); xcondition_free(dpy->lock->writers); } while ((cvl = dpy->lock->free_cvls)) { dpy->lock->free_cvls = cvl->next; xcondition_clear(cvl->cv); Xfree((char *)cvl->cv); Xfree((char *)cvl); } Xfree((char *)dpy->lock); dpy->lock = NULL; } if (dpy->lock_fns != NULL) { Xfree((char *)dpy->lock_fns); dpy->lock_fns = NULL; } } /* * wait for thread with user-level display lock to release it. */ static void _XDisplayLockWait( Display *dpy) { xthread_t self; while (dpy->lock->locking_level > 0) { self = xthread_self(); if (xthread_equal(dpy->lock->locking_thread, self)) break; ConditionWait(dpy, dpy->lock->cv); } } static void _XLockDisplay( Display *dpy XTHREADS_FILE_LINE_ARGS ) { #ifdef XTHREADS_WARN _XLockDisplayWarn(dpy, file, line); #else xmutex_lock(dpy->lock->mutex); #endif if (dpy->lock->locking_level > 0) _XDisplayLockWait(dpy); _XIDHandler(dpy); _XSeqSyncFunction(dpy); } /* * _XReply is allowed to exit from select/poll and clean up even if a * user-level lock is in force, so it uses this instead of _XFancyLockDisplay. */ static void _XInternalLockDisplay( Display *dpy, Bool wskip XTHREADS_FILE_LINE_ARGS ) { #ifdef XTHREADS_WARN _XLockDisplayWarn(dpy, file, line); #else xmutex_lock(dpy->lock->mutex); #endif if (!wskip && dpy->lock->locking_level > 0) _XDisplayLockWait(dpy); } static void _XUserLockDisplay( register Display* dpy) { _XDisplayLockWait(dpy); if (++dpy->lock->locking_level == 1) { dpy->lock->lock_wait = _XDisplayLockWait; dpy->lock->locking_thread = xthread_self(); } } static void _XUserUnlockDisplay( register Display* dpy) { if (dpy->lock->locking_level > 0 && --dpy->lock->locking_level == 0) { /* signal other threads that might be waiting in XLockDisplay */ ConditionBroadcast(dpy, dpy->lock->cv); dpy->lock->lock_wait = NULL; xthread_clear_id(dpy->lock->locking_thread); } } /* returns 0 if initialized ok, -1 if unable to allocate a mutex or other memory */ static int _XInitDisplayLock( Display *dpy) { dpy->lock_fns = (struct _XLockPtrs*)Xmalloc(sizeof(struct _XLockPtrs)); if (dpy->lock_fns == NULL) return -1; dpy->lock = (struct _XLockInfo *)Xmalloc(sizeof(struct _XLockInfo)); if (dpy->lock == NULL) { _XFreeDisplayLock(dpy); return -1; } dpy->lock->cv = xcondition_malloc(); dpy->lock->mutex = xmutex_malloc(); dpy->lock->writers = xcondition_malloc(); if (!dpy->lock->cv || !dpy->lock->mutex || !dpy->lock->writers) { _XFreeDisplayLock(dpy); return -1; } dpy->lock->reply_bytes_left = 0; dpy->lock->reply_was_read = False; dpy->lock->reply_awaiters = NULL; dpy->lock->reply_awaiters_tail = &dpy->lock->reply_awaiters; dpy->lock->event_awaiters = NULL; dpy->lock->event_awaiters_tail = &dpy->lock->event_awaiters; dpy->lock->reply_first = False; dpy->lock->locking_level = 0; dpy->lock->num_free_cvls = 0; dpy->lock->free_cvls = NULL; xthread_clear_id(dpy->lock->locking_thread); xthread_clear_id(dpy->lock->reading_thread); xthread_clear_id(dpy->lock->conni_thread); xmutex_init(dpy->lock->mutex); xmutex_set_name(dpy->lock->mutex, "Xlib Display"); xcondition_init(dpy->lock->cv); xcondition_set_name(dpy->lock->cv, "XLockDisplay"); xcondition_init(dpy->lock->writers); xcondition_set_name(dpy->lock->writers, "Xlib wait for writable"); dpy->lock_fns->lock_display = _XLockDisplay; dpy->lock->internal_lock_display = _XInternalLockDisplay; dpy->lock_fns->unlock_display = _XUnlockDisplay; dpy->lock->user_lock_display = _XUserLockDisplay; dpy->lock->user_unlock_display = _XUserUnlockDisplay; dpy->lock->pop_reader = _XPopReader; dpy->lock->push_reader = _XPushReader; dpy->lock->condition_wait = _XConditionWait; dpy->lock->condition_signal = _XConditionSignal; dpy->lock->condition_broadcast = _XConditionBroadcast; dpy->lock->create_cvl = _XCreateCVL; dpy->lock->lock_wait = NULL; /* filled in by XLockDisplay() */ return 0; } #ifdef __UNIXWARE__ xthread_t __x11_thr_self() { return 0; } xthread_t (*_x11_thr_self)() = __x11_thr_self; #endif Status XInitThreads(void) { if (_Xglobal_lock) return 1; #ifdef __UNIXWARE__ else { void *dl_handle = dlopen(NULL, RTLD_LAZY); if (!dl_handle || ((_x11_thr_self = (xthread_t(*)())dlsym(dl_handle,"thr_self")) == 0)) { _x11_thr_self = __x11_thr_self; (void) fprintf (stderr, "XInitThreads called, but no libthread in the calling program!\n" ); } } #endif /* __UNIXWARE__ */ #ifdef xthread_init xthread_init(); /* return value? */ #endif if (!(global_lock.lock = xmutex_malloc())) return 0; if (!(i18n_lock.lock = xmutex_malloc())) { xmutex_free(global_lock.lock); global_lock.lock = NULL; return 0; } _Xglobal_lock = &global_lock; xmutex_init(_Xglobal_lock->lock); xmutex_set_name(_Xglobal_lock->lock, "Xlib global"); _Xi18n_lock = &i18n_lock; xmutex_init(_Xi18n_lock->lock); xmutex_set_name(_Xi18n_lock->lock, "Xlib i18n"); _XLockMutex_fn = _XLockMutex; _XUnlockMutex_fn = _XUnlockMutex; _XCreateMutex_fn = _XCreateMutex; _XFreeMutex_fn = _XFreeMutex; _XInitDisplayLock_fn = _XInitDisplayLock; _XFreeDisplayLock_fn = _XFreeDisplayLock; _Xthread_self_fn = _Xthread_self; #ifdef XTHREADS_WARN #ifdef XTHREADS_DEBUG setlinebuf(stdout); /* for debugging messages */ #endif #endif return 1; } #else /* XTHREADS */ Status XInitThreads(void) { return 0; } #endif /* XTHREADS */