1/************************************************************
2Copyright (c) 1993, Oracle and/or its affiliates.
3
4Permission is hereby granted, free of charge, to any person obtaining a
5copy of this software and associated documentation files (the "Software"),
6to deal in the Software without restriction, including without limitation
7the rights to use, copy, modify, merge, publish, distribute, sublicense,
8and/or sell copies of the Software, and to permit persons to whom the
9Software is furnished to do so, subject to the following conditions:
10
11The above copyright notice and this permission notice (including the next
12paragraph) shall be included in all copies or substantial portions of the
13Software.
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
18THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
20FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
21DEALINGS IN THE SOFTWARE.
22
23********************************************************/
24
25/*
26
27Copyright 1994, 1998  The Open Group
28
29Permission to use, copy, modify, distribute, and sell this software and its
30documentation for any purpose is hereby granted without fee, provided that
31the above copyright notice appear in all copies and that both that
32copyright notice and this permission notice appear in supporting
33documentation.
34
35The above copyright notice and this permission notice shall be included in
36all copies or substantial portions of the Software.
37
38THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
39IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
40FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
41OPEN GROUP BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
42AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
43CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
44
45Except as contained in this notice, the name of The Open Group shall not be
46used in advertising or otherwise to promote the sale, use or other dealings
47in this Software without prior written authorization from The Open Group.
48
49*/
50
51#ifdef HAVE_CONFIG_H
52#include <config.h>
53#endif
54#include "IntrinsicI.h"
55
56#ifdef XTHREADS
57
58#define xmalloc __XtMalloc
59#define xfree XtFree
60#include <X11/Xthreads.h>
61
62#ifndef NDEBUG
63#define NDEBUG
64#endif
65#include <assert.h>
66#include <stdio.h>
67
68typedef struct _ThreadStack {
69    unsigned int size;
70    int sp;
71    struct _Tstack {
72        xthread_t t;
73        xcondition_t c;
74    } *st;
75} ThreadStack;
76
77typedef struct _LockRec {
78    xmutex_t mutex;
79    int level;
80    ThreadStack stack;
81#ifndef _XMUTEX_NESTS
82    xthread_t holder;
83    xcondition_t cond;
84#endif
85} LockRec;
86
87#define STACK_INCR 16
88
89static LockPtr process_lock = NULL;
90
91static void
92InitProcessLock(void)
93{
94    if (!process_lock) {
95        process_lock = XtNew(LockRec);
96        process_lock->mutex = xmutex_malloc();
97        xmutex_init(process_lock->mutex);
98        process_lock->level = 0;
99#ifndef _XMUTEX_NESTS
100        process_lock->cond = xcondition_malloc();
101        xcondition_init(process_lock->cond);
102        xthread_clear_id(process_lock->holder);
103#endif
104    }
105}
106
107static void
108ProcessLock(void)
109{
110#ifdef _XMUTEX_NESTS
111    xmutex_lock(process_lock->mutex);
112    process_lock->level++;
113#else
114    xthread_t this_thread = xthread_self();
115
116    xmutex_lock(process_lock->mutex);
117
118    if (!xthread_have_id(process_lock->holder)) {
119        process_lock->holder = this_thread;
120        xmutex_unlock(process_lock->mutex);
121        return;
122    }
123
124    if (xthread_equal(process_lock->holder, this_thread)) {
125        process_lock->level++;
126        xmutex_unlock(process_lock->mutex);
127        return;
128    }
129
130    while (xthread_have_id(process_lock->holder))
131        xcondition_wait(process_lock->cond, process_lock->mutex);
132
133    process_lock->holder = this_thread;
134    assert(xthread_equal(process_lock->holder, this_thread));
135    xmutex_unlock(process_lock->mutex);
136#endif
137}
138
139static void
140ProcessUnlock(void)
141{
142#ifdef _XMUTEX_NESTS
143    process_lock->level--;
144    xmutex_unlock(process_lock->mutex);
145#else
146    xmutex_lock(process_lock->mutex);
147    assert(xthread_equal(process_lock->holder, xthread_self()));
148    if (process_lock->level != 0) {
149        process_lock->level--;
150        xmutex_unlock(process_lock->mutex);
151        return;
152    }
153
154    xthread_clear_id(process_lock->holder);
155    xcondition_signal(process_lock->cond);
156
157    xmutex_unlock(process_lock->mutex);
158#endif
159}
160
161static void
162AppLock(XtAppContext app)
163{
164    LockPtr app_lock = app->lock_info;
165
166#ifdef _XMUTEX_NESTS
167    xmutex_lock(app_lock->mutex);
168    app_lock->level++;
169#else
170    xthread_t self = xthread_self();
171
172    xmutex_lock(app_lock->mutex);
173    if (!xthread_have_id(app_lock->holder)) {
174        app_lock->holder = self;
175        assert(xthread_equal(app_lock->holder, self));
176        xmutex_unlock(app_lock->mutex);
177        return;
178    }
179    if (xthread_equal(app_lock->holder, self)) {
180        app_lock->level++;
181        xmutex_unlock(app_lock->mutex);
182        return;
183    }
184    while (xthread_have_id(app_lock->holder)) {
185        xcondition_wait(app_lock->cond, app_lock->mutex);
186    }
187    app_lock->holder = self;
188    assert(xthread_equal(app_lock->holder, self));
189    xmutex_unlock(app_lock->mutex);
190#endif
191}
192
193static void
194AppUnlock(XtAppContext app)
195{
196    LockPtr app_lock = app->lock_info;
197
198#ifdef _XMUTEX_NESTS
199    app_lock->level--;
200    xmutex_unlock(app_lock->mutex);
201#else
202    xthread_t self;
203
204    self = xthread_self();
205    (void) self;
206
207    xmutex_lock(app_lock->mutex);
208    assert(xthread_equal(app_lock->holder, self));
209    if (app_lock->level != 0) {
210        app_lock->level--;
211        xmutex_unlock(app_lock->mutex);
212        return;
213    }
214    xthread_clear_id(app_lock->holder);
215    xcondition_signal(app_lock->cond);
216    xmutex_unlock(app_lock->mutex);
217#endif
218}
219
220static void
221YieldAppLock(XtAppContext app,
222             Boolean *push_thread,
223             Boolean *pushed_thread,
224             int *level)
225{
226    LockPtr app_lock = app->lock_info;
227    xthread_t self = xthread_self();
228
229#ifndef _XMUTEX_NESTS
230    xmutex_lock(app_lock->mutex);
231    assert(xthread_equal(app_lock->holder, self));
232#endif
233    *level = app_lock->level;
234    if (*push_thread) {
235        *push_thread = FALSE;
236        *pushed_thread = TRUE;
237
238        if (app_lock->stack.sp == (int) app_lock->stack.size - 1) {
239            unsigned ii;
240
241            app_lock->stack.st = (struct _Tstack *)
242                XtReallocArray(app_lock->stack.st,
243                               (Cardinal) (app_lock->stack.size + STACK_INCR),
244                               (Cardinal) sizeof(struct _Tstack));
245            ii = app_lock->stack.size;
246            app_lock->stack.size += STACK_INCR;
247            for (; ii < app_lock->stack.size; ii++) {
248                app_lock->stack.st[ii].c = xcondition_malloc();
249                xcondition_init(app_lock->stack.st[ii].c);
250            }
251        }
252        app_lock->stack.st[++(app_lock->stack.sp)].t = self;
253    }
254#ifdef _XMUTEX_NESTS
255    while (app_lock->level > 0) {
256        app_lock->level--;
257        xmutex_unlock(app_lock->mutex);
258    }
259#else
260    xcondition_signal(app_lock->cond);
261    app_lock->level = 0;
262    xthread_clear_id(app_lock->holder);
263    xmutex_unlock(app_lock->mutex);
264#endif
265}
266
267static void
268RestoreAppLock(XtAppContext app, int level, Boolean *pushed_thread)
269{
270    LockPtr app_lock = app->lock_info;
271    xthread_t self = xthread_self();
272
273    xmutex_lock(app_lock->mutex);
274#ifdef _XMUTEX_NESTS
275    app_lock->level++;
276#else
277    while (xthread_have_id(app_lock->holder)) {
278        xcondition_wait(app_lock->cond, app_lock->mutex);
279    }
280#endif
281    if (!xthread_equal(app_lock->stack.st[app_lock->stack.sp].t, self)) {
282        int ii;
283
284        for (ii = app_lock->stack.sp - 1; ii >= 0; ii--) {
285            if (xthread_equal(app_lock->stack.st[ii].t, self)) {
286                xcondition_wait(app_lock->stack.st[ii].c, app_lock->mutex);
287                break;
288            }
289        }
290#ifndef _XMUTEX_NESTS
291        while (xthread_have_id(app_lock->holder)) {
292            xcondition_wait(app_lock->cond, app_lock->mutex);
293        }
294#endif
295    }
296#ifdef _XMUTEX_NESTS
297    while (app_lock->level < level) {
298        xmutex_lock(app_lock->mutex);
299        app_lock->level++;
300    }
301#else
302    app_lock->holder = self;
303    app_lock->level = level;
304    assert(xthread_equal(app_lock->holder, self));
305#endif
306    if (*pushed_thread) {
307        *pushed_thread = FALSE;
308        (app_lock->stack.sp)--;
309        if (app_lock->stack.sp >= 0) {
310            xcondition_signal(app_lock->stack.st[app_lock->stack.sp].c);
311        }
312    }
313#ifndef _XMUTEX_NESTS
314    xmutex_unlock(app_lock->mutex);
315#endif
316}
317
318static void
319FreeAppLock(XtAppContext app)
320{
321    unsigned ii;
322    LockPtr app_lock = app->lock_info;
323
324    if (app_lock) {
325        xmutex_clear(app_lock->mutex);
326        xmutex_free(app_lock->mutex);
327#ifndef _XMUTEX_NESTS
328        xcondition_clear(app_lock->cond);
329        xcondition_free(app_lock->cond);
330#endif
331        if (app_lock->stack.st != (struct _Tstack *) NULL) {
332            for (ii = 0; ii < app_lock->stack.size; ii++) {
333                xcondition_clear(app_lock->stack.st[ii].c);
334                xcondition_free(app_lock->stack.st[ii].c);
335            }
336            XtFree((char *) app_lock->stack.st);
337        }
338        XtFree((char *) app_lock);
339        app->lock_info = NULL;
340    }
341}
342
343static void
344InitAppLock(XtAppContext app)
345{
346    int ii;
347    LockPtr app_lock;
348
349    app->lock = AppLock;
350    app->unlock = AppUnlock;
351    app->yield_lock = YieldAppLock;
352    app->restore_lock = RestoreAppLock;
353    app->free_lock = FreeAppLock;
354
355    app_lock = app->lock_info = XtNew(LockRec);
356    app_lock->mutex = xmutex_malloc();
357    xmutex_init(app_lock->mutex);
358    app_lock->level = 0;
359#ifndef _XMUTEX_NESTS
360    app_lock->cond = xcondition_malloc();
361    xcondition_init(app_lock->cond);
362    xthread_clear_id(app_lock->holder);
363#endif
364    app_lock->stack.size = STACK_INCR;
365    app_lock->stack.sp = -1;
366    app_lock->stack.st = XtMallocArray(STACK_INCR, sizeof(struct _Tstack));
367    for (ii = 0; ii < STACK_INCR; ii++) {
368        app_lock->stack.st[ii].c = xcondition_malloc();
369        xcondition_init(app_lock->stack.st[ii].c);
370    }
371}
372
373#endif                          /* defined(XTHREADS) */
374
375void
376XtAppLock(XtAppContext app)
377{
378#ifdef XTHREADS
379    if (app->lock)
380        (*app->lock) (app);
381#endif
382}
383
384void
385XtAppUnlock(XtAppContext app)
386{
387#ifdef XTHREADS
388    if (app->unlock)
389        (*app->unlock) (app);
390#endif
391}
392
393void
394XtProcessLock(void)
395{
396#ifdef XTHREADS
397    if (_XtProcessLock)
398        (*_XtProcessLock) ();
399#endif
400}
401
402void
403XtProcessUnlock(void)
404{
405#ifdef XTHREADS
406    if (_XtProcessUnlock)
407        (*_XtProcessUnlock) ();
408#endif
409}
410
411Boolean
412XtToolkitThreadInitialize(void)
413{
414#ifdef XTHREADS
415    if (_XtProcessLock == NULL) {
416#ifdef xthread_init
417        xthread_init();
418#endif
419        InitProcessLock();
420        _XtProcessLock = ProcessLock;
421        _XtProcessUnlock = ProcessUnlock;
422        _XtInitAppLock = InitAppLock;
423    }
424    return True;
425#else
426    return False;
427#endif
428}
429