locking.c revision 0efe039a
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 _XInternalLockDisplay(
469    Display *dpy,
470    Bool wskip
471    XTHREADS_FILE_LINE_ARGS
472    );
473
474static void _XIfEventInternalLockDisplay(
475    Display *dpy,
476    Bool wskip
477    XTHREADS_FILE_LINE_ARGS
478    )
479{
480    /* assert(dpy->in_ifevent); */
481}
482
483static void _XIfEventUnlockDisplay(
484    Display *dpy
485    XTHREADS_FILE_LINE_ARGS
486    )
487{
488    if (dpy->in_ifevent == 0) {
489	dpy->lock_fns->lock_display = _XLockDisplay;
490	dpy->lock_fns->unlock_display = _XUnlockDisplay;
491	dpy->lock->internal_lock_display = _XInternalLockDisplay;
492	UnlockDisplay(dpy);
493    } else
494	return;
495}
496
497static void _XLockDisplay(
498    Display *dpy
499    XTHREADS_FILE_LINE_ARGS
500    )
501{
502#ifdef XTHREADS
503    struct _XErrorThreadInfo *ti;
504#endif
505#ifdef XTHREADS_WARN
506    _XLockDisplayWarn(dpy, file, line);
507#else
508    xmutex_lock(dpy->lock->mutex);
509#endif
510    if (dpy->lock->locking_level > 0)
511	_XDisplayLockWait(dpy);
512#ifdef XTHREADS
513    /*
514     * Skip the two function calls below which may generate requests
515     * when LockDisplay is called from within _XError.
516     */
517    for (ti = dpy->error_threads; ti; ti = ti->next)
518	    if (ti->error_thread == xthread_self())
519		    return;
520#endif
521    _XIDHandler(dpy);
522    _XSeqSyncFunction(dpy);
523    if (dpy->in_ifevent) {
524	dpy->lock_fns->lock_display = _XIfEventLockDisplay;
525	dpy->lock_fns->unlock_display = _XIfEventUnlockDisplay;
526	dpy->lock->internal_lock_display = _XIfEventInternalLockDisplay;
527    }
528}
529
530/*
531 * _XReply is allowed to exit from select/poll and clean up even if a
532 * user-level lock is in force, so it uses this instead of _XFancyLockDisplay.
533 */
534static void _XInternalLockDisplay(
535    Display *dpy,
536    Bool wskip
537    XTHREADS_FILE_LINE_ARGS
538    )
539{
540#ifdef XTHREADS_WARN
541    _XLockDisplayWarn(dpy, file, line);
542#else
543    xmutex_lock(dpy->lock->mutex);
544#endif
545    if (!wskip && dpy->lock->locking_level > 0)
546	_XDisplayLockWait(dpy);
547}
548
549static void _XUserLockDisplay(
550    register Display* dpy)
551{
552    _XDisplayLockWait(dpy);
553
554    if (++dpy->lock->locking_level == 1) {
555	dpy->lock->lock_wait = _XDisplayLockWait;
556	dpy->lock->locking_thread = xthread_self();
557    }
558}
559
560static
561void _XUserUnlockDisplay(
562    register Display* dpy)
563{
564    if (dpy->lock->locking_level > 0 && --dpy->lock->locking_level == 0) {
565	/* signal other threads that might be waiting in XLockDisplay */
566	ConditionBroadcast(dpy, dpy->lock->cv);
567	dpy->lock->lock_wait = NULL;
568	xthread_clear_id(dpy->lock->locking_thread);
569    }
570}
571
572/* returns 0 if initialized ok, -1 if unable to allocate
573   a mutex or other memory */
574
575static int _XInitDisplayLock(
576    Display *dpy)
577{
578    dpy->lock_fns = Xmalloc(sizeof(struct _XLockPtrs));
579    if (dpy->lock_fns == NULL)
580	return -1;
581    dpy->lock = Xmalloc(sizeof(struct _XLockInfo));
582    if (dpy->lock == NULL) {
583	_XFreeDisplayLock(dpy);
584	return -1;
585    }
586    dpy->lock->cv = xcondition_malloc();
587    dpy->lock->mutex = xmutex_malloc();
588    dpy->lock->writers = xcondition_malloc();
589    if (!dpy->lock->cv || !dpy->lock->mutex || !dpy->lock->writers) {
590	_XFreeDisplayLock(dpy);
591	return -1;
592    }
593
594    dpy->lock->reply_bytes_left = 0;
595    dpy->lock->reply_was_read = False;
596    dpy->lock->reply_awaiters = NULL;
597    dpy->lock->reply_awaiters_tail = &dpy->lock->reply_awaiters;
598    dpy->lock->event_awaiters = NULL;
599    dpy->lock->event_awaiters_tail = &dpy->lock->event_awaiters;
600    dpy->lock->reply_first = False;
601    dpy->lock->locking_level = 0;
602    dpy->lock->num_free_cvls = 0;
603    dpy->lock->free_cvls = NULL;
604    xthread_clear_id(dpy->lock->locking_thread);
605    xthread_clear_id(dpy->lock->reading_thread);
606    xthread_clear_id(dpy->lock->conni_thread);
607    xmutex_init(dpy->lock->mutex);
608    xmutex_set_name(dpy->lock->mutex, "Xlib Display");
609    xcondition_init(dpy->lock->cv);
610    xcondition_set_name(dpy->lock->cv, "XLockDisplay");
611    xcondition_init(dpy->lock->writers);
612    xcondition_set_name(dpy->lock->writers, "Xlib wait for writable");
613    dpy->lock_fns->lock_display = _XLockDisplay;
614    dpy->lock->internal_lock_display = _XInternalLockDisplay;
615    dpy->lock_fns->unlock_display = _XUnlockDisplay;
616    dpy->lock->user_lock_display = _XUserLockDisplay;
617    dpy->lock->user_unlock_display = _XUserUnlockDisplay;
618    dpy->lock->pop_reader = _XPopReader;
619    dpy->lock->push_reader = _XPushReader;
620    dpy->lock->condition_wait = _XConditionWait;
621    dpy->lock->condition_signal = _XConditionSignal;
622    dpy->lock->condition_broadcast = _XConditionBroadcast;
623    dpy->lock->create_cvl = _XCreateCVL;
624    dpy->lock->lock_wait = NULL; /* filled in by XLockDisplay() */
625
626    return 0;
627}
628
629#ifdef __UNIXWARE__
630xthread_t __x11_thr_self() { return 0; }
631xthread_t (*_x11_thr_self)() = __x11_thr_self;
632#endif
633
634
635Status XInitThreads(void)
636{
637    if (_Xglobal_lock)
638	return 1;
639#ifdef __UNIXWARE__
640    else {
641       void *dl_handle = dlopen(NULL, RTLD_LAZY);
642       if (!dl_handle ||
643         ((_x11_thr_self = (xthread_t(*)())dlsym(dl_handle,"thr_self")) == 0)) {
644	       _x11_thr_self = __x11_thr_self;
645	       (void) fprintf (stderr,
646	"XInitThreads called, but no libthread in the calling program!\n" );
647       }
648    }
649#endif /* __UNIXWARE__ */
650#ifdef xthread_init
651    xthread_init();		/* return value? */
652#endif
653    if (!(global_lock.lock = xmutex_malloc()))
654	return 0;
655    if (!(i18n_lock.lock = xmutex_malloc())) {
656	xmutex_free(global_lock.lock);
657	global_lock.lock = NULL;
658	return 0;
659    }
660    if (!(conv_lock.lock = xmutex_malloc())) {
661	xmutex_free(global_lock.lock);
662	global_lock.lock = NULL;
663	xmutex_free(i18n_lock.lock);
664	i18n_lock.lock = NULL;
665	return 0;
666    }
667    _Xglobal_lock = &global_lock;
668    xmutex_init(_Xglobal_lock->lock);
669    xmutex_set_name(_Xglobal_lock->lock, "Xlib global");
670    _Xi18n_lock = &i18n_lock;
671    xmutex_init(_Xi18n_lock->lock);
672    xmutex_set_name(_Xi18n_lock->lock, "Xlib i18n");
673    _conv_lock = &conv_lock;
674    xmutex_init(_conv_lock->lock);
675    xmutex_set_name(_conv_lock->lock, "Xlib conv");
676    _XLockMutex_fn = _XLockMutex;
677    _XUnlockMutex_fn = _XUnlockMutex;
678    _XCreateMutex_fn = _XCreateMutex;
679    _XFreeMutex_fn = _XFreeMutex;
680    _XInitDisplayLock_fn = _XInitDisplayLock;
681    _XFreeDisplayLock_fn = _XFreeDisplayLock;
682    _Xthread_self_fn = _Xthread_self;
683
684#ifdef XTHREADS_WARN
685#ifdef XTHREADS_DEBUG
686    setlinebuf(stdout);		/* for debugging messages */
687#endif
688#endif
689
690    return 1;
691}
692
693Status XFreeThreads(void)
694{
695    if (global_lock.lock != NULL) {
696	xmutex_free(global_lock.lock);
697	global_lock.lock = NULL;
698    }
699    if (i18n_lock.lock != NULL) {
700	xmutex_free(i18n_lock.lock);
701	i18n_lock.lock = NULL;
702    }
703    if (conv_lock.lock != NULL) {
704	xmutex_free(conv_lock.lock);
705	conv_lock.lock = NULL;
706    }
707
708    return 1;
709}
710
711#else /* XTHREADS */
712Status XInitThreads(void)
713{
714    return 0;
715}
716
717Status XFreeThreads(void)
718{
719    return 0;
720}
721#endif /* XTHREADS */
722