locking.c revision 61b2299d
1/* $Xorg: locking.c,v 1.5 2001/02/09 02:03:40 xorgcvs Exp $ */
2/*
3
4Copyright 1992, 1998  The Open Group
5
6Permission to use, copy, modify, distribute, and sell this software and its
7documentation for any purpose is hereby granted without fee, provided that
8the above copyright notice appear in all copies and that both that
9copyright notice and this permission notice appear in supporting
10documentation.
11
12The above copyright notice and this permission notice shall be included in
13all copies or substantial portions of the Software.
14
15THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
18OPEN GROUP BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
19AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
20CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21
22Except as contained in this notice, the name of The Open Group shall not be
23used in advertising or otherwise to promote the sale, use or other dealings
24in this Software without prior written authorization from The Open Group.
25
26*/
27/* $XFree86: xc/lib/X11/locking.c,v 1.5 2003/04/13 19:22:22 dawes Exp $ */
28
29/*
30 * Author: Stephen Gildea, MIT X Consortium
31 *
32 * locking.c - multi-thread locking routines implemented in C Threads
33 */
34
35#ifdef HAVE_CONFIG_H
36#include <config.h>
37#endif
38#include "Xlibint.h"
39#undef _XLockMutex
40#undef _XUnlockMutex
41#undef _XCreateMutex
42#undef _XFreeMutex
43
44#ifdef XTHREADS
45
46#ifdef __UNIXWARE__
47#include <dlfcn.h>
48#endif
49
50#include "locking.h"
51#ifdef XTHREADS_WARN
52#include <stdio.h>		/* for warn/debug stuff */
53#endif
54
55#define NUM_FREE_CVLS 4
56
57/* in lcWrap.c */
58extern LockInfoPtr _Xi18n_lock;
59
60#ifdef WIN32
61static DWORD _X_TlsIndex = (DWORD)-1;
62
63void _Xthread_init()
64{
65    if (_X_TlsIndex == (DWORD)-1)
66	_X_TlsIndex = TlsAlloc();
67}
68
69struct _xthread_waiter *
70_Xthread_waiter()
71{
72    struct _xthread_waiter *me;
73
74    if (!(me = TlsGetValue(_X_TlsIndex))) {
75	me = (struct _xthread_waiter *)xmalloc(sizeof(struct _xthread_waiter));
76	me->sem = CreateSemaphore(NULL, 0, 1, NULL);
77	me->next = NULL;
78	TlsSetValue(_X_TlsIndex, me);
79    }
80    return me;
81}
82#endif /* WIN32 */
83
84static xthread_t _Xthread_self(void)
85{
86    return xthread_self();
87}
88
89static LockInfoRec global_lock;
90static LockInfoRec i18n_lock;
91
92#if defined(XTHREADS_WARN) || defined(XTHREADS_FILE_LINE)
93static void _XLockMutex(lip,file,line)
94    LockInfoPtr lip;
95    char* file;
96    int line;
97#else
98static void _XLockMutex(
99    LockInfoPtr lip)
100#endif
101{
102    xmutex_lock(lip->lock);
103}
104
105#if defined(XTHREADS_WARN) || defined(XTHREADS_FILE_LINE)
106static void _XUnlockMutex(
107    LockInfoPtr lip,
108    char* file,
109    int line)
110#else
111static void _XUnlockMutex(
112    LockInfoPtr lip)
113#endif
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
205#if defined(XTHREADS_WARN) || defined(XTHREADS_FILE_LINE)
206static void _XUnlockDisplay(dpy,file,line)
207    Display *dpy;
208    char *file;
209    int line;
210#else
211static void _XUnlockDisplay(
212    Display *dpy)
213#endif
214{
215#ifdef XTHREADS_WARN
216    xthread_t self = xthread_self();
217
218#ifdef XTHREADS_DEBUG
219    if (strcmp("XClearArea.c", file) && strcmp("XDrSegs.c", file)) /* ico */
220	printf("%s line %d: thread %x unlocking display\n", file, line, self);
221#endif /* XTHREADS_DEBUG */
222
223    if (!xthread_have_id(locking_thread))
224	printf("Xlib ERROR: %s line %d thread %x: unlocking display that is not locked\n",
225	       file, line, self);
226    else if (strcmp(file, "XlibInt.c") == 0)
227	xlibint_unlock = True;
228#ifdef XTHREADS_DEBUG
229    else if (strcmp(file, locking_file) != 0)
230	/* not always an error because locking_file is not per-thread */
231	printf("%s line %d: unlocking display locked from %s line %d (probably okay)\n",
232	       file, line, locking_file, locking_line);
233#endif /* XTHREADS_DEBUG */
234    xthread_clear_id(locking_thread);
235
236    locking_history[lock_hist_loc].file = file;
237    locking_history[lock_hist_loc].line = line;
238    locking_history[lock_hist_loc].thread = self;
239    locking_history[lock_hist_loc].lockp = False;
240    lock_hist_loc++;
241    if (lock_hist_loc >= LOCK_HIST_SIZE)
242	lock_hist_loc = 0;
243#endif /* XTHREADS_WARN */
244    xmutex_unlock(dpy->lock->mutex);
245}
246
247
248static struct _XCVList *_XCreateCVL(
249    Display *dpy)
250{
251    struct _XCVList *cvl;
252
253    if ((cvl = dpy->lock->free_cvls) != NULL) {
254	dpy->lock->free_cvls = cvl->next;
255	dpy->lock->num_free_cvls--;
256    } else {
257	cvl = (struct _XCVList *)Xmalloc(sizeof(struct _XCVList));
258	if (!cvl)
259	    return NULL;
260	cvl->cv = xcondition_malloc();
261	if (!cvl->cv) {
262	    Xfree(cvl);
263	    return NULL;
264	}
265	xcondition_init(cvl->cv);
266	xcondition_set_name(cvl->cv, "Xlib read queue");
267    }
268    cvl->next = NULL;
269    return cvl;
270}
271
272/* Put ourselves on the queue to read the connection.
273   Allocates and returns a queue element. */
274
275static struct _XCVList *
276_XPushReader(
277    Display *dpy,
278    struct _XCVList ***tail)
279{
280    struct _XCVList *cvl;
281
282    cvl = _XCreateCVL(dpy);
283#ifdef XTHREADS_DEBUG
284    printf("_XPushReader called in thread %x, pushing %x\n",
285	   xthread_self(), cvl);
286#endif
287    **tail = cvl;
288    *tail = &cvl->next;
289    return cvl;
290}
291
292/* signal the next thread waiting to read the connection */
293
294static void _XPopReader(
295    Display *dpy,
296    struct _XCVList **list,
297    struct _XCVList ***tail)
298{
299    register struct _XCVList *front = *list;
300
301#ifdef XTHREADS_DEBUG
302    printf("_XPopReader called in thread %x, popping %x\n",
303	   xthread_self(), front);
304#endif
305
306    if (dpy->flags & XlibDisplayProcConni)
307	/* we never added ourself in the first place */
308	return;
309
310    if (front) {		/* check "front" for paranoia */
311	*list = front->next;
312	if (*tail == &front->next)	/* did we free the last elt? */
313	    *tail = list;
314	if (dpy->lock->num_free_cvls < NUM_FREE_CVLS) {
315	    front->next = dpy->lock->free_cvls;
316	    dpy->lock->free_cvls = front;
317	    dpy->lock->num_free_cvls++;
318	} else {
319	    xcondition_clear(front->cv);
320	    Xfree((char *)front->cv);
321	    Xfree((char *)front);
322	}
323    }
324
325    /* signal new front after it is in place */
326    if ((dpy->lock->reply_first = (dpy->lock->reply_awaiters != NULL))) {
327	ConditionSignal(dpy, dpy->lock->reply_awaiters->cv);
328    } else if (dpy->lock->event_awaiters) {
329	ConditionSignal(dpy, dpy->lock->event_awaiters->cv);
330    }
331}
332
333#if defined(XTHREADS_WARN) || defined(XTHREADS_FILE_LINE)
334static void _XConditionWait(cv, mutex,file,line)
335    xcondition_t cv;
336    xmutex_t mutex;
337    char *file;
338    int line;
339#else
340static void _XConditionWait(
341    xcondition_t cv,
342    xmutex_t mutex)
343#endif
344{
345#ifdef XTHREADS_WARN
346    xthread_t self = xthread_self();
347    char *old_file = locking_file;
348    int old_line = locking_line;
349
350#ifdef XTHREADS_DEBUG
351    printf("line %d thread %x in condition wait\n", line, self);
352#endif
353    xthread_clear_id(locking_thread);
354
355    locking_history[lock_hist_loc].file = file;
356    locking_history[lock_hist_loc].line = line;
357    locking_history[lock_hist_loc].thread = self;
358    locking_history[lock_hist_loc].lockp = False;
359    lock_hist_loc++;
360    if (lock_hist_loc >= LOCK_HIST_SIZE)
361	lock_hist_loc = 0;
362#endif /* XTHREADS_WARN */
363
364    xcondition_wait(cv, mutex);
365
366#ifdef XTHREADS_WARN
367    locking_thread = self;
368    locking_file = old_file;
369    locking_line = old_line;
370
371    locking_history[lock_hist_loc].file = file;
372    locking_history[lock_hist_loc].line = line;
373    locking_history[lock_hist_loc].thread = self;
374    locking_history[lock_hist_loc].lockp = True;
375    lock_hist_loc++;
376    if (lock_hist_loc >= LOCK_HIST_SIZE)
377	lock_hist_loc = 0;
378#ifdef XTHREADS_DEBUG
379    printf("line %d thread %x was signaled\n", line, self);
380#endif /* XTHREADS_DEBUG */
381#endif /* XTHREADS_WARN */
382}
383
384#if defined(XTHREADS_WARN) || defined(XTHREADS_FILE_LINE)
385static void _XConditionSignal(cv,file,line)
386    xcondition_t cv;
387    char *file;
388    int line;
389#else
390static void _XConditionSignal(
391    xcondition_t cv)
392#endif
393{
394#ifdef XTHREADS_WARN
395#ifdef XTHREADS_DEBUG
396    printf("line %d thread %x is signalling\n", line, xthread_self());
397#endif
398#endif
399    xcondition_signal(cv);
400}
401
402
403#if defined(XTHREADS_WARN) || defined(XTHREADS_FILE_LINE)
404static void _XConditionBroadcast(cv,file,line)
405    xcondition_t cv;
406    char *file;
407    int line;
408#else
409static void _XConditionBroadcast(
410    xcondition_t cv)
411#endif
412{
413#ifdef XTHREADS_WARN
414#ifdef XTHREADS_DEBUG
415    printf("line %d thread %x is broadcasting\n", line, xthread_self());
416#endif
417#endif
418    xcondition_broadcast(cv);
419}
420
421
422static void _XFreeDisplayLock(
423    Display *dpy)
424{
425    struct _XCVList *cvl;
426
427    if (dpy->lock != NULL) {
428	if (dpy->lock->mutex != NULL) {
429	    xmutex_clear(dpy->lock->mutex);
430	    xmutex_free(dpy->lock->mutex);
431	}
432	if (dpy->lock->cv != NULL) {
433	    xcondition_clear(dpy->lock->cv);
434	    xcondition_free(dpy->lock->cv);
435	}
436	if (dpy->lock->writers != NULL) {
437	    xcondition_clear(dpy->lock->writers);
438	    xcondition_free(dpy->lock->writers);
439	}
440	while ((cvl = dpy->lock->free_cvls)) {
441	    dpy->lock->free_cvls = cvl->next;
442	    xcondition_clear(cvl->cv);
443	    Xfree((char *)cvl->cv);
444	    Xfree((char *)cvl);
445	}
446	Xfree((char *)dpy->lock);
447	dpy->lock = NULL;
448    }
449    if (dpy->lock_fns != NULL) {
450	Xfree((char *)dpy->lock_fns);
451	dpy->lock_fns = NULL;
452    }
453}
454
455/*
456 * wait for thread with user-level display lock to release it.
457 */
458
459static void _XDisplayLockWait(
460    Display *dpy)
461{
462    xthread_t self;
463
464    while (dpy->lock->locking_level > 0) {
465	self = xthread_self();
466	if (xthread_equal(dpy->lock->locking_thread, self))
467	    break;
468	ConditionWait(dpy, dpy->lock->cv);
469    }
470}
471
472#if defined(XTHREADS_WARN) || defined(XTHREADS_FILE_LINE)
473static void _XLockDisplay(dpy, file, line)
474    Display *dpy;
475    char *file;			/* source file, from macro */
476    int line;
477#else
478static void _XLockDisplay(
479    Display *dpy)
480#endif
481{
482#ifdef XTHREADS_WARN
483    _XLockDisplayWarn(dpy, file, line);
484#else
485    xmutex_lock(dpy->lock->mutex);
486#endif
487    if (dpy->lock->locking_level > 0)
488	_XDisplayLockWait(dpy);
489}
490
491/*
492 * _XReply is allowed to exit from select/poll and clean up even if a
493 * user-level lock is in force, so it uses this instead of _XFancyLockDisplay.
494 */
495#if defined(XTHREADS_WARN) || defined(XTHREADS_FILE_LINE)
496static void _XInternalLockDisplay(dpy, wskip, file, line)
497    Display *dpy;
498    Bool wskip;
499    char *file;			/* source file, from macro */
500    int line;
501#else
502static void _XInternalLockDisplay(
503    Display *dpy,
504    Bool wskip)
505#endif
506{
507#ifdef XTHREADS_WARN
508    _XLockDisplayWarn(dpy, file, line);
509#else
510    xmutex_lock(dpy->lock->mutex);
511#endif
512    if (!wskip && dpy->lock->locking_level > 0)
513	_XDisplayLockWait(dpy);
514}
515
516static void _XUserLockDisplay(
517    register Display* dpy)
518{
519    if (++dpy->lock->locking_level == 1) {
520	dpy->lock->lock_wait = _XDisplayLockWait;
521	dpy->lock->locking_thread = xthread_self();
522    }
523}
524
525static
526void _XUserUnlockDisplay(
527    register Display* dpy)
528{
529    if (dpy->lock->locking_level > 0 && --dpy->lock->locking_level == 0) {
530	/* signal other threads that might be waiting in XLockDisplay */
531	ConditionBroadcast(dpy, dpy->lock->cv);
532	dpy->lock->lock_wait = NULL;
533	xthread_clear_id(dpy->lock->locking_thread);
534    }
535}
536
537/* returns 0 if initialized ok, -1 if unable to allocate
538   a mutex or other memory */
539
540static int _XInitDisplayLock(
541    Display *dpy)
542{
543    dpy->lock_fns = (struct _XLockPtrs*)Xmalloc(sizeof(struct _XLockPtrs));
544    if (dpy->lock_fns == NULL)
545	return -1;
546    dpy->lock = (struct _XLockInfo *)Xmalloc(sizeof(struct _XLockInfo));
547    if (dpy->lock == NULL) {
548	_XFreeDisplayLock(dpy);
549	return -1;
550    }
551    dpy->lock->cv = xcondition_malloc();
552    dpy->lock->mutex = xmutex_malloc();
553    dpy->lock->writers = xcondition_malloc();
554    if (!dpy->lock->cv || !dpy->lock->mutex || !dpy->lock->writers) {
555	_XFreeDisplayLock(dpy);
556	return -1;
557    }
558
559    dpy->lock->reply_bytes_left = 0;
560    dpy->lock->reply_was_read = False;
561    dpy->lock->reply_awaiters = NULL;
562    dpy->lock->reply_awaiters_tail = &dpy->lock->reply_awaiters;
563    dpy->lock->event_awaiters = NULL;
564    dpy->lock->event_awaiters_tail = &dpy->lock->event_awaiters;
565    dpy->lock->reply_first = False;
566    dpy->lock->locking_level = 0;
567    dpy->lock->num_free_cvls = 0;
568    dpy->lock->free_cvls = NULL;
569    xthread_clear_id(dpy->lock->locking_thread);
570    xthread_clear_id(dpy->lock->reading_thread);
571    xthread_clear_id(dpy->lock->conni_thread);
572    xmutex_init(dpy->lock->mutex);
573    xmutex_set_name(dpy->lock->mutex, "Xlib Display");
574    xcondition_init(dpy->lock->cv);
575    xcondition_set_name(dpy->lock->cv, "XLockDisplay");
576    xcondition_init(dpy->lock->writers);
577    xcondition_set_name(dpy->lock->writers, "Xlib wait for writable");
578    dpy->lock_fns->lock_display = _XLockDisplay;
579    dpy->lock->internal_lock_display = _XInternalLockDisplay;
580    dpy->lock_fns->unlock_display = _XUnlockDisplay;
581    dpy->lock->user_lock_display = _XUserLockDisplay;
582    dpy->lock->user_unlock_display = _XUserUnlockDisplay;
583    dpy->lock->pop_reader = _XPopReader;
584    dpy->lock->push_reader = _XPushReader;
585    dpy->lock->condition_wait = _XConditionWait;
586    dpy->lock->condition_signal = _XConditionSignal;
587    dpy->lock->condition_broadcast = _XConditionBroadcast;
588    dpy->lock->create_cvl = _XCreateCVL;
589    dpy->lock->lock_wait = NULL; /* filled in by XLockDisplay() */
590
591    return 0;
592}
593
594#ifdef __UNIXWARE__
595xthread_t __x11_thr_self() { return 0; }
596xthread_t (*_x11_thr_self)() = __x11_thr_self;
597#endif
598
599
600Status XInitThreads(void)
601{
602    if (_Xglobal_lock)
603	return 1;
604#ifdef __UNIXWARE__
605    else {
606       void *dl_handle = dlopen(NULL, RTLD_LAZY);
607       if (!dl_handle ||
608         ((_x11_thr_self = (xthread_t(*)())dlsym(dl_handle,"thr_self")) == 0)) {
609	       _x11_thr_self = __x11_thr_self;
610	       (void) fprintf (stderr,
611	"XInitThreads called, but no libthread in the calling program!\n" );
612       }
613    }
614#endif /* __UNIXWARE__ */
615#ifdef xthread_init
616    xthread_init();		/* return value? */
617#endif
618    if (!(global_lock.lock = xmutex_malloc()))
619	return 0;
620    if (!(i18n_lock.lock = xmutex_malloc())) {
621	xmutex_free(global_lock.lock);
622	global_lock.lock = NULL;
623	return 0;
624    }
625    _Xglobal_lock = &global_lock;
626    xmutex_init(_Xglobal_lock->lock);
627    xmutex_set_name(_Xglobal_lock->lock, "Xlib global");
628    _Xi18n_lock = &i18n_lock;
629    xmutex_init(_Xi18n_lock->lock);
630    xmutex_set_name(_Xi18n_lock->lock, "Xlib i18n");
631    _XLockMutex_fn = _XLockMutex;
632    _XUnlockMutex_fn = _XUnlockMutex;
633    _XCreateMutex_fn = _XCreateMutex;
634    _XFreeMutex_fn = _XFreeMutex;
635    _XInitDisplayLock_fn = _XInitDisplayLock;
636    _XFreeDisplayLock_fn = _XFreeDisplayLock;
637    _Xthread_self_fn = _Xthread_self;
638
639#ifdef XTHREADS_WARN
640#ifdef XTHREADS_DEBUG
641    setlinebuf(stdout);		/* for debugging messages */
642#endif
643#endif
644
645    return 1;
646}
647
648#else /* XTHREADS */
649Status XInitThreads()
650{
651    return 0;
652}
653#endif /* XTHREADS */
654