b3dfb3b01b8d831ed7cc5b4f28b4754005228350
[platform/upstream/libX11.git] / src / locking.c
1 /*
2
3 Copyright 1992, 1998  The Open Group
4
5 Permission to use, copy, modify, distribute, and sell this software and its
6 documentation for any purpose is hereby granted without fee, provided that
7 the above copyright notice appear in all copies and that both that
8 copyright notice and this permission notice appear in supporting
9 documentation.
10
11 The above copyright notice and this permission notice shall be included in
12 all copies or substantial portions of the Software.
13
14 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
17 OPEN GROUP BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
18 AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
19 CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
20
21 Except as contained in this notice, the name of The Open Group shall not be
22 used in advertising or otherwise to promote the sale, use or other dealings
23 in 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 */
68 extern LockInfoPtr _Xi18n_lock;
69
70 #ifdef WIN32
71 static DWORD _X_TlsIndex = (DWORD)-1;
72
73 void _Xthread_init(void)
74 {
75     if (_X_TlsIndex == (DWORD)-1)
76         _X_TlsIndex = TlsAlloc();
77 }
78
79 struct _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
94 static xthread_t _Xthread_self(void)
95 {
96     return xthread_self();
97 }
98
99 static LockInfoRec global_lock;
100 static LockInfoRec i18n_lock;
101
102 static void _XLockMutex(
103     LockInfoPtr lip
104     XTHREADS_FILE_LINE_ARGS
105     )
106 {
107     xmutex_lock(lip->lock);
108 }
109
110 static void _XUnlockMutex(
111     LockInfoPtr lip
112     XTHREADS_FILE_LINE_ARGS
113     )
114 {
115     xmutex_unlock(lip->lock);
116 }
117
118 static 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
128 static void _XFreeMutex(
129     LockInfoPtr lip)
130 {
131     xmutex_clear(lip->lock);
132     xmutex_free(lip->lock);
133 }
134
135 #ifdef XTHREADS_WARN
136 static char *locking_file;
137 static int locking_line;
138 static xthread_t locking_thread;
139 static 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
144 static 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
151 int lock_hist_loc = 0;          /* next slot to fill */
152
153 static 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 static 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
243 static 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
270 static 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
289 static 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
328 static 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
373 static 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
387 static 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
401 static 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
438 static 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
451 static 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  */
471 static 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
486 static void _XUserLockDisplay(
487     register Display* dpy)
488 {
489     _XDisplayLockWait(dpy);
490
491     if (++dpy->lock->locking_level == 1) {
492         dpy->lock->lock_wait = _XDisplayLockWait;
493         dpy->lock->locking_thread = xthread_self();
494     }
495 }
496
497 static
498 void _XUserUnlockDisplay(
499     register Display* dpy)
500 {
501     if (dpy->lock->locking_level > 0 && --dpy->lock->locking_level == 0) {
502         /* signal other threads that might be waiting in XLockDisplay */
503         ConditionBroadcast(dpy, dpy->lock->cv);
504         dpy->lock->lock_wait = NULL;
505         xthread_clear_id(dpy->lock->locking_thread);
506     }
507 }
508
509 /* returns 0 if initialized ok, -1 if unable to allocate
510    a mutex or other memory */
511
512 static int _XInitDisplayLock(
513     Display *dpy)
514 {
515     dpy->lock_fns = (struct _XLockPtrs*)Xmalloc(sizeof(struct _XLockPtrs));
516     if (dpy->lock_fns == NULL)
517         return -1;
518     dpy->lock = (struct _XLockInfo *)Xmalloc(sizeof(struct _XLockInfo));
519     if (dpy->lock == NULL) {
520         _XFreeDisplayLock(dpy);
521         return -1;
522     }
523     dpy->lock->cv = xcondition_malloc();
524     dpy->lock->mutex = xmutex_malloc();
525     dpy->lock->writers = xcondition_malloc();
526     if (!dpy->lock->cv || !dpy->lock->mutex || !dpy->lock->writers) {
527         _XFreeDisplayLock(dpy);
528         return -1;
529     }
530
531     dpy->lock->reply_bytes_left = 0;
532     dpy->lock->reply_was_read = False;
533     dpy->lock->reply_awaiters = NULL;
534     dpy->lock->reply_awaiters_tail = &dpy->lock->reply_awaiters;
535     dpy->lock->event_awaiters = NULL;
536     dpy->lock->event_awaiters_tail = &dpy->lock->event_awaiters;
537     dpy->lock->reply_first = False;
538     dpy->lock->locking_level = 0;
539     dpy->lock->num_free_cvls = 0;
540     dpy->lock->free_cvls = NULL;
541     xthread_clear_id(dpy->lock->locking_thread);
542     xthread_clear_id(dpy->lock->reading_thread);
543     xthread_clear_id(dpy->lock->conni_thread);
544     xmutex_init(dpy->lock->mutex);
545     xmutex_set_name(dpy->lock->mutex, "Xlib Display");
546     xcondition_init(dpy->lock->cv);
547     xcondition_set_name(dpy->lock->cv, "XLockDisplay");
548     xcondition_init(dpy->lock->writers);
549     xcondition_set_name(dpy->lock->writers, "Xlib wait for writable");
550     dpy->lock_fns->lock_display = _XLockDisplay;
551     dpy->lock->internal_lock_display = _XInternalLockDisplay;
552     dpy->lock_fns->unlock_display = _XUnlockDisplay;
553     dpy->lock->user_lock_display = _XUserLockDisplay;
554     dpy->lock->user_unlock_display = _XUserUnlockDisplay;
555     dpy->lock->pop_reader = _XPopReader;
556     dpy->lock->push_reader = _XPushReader;
557     dpy->lock->condition_wait = _XConditionWait;
558     dpy->lock->condition_signal = _XConditionSignal;
559     dpy->lock->condition_broadcast = _XConditionBroadcast;
560     dpy->lock->create_cvl = _XCreateCVL;
561     dpy->lock->lock_wait = NULL; /* filled in by XLockDisplay() */
562
563     return 0;
564 }
565
566 #ifdef __UNIXWARE__
567 xthread_t __x11_thr_self() { return 0; }
568 xthread_t (*_x11_thr_self)() = __x11_thr_self;
569 #endif
570
571
572 Status XInitThreads(void)
573 {
574     if (_Xglobal_lock)
575         return 1;
576 #ifdef __UNIXWARE__
577     else {
578        void *dl_handle = dlopen(NULL, RTLD_LAZY);
579        if (!dl_handle ||
580          ((_x11_thr_self = (xthread_t(*)())dlsym(dl_handle,"thr_self")) == 0)) {
581                _x11_thr_self = __x11_thr_self;
582                (void) fprintf (stderr,
583         "XInitThreads called, but no libthread in the calling program!\n" );
584        }
585     }
586 #endif /* __UNIXWARE__ */
587 #ifdef xthread_init
588     xthread_init();             /* return value? */
589 #endif
590     if (!(global_lock.lock = xmutex_malloc()))
591         return 0;
592     if (!(i18n_lock.lock = xmutex_malloc())) {
593         xmutex_free(global_lock.lock);
594         global_lock.lock = NULL;
595         return 0;
596     }
597     _Xglobal_lock = &global_lock;
598     xmutex_init(_Xglobal_lock->lock);
599     xmutex_set_name(_Xglobal_lock->lock, "Xlib global");
600     _Xi18n_lock = &i18n_lock;
601     xmutex_init(_Xi18n_lock->lock);
602     xmutex_set_name(_Xi18n_lock->lock, "Xlib i18n");
603     _XLockMutex_fn = _XLockMutex;
604     _XUnlockMutex_fn = _XUnlockMutex;
605     _XCreateMutex_fn = _XCreateMutex;
606     _XFreeMutex_fn = _XFreeMutex;
607     _XInitDisplayLock_fn = _XInitDisplayLock;
608     _XFreeDisplayLock_fn = _XFreeDisplayLock;
609     _Xthread_self_fn = _Xthread_self;
610
611 #ifdef XTHREADS_WARN
612 #ifdef XTHREADS_DEBUG
613     setlinebuf(stdout);         /* for debugging messages */
614 #endif
615 #endif
616
617     return 1;
618 }
619
620 #else /* XTHREADS */
621 Status XInitThreads(void)
622 {
623     return 0;
624 }
625 #endif /* XTHREADS */