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