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