locking.c revision 1ccffcbd
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/* in lcConv.c */
70extern LockInfoPtr _conv_lock;
71
72#ifdef WIN32
73static DWORD _X_TlsIndex = (DWORD)-1;
74
75void _Xthread_init(void)
76{
77    if (_X_TlsIndex == (DWORD)-1)
78	_X_TlsIndex = TlsAlloc();
79}
80
81struct _xthread_waiter *
82_Xthread_waiter(void)
83{
84    struct _xthread_waiter *me;
85
86    if (!(me = TlsGetValue(_X_TlsIndex))) {
87	me = xmalloc(sizeof(struct _xthread_waiter));
88	me->sem = CreateSemaphore(NULL, 0, 1, NULL);
89	me->next = NULL;
90	TlsSetValue(_X_TlsIndex, me);
91    }
92    return me;
93}
94#endif /* WIN32 */
95
96static xthread_t _Xthread_self(void)
97{
98    return xthread_self();
99}
100
101static LockInfoRec global_lock;
102static LockInfoRec i18n_lock;
103static LockInfoRec conv_lock;
104
105static void _XLockMutex(
106    LockInfoPtr lip
107    XTHREADS_FILE_LINE_ARGS
108    )
109{
110    xmutex_lock(lip->lock);
111}
112
113static void _XUnlockMutex(
114    LockInfoPtr lip
115    XTHREADS_FILE_LINE_ARGS
116    )
117{
118    xmutex_unlock(lip->lock);
119}
120
121static void _XCreateMutex(
122    LockInfoPtr lip)
123{
124    lip->lock = xmutex_malloc();
125    if (lip->lock) {
126	xmutex_init(lip->lock);
127	xmutex_set_name(lip->lock, "Xlib");
128    }
129}
130
131static void _XFreeMutex(
132    LockInfoPtr lip)
133{
134    xmutex_clear(lip->lock);
135    xmutex_free(lip->lock);
136    lip->lock = NULL;
137}
138
139#ifdef XTHREADS_WARN
140static char *locking_file;
141static int locking_line;
142static xthread_t locking_thread;
143static Bool xlibint_unlock = False; /* XlibInt.c may Unlock and re-Lock */
144
145/* history that is useful to examine in a debugger */
146#define LOCK_HIST_SIZE 21
147
148static struct {
149    Bool lockp;			/* True for lock, False for unlock */
150    xthread_t thread;
151    char *file;
152    int line;
153} locking_history[LOCK_HIST_SIZE];
154
155int lock_hist_loc = 0;		/* next slot to fill */
156
157static void _XLockDisplayWarn(
158    Display *dpy,
159    char *file,			/* source file, from macro */
160    int line)
161{
162    xthread_t self;
163    xthread_t old_locker;
164
165    self = xthread_self();
166    old_locker = locking_thread;
167    if (xthread_have_id(old_locker)) {
168	if (xthread_equal(old_locker, self))
169	    printf("Xlib ERROR: %s line %d thread %x: locking display already locked at %s line %d\n",
170		   file, line, self, locking_file, locking_line);
171#ifdef XTHREADS_DEBUG
172	else
173	    printf("%s line %d: thread %x waiting on lock held by %s line %d thread %x\n",
174		   file, line, self,
175		   locking_file, locking_line, old_locker);
176#endif /* XTHREADS_DEBUG */
177    }
178
179    xmutex_lock(dpy->lock->mutex);
180
181    if (strcmp(file, "XlibInt.c") == 0) {
182	if (!xlibint_unlock)
183	    printf("Xlib ERROR: XlibInt.c line %d thread %x locking display it did not unlock\n",
184		   line, self);
185	xlibint_unlock = False;
186    }
187
188#ifdef XTHREADS_DEBUG
189    /* if (old_locker  &&  old_locker != self) */
190    if (strcmp("XClearArea.c", file) && strcmp("XDrSegs.c", file)) /* ico */
191	printf("%s line %d: thread %x got display lock\n", file, line, self);
192#endif /* XTHREADS_DEBUG */
193
194    locking_thread = self;
195    if (strcmp(file, "XlibInt.c") != 0) {
196	locking_file = file;
197	locking_line = line;
198    }
199    locking_history[lock_hist_loc].file = file;
200    locking_history[lock_hist_loc].line = line;
201    locking_history[lock_hist_loc].thread = self;
202    locking_history[lock_hist_loc].lockp = True;
203    lock_hist_loc++;
204    if (lock_hist_loc >= LOCK_HIST_SIZE)
205	lock_hist_loc = 0;
206}
207#endif /* XTHREADS_WARN */
208
209static void _XUnlockDisplay(
210    Display *dpy
211    XTHREADS_FILE_LINE_ARGS
212    )
213{
214#ifdef XTHREADS_WARN
215    xthread_t self = xthread_self();
216
217#ifdef XTHREADS_DEBUG
218    if (strcmp("XClearArea.c", file) && strcmp("XDrSegs.c", file)) /* ico */
219	printf("%s line %d: thread %x unlocking display\n", file, line, self);
220#endif /* XTHREADS_DEBUG */
221
222    if (!xthread_have_id(locking_thread))
223	printf("Xlib ERROR: %s line %d thread %x: unlocking display that is not locked\n",
224	       file, line, self);
225    else if (strcmp(file, "XlibInt.c") == 0)
226	xlibint_unlock = True;
227#ifdef XTHREADS_DEBUG
228    else if (strcmp(file, locking_file) != 0)
229	/* not always an error because locking_file is not per-thread */
230	printf("%s line %d: unlocking display locked from %s line %d (probably okay)\n",
231	       file, line, locking_file, locking_line);
232#endif /* XTHREADS_DEBUG */
233    xthread_clear_id(locking_thread);
234
235    locking_history[lock_hist_loc].file = file;
236    locking_history[lock_hist_loc].line = line;
237    locking_history[lock_hist_loc].thread = self;
238    locking_history[lock_hist_loc].lockp = False;
239    lock_hist_loc++;
240    if (lock_hist_loc >= LOCK_HIST_SIZE)
241	lock_hist_loc = 0;
242#endif /* XTHREADS_WARN */
243    xmutex_unlock(dpy->lock->mutex);
244}
245
246
247static struct _XCVList *_XCreateCVL(
248    Display *dpy)
249{
250    struct _XCVList *cvl;
251
252    if ((cvl = dpy->lock->free_cvls) != NULL) {
253	dpy->lock->free_cvls = cvl->next;
254	dpy->lock->num_free_cvls--;
255    } else {
256	cvl = Xmalloc(sizeof(struct _XCVList));
257	if (!cvl)
258	    return NULL;
259	cvl->cv = xcondition_malloc();
260	if (!cvl->cv) {
261	    Xfree(cvl);
262	    return NULL;
263	}
264	xcondition_init(cvl->cv);
265	xcondition_set_name(cvl->cv, "Xlib read queue");
266    }
267    cvl->next = NULL;
268    return cvl;
269}
270
271/* Put ourselves on the queue to read the connection.
272   Allocates and returns a queue element. */
273
274static struct _XCVList *
275_XPushReader(
276    Display *dpy,
277    struct _XCVList ***tail)
278{
279    struct _XCVList *cvl;
280
281    cvl = _XCreateCVL(dpy);
282#ifdef XTHREADS_DEBUG
283    printf("_XPushReader called in thread %x, pushing %x\n",
284	   xthread_self(), cvl);
285#endif
286    **tail = cvl;
287    *tail = &cvl->next;
288    return cvl;
289}
290
291/* signal the next thread waiting to read the connection */
292
293static void _XPopReader(
294    Display *dpy,
295    struct _XCVList **list,
296    struct _XCVList ***tail)
297{
298    register struct _XCVList *front = *list;
299
300#ifdef XTHREADS_DEBUG
301    printf("_XPopReader called in thread %x, popping %x\n",
302	   xthread_self(), front);
303#endif
304
305    if (dpy->flags & XlibDisplayProcConni)
306	/* we never added ourself in the first place */
307	return;
308
309    if (front) {		/* check "front" for paranoia */
310	*list = front->next;
311	if (*tail == &front->next)	/* did we free the last elt? */
312	    *tail = list;
313	if (dpy->lock->num_free_cvls < NUM_FREE_CVLS) {
314	    front->next = dpy->lock->free_cvls;
315	    dpy->lock->free_cvls = front;
316	    dpy->lock->num_free_cvls++;
317	} else {
318	    xcondition_clear(front->cv);
319	    Xfree(front->cv);
320	    Xfree(front);
321	}
322    }
323
324    /* signal new front after it is in place */
325    if ((dpy->lock->reply_first = (dpy->lock->reply_awaiters != NULL))) {
326	ConditionSignal(dpy, dpy->lock->reply_awaiters->cv);
327    } else if (dpy->lock->event_awaiters) {
328	ConditionSignal(dpy, dpy->lock->event_awaiters->cv);
329    }
330}
331
332static void _XConditionWait(
333    xcondition_t cv,
334    xmutex_t mutex
335    XTHREADS_FILE_LINE_ARGS
336    )
337{
338#ifdef XTHREADS_WARN
339    xthread_t self = xthread_self();
340    char *old_file = locking_file;
341    int old_line = locking_line;
342
343#ifdef XTHREADS_DEBUG
344    printf("line %d thread %x in condition wait\n", line, self);
345#endif
346    xthread_clear_id(locking_thread);
347
348    locking_history[lock_hist_loc].file = file;
349    locking_history[lock_hist_loc].line = line;
350    locking_history[lock_hist_loc].thread = self;
351    locking_history[lock_hist_loc].lockp = False;
352    lock_hist_loc++;
353    if (lock_hist_loc >= LOCK_HIST_SIZE)
354	lock_hist_loc = 0;
355#endif /* XTHREADS_WARN */
356
357    xcondition_wait(cv, mutex);
358
359#ifdef XTHREADS_WARN
360    locking_thread = self;
361    locking_file = old_file;
362    locking_line = old_line;
363
364    locking_history[lock_hist_loc].file = file;
365    locking_history[lock_hist_loc].line = line;
366    locking_history[lock_hist_loc].thread = self;
367    locking_history[lock_hist_loc].lockp = True;
368    lock_hist_loc++;
369    if (lock_hist_loc >= LOCK_HIST_SIZE)
370	lock_hist_loc = 0;
371#ifdef XTHREADS_DEBUG
372    printf("line %d thread %x was signaled\n", line, self);
373#endif /* XTHREADS_DEBUG */
374#endif /* XTHREADS_WARN */
375}
376
377static void _XConditionSignal(
378    xcondition_t cv
379    XTHREADS_FILE_LINE_ARGS
380    )
381{
382#ifdef XTHREADS_WARN
383#ifdef XTHREADS_DEBUG
384    printf("line %d thread %x is signalling\n", line, xthread_self());
385#endif
386#endif
387    xcondition_signal(cv);
388}
389
390
391static void _XConditionBroadcast(
392    xcondition_t cv
393    XTHREADS_FILE_LINE_ARGS
394    )
395{
396#ifdef XTHREADS_WARN
397#ifdef XTHREADS_DEBUG
398    printf("line %d thread %x is broadcasting\n", line, xthread_self());
399#endif
400#endif
401    xcondition_broadcast(cv);
402}
403
404
405static void _XFreeDisplayLock(
406    Display *dpy)
407{
408    struct _XCVList *cvl;
409
410    if (dpy->lock != NULL) {
411	if (dpy->lock->mutex != NULL) {
412	    xmutex_clear(dpy->lock->mutex);
413	    xmutex_free(dpy->lock->mutex);
414	}
415	if (dpy->lock->cv != NULL) {
416	    xcondition_clear(dpy->lock->cv);
417	    xcondition_free(dpy->lock->cv);
418	}
419	if (dpy->lock->writers != NULL) {
420	    xcondition_clear(dpy->lock->writers);
421	    xcondition_free(dpy->lock->writers);
422	}
423	while ((cvl = dpy->lock->free_cvls)) {
424	    dpy->lock->free_cvls = cvl->next;
425	    xcondition_clear(cvl->cv);
426	    Xfree(cvl->cv);
427	    Xfree(cvl);
428	}
429	Xfree(dpy->lock);
430	dpy->lock = NULL;
431    }
432    if (dpy->lock_fns != NULL) {
433	Xfree(dpy->lock_fns);
434	dpy->lock_fns = NULL;
435    }
436}
437
438/*
439 * wait for thread with user-level display lock to release it.
440 */
441
442static void _XDisplayLockWait(
443    Display *dpy)
444{
445    xthread_t self;
446
447    while (dpy->lock->locking_level > 0) {
448	self = xthread_self();
449	if (xthread_equal(dpy->lock->locking_thread, self))
450	    break;
451	ConditionWait(dpy, dpy->lock->cv);
452    }
453}
454
455static void _XLockDisplay(
456    Display *dpy
457    XTHREADS_FILE_LINE_ARGS
458    );
459
460static void _XIfEventLockDisplay(
461    Display *dpy
462    XTHREADS_FILE_LINE_ARGS
463    )
464{
465    /* assert(dpy->in_ifevent); */
466}
467
468static void _XIfEventUnlockDisplay(
469    Display *dpy
470    XTHREADS_FILE_LINE_ARGS
471    )
472{
473    if (dpy->in_ifevent)
474        return;
475
476    dpy->lock_fns->lock_display = _XLockDisplay;
477    dpy->lock_fns->unlock_display = _XUnlockDisplay;
478    UnlockDisplay(dpy);
479}
480
481static void _XLockDisplay(
482    Display *dpy
483    XTHREADS_FILE_LINE_ARGS
484    )
485{
486#ifdef XTHREADS
487    struct _XErrorThreadInfo *ti;
488#endif
489#ifdef XTHREADS_WARN
490    _XLockDisplayWarn(dpy, file, line);
491#else
492    xmutex_lock(dpy->lock->mutex);
493#endif
494    if (dpy->lock->locking_level > 0)
495	_XDisplayLockWait(dpy);
496#ifdef XTHREADS
497    /*
498     * Skip the two function calls below which may generate requests
499     * when LockDisplay is called from within _XError.
500     */
501    for (ti = dpy->error_threads; ti; ti = ti->next)
502	    if (ti->error_thread == xthread_self())
503		    return;
504#endif
505    _XIDHandler(dpy);
506    _XSeqSyncFunction(dpy);
507    if (dpy->in_ifevent) {
508        dpy->lock_fns->lock_display = _XIfEventLockDisplay;
509        dpy->lock_fns->unlock_display = _XIfEventUnlockDisplay;
510    }
511}
512
513/*
514 * _XReply is allowed to exit from select/poll and clean up even if a
515 * user-level lock is in force, so it uses this instead of _XFancyLockDisplay.
516 */
517static void _XInternalLockDisplay(
518    Display *dpy,
519    Bool wskip
520    XTHREADS_FILE_LINE_ARGS
521    )
522{
523#ifdef XTHREADS_WARN
524    _XLockDisplayWarn(dpy, file, line);
525#else
526    xmutex_lock(dpy->lock->mutex);
527#endif
528    if (!wskip && dpy->lock->locking_level > 0)
529	_XDisplayLockWait(dpy);
530}
531
532static void _XUserLockDisplay(
533    register Display* dpy)
534{
535    _XDisplayLockWait(dpy);
536
537    if (++dpy->lock->locking_level == 1) {
538	dpy->lock->lock_wait = _XDisplayLockWait;
539	dpy->lock->locking_thread = xthread_self();
540    }
541}
542
543static
544void _XUserUnlockDisplay(
545    register Display* dpy)
546{
547    if (dpy->lock->locking_level > 0 && --dpy->lock->locking_level == 0) {
548	/* signal other threads that might be waiting in XLockDisplay */
549	ConditionBroadcast(dpy, dpy->lock->cv);
550	dpy->lock->lock_wait = NULL;
551	xthread_clear_id(dpy->lock->locking_thread);
552    }
553}
554
555/* returns 0 if initialized ok, -1 if unable to allocate
556   a mutex or other memory */
557
558static int _XInitDisplayLock(
559    Display *dpy)
560{
561    dpy->lock_fns = Xmalloc(sizeof(struct _XLockPtrs));
562    if (dpy->lock_fns == NULL)
563	return -1;
564    dpy->lock = Xmalloc(sizeof(struct _XLockInfo));
565    if (dpy->lock == NULL) {
566	_XFreeDisplayLock(dpy);
567	return -1;
568    }
569    dpy->lock->cv = xcondition_malloc();
570    dpy->lock->mutex = xmutex_malloc();
571    dpy->lock->writers = xcondition_malloc();
572    if (!dpy->lock->cv || !dpy->lock->mutex || !dpy->lock->writers) {
573	_XFreeDisplayLock(dpy);
574	return -1;
575    }
576
577    dpy->lock->reply_bytes_left = 0;
578    dpy->lock->reply_was_read = False;
579    dpy->lock->reply_awaiters = NULL;
580    dpy->lock->reply_awaiters_tail = &dpy->lock->reply_awaiters;
581    dpy->lock->event_awaiters = NULL;
582    dpy->lock->event_awaiters_tail = &dpy->lock->event_awaiters;
583    dpy->lock->reply_first = False;
584    dpy->lock->locking_level = 0;
585    dpy->lock->num_free_cvls = 0;
586    dpy->lock->free_cvls = NULL;
587    xthread_clear_id(dpy->lock->locking_thread);
588    xthread_clear_id(dpy->lock->reading_thread);
589    xthread_clear_id(dpy->lock->conni_thread);
590    xmutex_init(dpy->lock->mutex);
591    xmutex_set_name(dpy->lock->mutex, "Xlib Display");
592    xcondition_init(dpy->lock->cv);
593    xcondition_set_name(dpy->lock->cv, "XLockDisplay");
594    xcondition_init(dpy->lock->writers);
595    xcondition_set_name(dpy->lock->writers, "Xlib wait for writable");
596    dpy->lock_fns->lock_display = _XLockDisplay;
597    dpy->lock->internal_lock_display = _XInternalLockDisplay;
598    dpy->lock_fns->unlock_display = _XUnlockDisplay;
599    dpy->lock->user_lock_display = _XUserLockDisplay;
600    dpy->lock->user_unlock_display = _XUserUnlockDisplay;
601    dpy->lock->pop_reader = _XPopReader;
602    dpy->lock->push_reader = _XPushReader;
603    dpy->lock->condition_wait = _XConditionWait;
604    dpy->lock->condition_signal = _XConditionSignal;
605    dpy->lock->condition_broadcast = _XConditionBroadcast;
606    dpy->lock->create_cvl = _XCreateCVL;
607    dpy->lock->lock_wait = NULL; /* filled in by XLockDisplay() */
608
609    return 0;
610}
611
612#ifdef __UNIXWARE__
613xthread_t __x11_thr_self() { return 0; }
614xthread_t (*_x11_thr_self)() = __x11_thr_self;
615#endif
616
617
618Status XInitThreads(void)
619{
620    if (_Xglobal_lock)
621	return 1;
622#ifdef __UNIXWARE__
623    else {
624       void *dl_handle = dlopen(NULL, RTLD_LAZY);
625       if (!dl_handle ||
626         ((_x11_thr_self = (xthread_t(*)())dlsym(dl_handle,"thr_self")) == 0)) {
627	       _x11_thr_self = __x11_thr_self;
628	       (void) fprintf (stderr,
629	"XInitThreads called, but no libthread in the calling program!\n" );
630       }
631    }
632#endif /* __UNIXWARE__ */
633#ifdef xthread_init
634    xthread_init();		/* return value? */
635#endif
636    if (!(global_lock.lock = xmutex_malloc()))
637	return 0;
638    if (!(i18n_lock.lock = xmutex_malloc())) {
639	xmutex_free(global_lock.lock);
640	global_lock.lock = NULL;
641	return 0;
642    }
643    if (!(conv_lock.lock = xmutex_malloc())) {
644	xmutex_free(global_lock.lock);
645	global_lock.lock = NULL;
646	xmutex_free(i18n_lock.lock);
647	i18n_lock.lock = NULL;
648	return 0;
649    }
650    _Xglobal_lock = &global_lock;
651    xmutex_init(_Xglobal_lock->lock);
652    xmutex_set_name(_Xglobal_lock->lock, "Xlib global");
653    _Xi18n_lock = &i18n_lock;
654    xmutex_init(_Xi18n_lock->lock);
655    xmutex_set_name(_Xi18n_lock->lock, "Xlib i18n");
656    _conv_lock = &conv_lock;
657    xmutex_init(_conv_lock->lock);
658    xmutex_set_name(_conv_lock->lock, "Xlib conv");
659    _XLockMutex_fn = _XLockMutex;
660    _XUnlockMutex_fn = _XUnlockMutex;
661    _XCreateMutex_fn = _XCreateMutex;
662    _XFreeMutex_fn = _XFreeMutex;
663    _XInitDisplayLock_fn = _XInitDisplayLock;
664    _XFreeDisplayLock_fn = _XFreeDisplayLock;
665    _Xthread_self_fn = _Xthread_self;
666
667#ifdef XTHREADS_WARN
668#ifdef XTHREADS_DEBUG
669    setlinebuf(stdout);		/* for debugging messages */
670#endif
671#endif
672
673    return 1;
674}
675
676#else /* XTHREADS */
677Status XInitThreads(void)
678{
679    return 0;
680}
681#endif /* XTHREADS */
682