text: /*
   
  Copyright 1992, 1998  The Open Group
  
  Permission to use, copy, modify, distribute, and sell this software and its
  documentation for any purpose is hereby granted without fee, provided that
  the above copyright notice appear in all copies and that both that
  copyright notice and this permission notice appear in supporting
  documentation.
  
  The above copyright notice and this permission notice shall be included in
  all copies or substantial portions of the Software.
  
  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
  OPEN GROUP BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
  AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
  CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
  
  Except as contained in this notice, the name of The Open Group shall not be
  used in advertising or otherwise to promote the sale, use or other dealings
  in this Software without prior written authorization from The Open Group.
  
  */
  /* $XFree86: xc/lib/X11/locking.c,v 1.7 2006/01/09 14:58:43 dawes Exp $ */
  
  /*
   * Author: Stephen Gildea, MIT X Consortium
   *
   * locking.c - multi-thread locking routines implemented in C Threads
   */
  
  #include "Xlibint.h"
  #undef _XLockMutex
  #undef _XUnlockMutex
  #undef _XCreateMutex
  #undef _XFreeMutex
  
  #ifdef XTHREADS
  
  #include "locking.h"
  #ifdef XTHREADS_WARN
  #include <stdio.h>		/* for warn/debug stuff */
  #endif
  
  #define NUM_FREE_CVLS 4
  
  /* in lcWrap.c */
  extern LockInfoPtr _Xi18n_lock;
  
  #ifdef WIN32
  static DWORD _X_TlsIndex = (DWORD)-1;
  
  _Xthread_init()
  {
      if (_X_TlsIndex == (DWORD)-1)
  	_X_TlsIndex = TlsAlloc();
  }
  
  struct _xthread_waiter *
  _Xthread_waiter()
  {
      struct _xthread_waiter *me;
  
      if (!(me = TlsGetValue(_X_TlsIndex))) {
  	me = (struct _xthread_waiter *)xmalloc(sizeof(struct _xthread_waiter));
  	me->sem = CreateSemaphore(NULL, 0, 1, NULL);
  	me->next = NULL;
  	TlsSetValue(_X_TlsIndex, me);
      }
      return me;
  }
  #endif /* WIN32 */
  
  static xthread_t _Xthread_self(void)
  {
      return xthread_self();
  }
  
  static LockInfoRec global_lock;
  static LockInfoRec i18n_lock;
  
  #if defined(XTHREADS_WARN) || defined(XTHREADS_FILE_LINE)
  static void _XLockMutex(lip,file,line)
      LockInfoPtr lip;
      char* file;
      int line;
  #else
  static void _XLockMutex(
      LockInfoPtr lip)
  #endif
  {
      xmutex_lock(lip->lock);
  }
  
  #if defined(XTHREADS_WARN) || defined(XTHREADS_FILE_LINE)
  static void _XUnlockMutex(
      LockInfoPtr lip,
      char* file,
      int line)
  #else
  static void _XUnlockMutex(
      LockInfoPtr lip)
  #endif
  {
      xmutex_unlock(lip->lock);
  }
  
  static void _XCreateMutex(
      LockInfoPtr lip)
  {
      lip->lock = xmutex_malloc();
      if (lip->lock) {
  	xmutex_init(lip->lock);
  	xmutex_set_name(lip->lock, "Xlib");
      }
  }
  
  static void _XFreeMutex(
      LockInfoPtr lip)
  {
      xmutex_clear(lip->lock);
      xmutex_free(lip->lock);
  }
  
  #ifdef XTHREADS_WARN
  static char *locking_file;
  static int locking_line;
  static xthread_t locking_thread;
  static Bool xlibint_unlock = False; /* XlibInt.c may Unlock and re-Lock */
  
  /* history that is useful to examine in a debugger */
  #define LOCK_HIST_SIZE 21
  
  static struct {
      Bool lockp;			/* True for lock, False for unlock */
      xthread_t thread;
      char *file;
      int line;
  } locking_history[LOCK_HIST_SIZE];
  
  int lock_hist_loc = 0;		/* next slot to fill */
  
  static void _XLockDisplayWarn(
      Display *dpy,
      char *file,			/* source file, from macro */
      int line)
  {
      xthread_t self;
      xthread_t old_locker;
  
      self = xthread_self();
      old_locker = locking_thread;
      if (xthread_have_id(old_locker)) {
  	if (xthread_equal(old_locker, self))
  	    printf("Xlib ERROR: %s line %d thread %x: locking display already locked at %s line %d\n",
  		   file, line, self, locking_file, locking_line);
  #ifdef XTHREADS_DEBUG
  	else
  	    printf("%s line %d: thread %x waiting on lock held by %s line %d thread %x\n",
  		   file, line, self,
  		   locking_file, locking_line, old_locker);
  #endif /* XTHREADS_DEBUG */
      }
  
      xmutex_lock(dpy->lock->mutex);
  
      if (strcmp(file, "XlibInt.c") == 0) {
  	if (!xlibint_unlock)
  	    printf("Xlib ERROR: XlibInt.c line %d thread %x locking display it did not unlock\n",
  		   line, self);
  	xlibint_unlock = False;
      }
  
  #ifdef XTHREADS_DEBUG
      /* if (old_locker  &&  old_locker != self) */
      if (strcmp("XClearArea.c", file) && strcmp("XDrSegs.c", file)) /* ico */
  	printf("%s line %d: thread %x got display lock\n", file, line, self);
  #endif /* XTHREADS_DEBUG */
  
      locking_thread = self;
      if (strcmp(file, "XlibInt.c") != 0) {
  	locking_file = file;
  	locking_line = line;
      }
      locking_history[lock_hist_loc].file = file;
      locking_history[lock_hist_loc].line = line;
      locking_history[lock_hist_loc].thread = self;
      locking_history[lock_hist_loc].lockp = True;
      lock_hist_loc++;
      if (lock_hist_loc >= LOCK_HIST_SIZE)
  	lock_hist_loc = 0;
  }
  #endif /* XTHREADS_WARN */
  
  #if defined(XTHREADS_WARN) || defined(XTHREADS_FILE_LINE)
  static void _XUnlockDisplay(dpy,file,line)
      Display *dpy;
      char *file;
      int line;
  #else
  static void _XUnlockDisplay(
      Display *dpy)
  #endif
  {
  #ifdef XTHREADS_WARN
      xthread_t self = xthread_self();
  
  #ifdef XTHREADS_DEBUG
      if (strcmp("XClearArea.c", file) && strcmp("XDrSegs.c", file)) /* ico */
  	printf("%s line %d: thread %x unlocking display\n", file, line, self);
  #endif /* XTHREADS_DEBUG */
  
      if (!xthread_have_id(locking_thread))
  	printf("Xlib ERROR: %s line %d thread %x: unlocking display that is not locked\n",
  	       file, line, self);
      else if (strcmp(file, "XlibInt.c") == 0)
  	xlibint_unlock = True;
  #ifdef XTHREADS_DEBUG
      else if (strcmp(file, locking_file) != 0)
  	/* not always an error because locking_file is not per-thread */
  	printf("%s line %d: unlocking display locked from %s line %d (probably okay)\n",
  	       file, line, locking_file, locking_line);
  #endif /* XTHREADS_DEBUG */
      xthread_clear_id(locking_thread);
  
      locking_history[lock_hist_loc].file = file;
      locking_history[lock_hist_loc].line = line;
      locking_history[lock_hist_loc].thread = self;
      locking_history[lock_hist_loc].lockp = False;
      lock_hist_loc++;
      if (lock_hist_loc >= LOCK_HIST_SIZE)
  	lock_hist_loc = 0;
  #endif /* XTHREADS_WARN */
      xmutex_unlock(dpy->lock->mutex);
  }
  
  
  static struct _XCVList *_XCreateCVL(
      Display *dpy)
  {
      struct _XCVList *cvl;
  
      if ((cvl = dpy->lock->free_cvls) != NULL) {
  	dpy->lock->free_cvls = cvl->next;
  	dpy->lock->num_free_cvls--;
      } else {
  	cvl = (struct _XCVList *)Xmalloc(sizeof(struct _XCVList));
  	if (!cvl)
  	    return NULL;
  	cvl->cv = xcondition_malloc();
  	if (!cvl->cv) {
  	    Xfree(cvl);
  	    return NULL;
  	}
  	xcondition_init(cvl->cv);
  	xcondition_set_name(cvl->cv, "Xlib read queue");
      }
      cvl->next = NULL;
      return cvl;
  }
  
  /* Put ourselves on the queue to read the connection.
     Allocates and returns a queue element. */
  
  static struct _XCVList *
  _XPushReader(
      Display *dpy,
      struct _XCVList ***tail)
  {
      struct _XCVList *cvl;
  
      cvl = _XCreateCVL(dpy);
  #ifdef XTHREADS_DEBUG
      printf("_XPushReader called in thread %x, pushing %x\n",
  	   xthread_self(), cvl);
  #endif
      **tail = cvl;
      *tail = &cvl->next;
      return cvl;
  }
  
  /* signal the next thread waiting to read the connection */
  
  static void _XPopReader(
      Display *dpy,
      struct _XCVList **list,
      struct _XCVList ***tail)
  {
      register struct _XCVList *front = *list;
  
  #ifdef XTHREADS_DEBUG
      printf("_XPopReader called in thread %x, popping %x\n",
  	   xthread_self(), front);
  #endif
  
      if (dpy->flags & XlibDisplayProcConni)
  	/* we never added ourself in the first place */
  	return;
  
      if (front) {		/* check "front" for paranoia */
  	*list = front->next;
  	if (*tail == &front->next)	/* did we free the last elt? */
  	    *tail = list;
  	if (dpy->lock->num_free_cvls < NUM_FREE_CVLS) {
  	    front->next = dpy->lock->free_cvls;
  	    dpy->lock->free_cvls = front;
  	    dpy->lock->num_free_cvls++;
  	} else {
  	    xcondition_clear(front->cv);
  	    Xfree((char *)front->cv);
  	    Xfree((char *)front);
  	}
      }
  
      /* signal new front after it is in place */
      if ((dpy->lock->reply_first = (dpy->lock->reply_awaiters != NULL))) {
  	ConditionSignal(dpy, dpy->lock->reply_awaiters->cv);
      } else if (dpy->lock->event_awaiters) {
  	ConditionSignal(dpy, dpy->lock->event_awaiters->cv);
      }
  }
  
  #if defined(XTHREADS_WARN) || defined(XTHREADS_FILE_LINE)
  static void _XConditionWait(cv, mutex,file,line)
      xcondition_t cv;
      xmutex_t mutex;
      char *file;
      int line;
  #else
  static void _XConditionWait(
      xcondition_t cv,
      xmutex_t mutex)
  #endif
  {
  #ifdef XTHREADS_WARN
      xthread_t self = xthread_self();
      char *old_file = locking_file;
      int old_line = locking_line;
      
  #ifdef XTHREADS_DEBUG
      printf("line %d thread %x in condition wait\n", line, self);
  #endif
      xthread_clear_id(locking_thread);
  
      locking_history[lock_hist_loc].file = file;
      locking_history[lock_hist_loc].line = line;
      locking_history[lock_hist_loc].thread = self;
      locking_history[lock_hist_loc].lockp = False;
      lock_hist_loc++;
      if (lock_hist_loc >= LOCK_HIST_SIZE)
  	lock_hist_loc = 0;
  #endif /* XTHREADS_WARN */
  
      xcondition_wait(cv, mutex);
  
  #ifdef XTHREADS_WARN
      locking_thread = self;
      locking_file = old_file;
      locking_line = old_line;
  
      locking_history[lock_hist_loc].file = file;
      locking_history[lock_hist_loc].line = line;
      locking_history[lock_hist_loc].thread = self;
      locking_history[lock_hist_loc].lockp = True;
      lock_hist_loc++;
      if (lock_hist_loc >= LOCK_HIST_SIZE)
  	lock_hist_loc = 0;
  #ifdef XTHREADS_DEBUG
      printf("line %d thread %x was signaled\n", line, self);
  #endif /* XTHREADS_DEBUG */
  #endif /* XTHREADS_WARN */
  }
  
  #if defined(XTHREADS_WARN) || defined(XTHREADS_FILE_LINE)
  static void _XConditionSignal(cv,file,line)
      xcondition_t cv;
      char *file;
      int line;
  #else
  static void _XConditionSignal(
      xcondition_t cv)
  #endif
  {
  #ifdef XTHREADS_WARN
  #ifdef XTHREADS_DEBUG
      printf("line %d thread %x is signalling\n", line, xthread_self());
  #endif
  #endif
      xcondition_signal(cv);
  }
      
  
  #if defined(XTHREADS_WARN) || defined(XTHREADS_FILE_LINE)
  static void _XConditionBroadcast(cv,file,line)
      xcondition_t cv;
      char *file;
      int line;
  #else
  static void _XConditionBroadcast(
      xcondition_t cv)
  #endif
  {
  #ifdef XTHREADS_WARN
  #ifdef XTHREADS_DEBUG
      printf("line %d thread %x is broadcasting\n", line, xthread_self());
  #endif
  #endif
      xcondition_broadcast(cv);
  }
      
  
  static void _XFreeDisplayLock(
      Display *dpy)
  {
      struct _XCVList *cvl;
  
      if (dpy->lock != NULL) {
  	if (dpy->lock->mutex != NULL) {
  	    xmutex_clear(dpy->lock->mutex);
  	    xmutex_free(dpy->lock->mutex);
  	}
  	if (dpy->lock->cv != NULL) {
  	    xcondition_clear(dpy->lock->cv);
  	    xcondition_free(dpy->lock->cv);
  	}
  	if (dpy->lock->writers != NULL) {
  	    xcondition_clear(dpy->lock->writers);
  	    xcondition_free(dpy->lock->writers);
  	}
  	while ((cvl = dpy->lock->free_cvls)) {
  	    dpy->lock->free_cvls = cvl->next;
  	    xcondition_clear(cvl->cv);
  	    Xfree((char *)cvl->cv);
  	    Xfree((char *)cvl);
  	}
  	Xfree((char *)dpy->lock);
  	dpy->lock = NULL;
      }
      if (dpy->lock_fns != NULL) {
  	Xfree((char *)dpy->lock_fns);
  	dpy->lock_fns = NULL;
      }
  }
  
  /*
   * wait for thread with user-level display lock to release it.
   */
  
  static void _XDisplayLockWait(
      Display *dpy)
  {
      xthread_t self;
  
      while (dpy->lock->locking_level > 0) {
  	self = xthread_self();
  	if (xthread_equal(dpy->lock->locking_thread, self))
  	    break;
  	ConditionWait(dpy, dpy->lock->cv);
      }
  }
  
  #if defined(XTHREADS_WARN) || defined(XTHREADS_FILE_LINE)
  static void _XLockDisplay(dpy, file, line)
      Display *dpy;
      char *file;			/* source file, from macro */
      int line;
  #else
  static void _XLockDisplay(
      Display *dpy)
  #endif
  {
  #ifdef XTHREADS_WARN
      _XLockDisplayWarn(dpy, file, line);
  #else
      xmutex_lock(dpy->lock->mutex);
  #endif
      if (dpy->lock->locking_level > 0)
  	_XDisplayLockWait(dpy);
  }
  
  /*
   * _XReply is allowed to exit from select/poll and clean up even if a
   * user-level lock is in force, so it uses this instead of _XFancyLockDisplay.
   */
  #if defined(XTHREADS_WARN) || defined(XTHREADS_FILE_LINE)
  static void _XInternalLockDisplay(dpy, wskip, file, line)
      Display *dpy;
      Bool wskip;
      char *file;			/* source file, from macro */
      int line;
  #else
  static void _XInternalLockDisplay(
      Display *dpy,
      Bool wskip)
  #endif
  {
  #ifdef XTHREADS_WARN
      _XLockDisplayWarn(dpy, file, line);
  #else
      xmutex_lock(dpy->lock->mutex);
  #endif
      if (!wskip && dpy->lock->locking_level > 0)
  	_XDisplayLockWait(dpy);
  }
  
  static void _XUserLockDisplay(
      register Display* dpy)
  {
      if (++dpy->lock->locking_level == 1) {
  	dpy->lock->lock_wait = _XDisplayLockWait;
  	dpy->lock->locking_thread = xthread_self();
      }
  }
  
  static
  void _XUserUnlockDisplay(
      register Display* dpy)
  {
      if (dpy->lock->locking_level > 0 && --dpy->lock->locking_level == 0) {
  	/* signal other threads that might be waiting in XLockDisplay */
  	ConditionBroadcast(dpy, dpy->lock->cv);
  	dpy->lock->lock_wait = NULL;
  	xthread_clear_id(dpy->lock->locking_thread);
      }
  }
  
  /* returns 0 if initialized ok, -1 if unable to allocate
     a mutex or other memory */
  
  static int _XInitDisplayLock(
      Display *dpy)
  {
      dpy->lock_fns = (struct _XLockPtrs*)Xmalloc(sizeof(struct _XLockPtrs));
      if (dpy->lock_fns == NULL)
  	return -1;
      dpy->lock = (struct _XLockInfo *)Xmalloc(sizeof(struct _XLockInfo));
      if (dpy->lock == NULL) {
  	_XFreeDisplayLock(dpy);
  	return -1;
      }
      dpy->lock->cv = xcondition_malloc();
      dpy->lock->mutex = xmutex_malloc();
      dpy->lock->writers = xcondition_malloc();
      if (!dpy->lock->cv || !dpy->lock->mutex || !dpy->lock->writers) {
  	_XFreeDisplayLock(dpy);
  	return -1;
      }
  
      dpy->lock->reply_bytes_left = 0;
      dpy->lock->reply_was_read = False;
      dpy->lock->reply_awaiters = NULL;
      dpy->lock->reply_awaiters_tail = &dpy->lock->reply_awaiters;
      dpy->lock->event_awaiters = NULL;
      dpy->lock->event_awaiters_tail = &dpy->lock->event_awaiters;
      dpy->lock->reply_first = False;
      dpy->lock->locking_level = 0;
      dpy->lock->num_free_cvls = 0;
      dpy->lock->free_cvls = NULL;
      xthread_clear_id(dpy->lock->locking_thread);
      xthread_clear_id(dpy->lock->reading_thread);
      xthread_clear_id(dpy->lock->conni_thread);
      xmutex_init(dpy->lock->mutex);
      xmutex_set_name(dpy->lock->mutex, "Xlib Display");
      xcondition_init(dpy->lock->cv);
      xcondition_set_name(dpy->lock->cv, "XLockDisplay");
      xcondition_init(dpy->lock->writers);
      xcondition_set_name(dpy->lock->writers, "Xlib wait for writable");
      dpy->lock_fns->lock_display = _XLockDisplay;
      dpy->lock->internal_lock_display = _XInternalLockDisplay;
      dpy->lock_fns->unlock_display = _XUnlockDisplay;
      dpy->lock->user_lock_display = _XUserLockDisplay;
      dpy->lock->user_unlock_display = _XUserUnlockDisplay;
      dpy->lock->pop_reader = _XPopReader;
      dpy->lock->push_reader = _XPushReader;
      dpy->lock->condition_wait = _XConditionWait;
      dpy->lock->condition_signal = _XConditionSignal;
      dpy->lock->condition_broadcast = _XConditionBroadcast;
      dpy->lock->create_cvl = _XCreateCVL;
      dpy->lock->lock_wait = NULL; /* filled in by XLockDisplay() */
  
      return 0;
  }
  
  
  Status XInitThreads()
  {
      if (_Xglobal_lock)
  	return 1;
  #ifdef xthread_init
      xthread_init();		/* return value? */
  #endif
      if (!(global_lock.lock = xmutex_malloc()))
  	return 0;
      if (!(i18n_lock.lock = xmutex_malloc())) {
  	xmutex_free(global_lock.lock);
  	global_lock.lock = NULL;
  	return 0;
      }
      _Xglobal_lock = &global_lock;
      xmutex_init(_Xglobal_lock->lock);
      xmutex_set_name(_Xglobal_lock->lock, "Xlib global");
      _Xi18n_lock = &i18n_lock;
      xmutex_init(_Xi18n_lock->lock);
      xmutex_set_name(_Xi18n_lock->lock, "Xlib i18n");
      _XLockMutex_fn = _XLockMutex;
      _XUnlockMutex_fn = _XUnlockMutex;
      _XCreateMutex_fn = _XCreateMutex;
      _XFreeMutex_fn = _XFreeMutex;
      _XInitDisplayLock_fn = _XInitDisplayLock;
      _XFreeDisplayLock_fn = _XFreeDisplayLock;
      _Xthread_self_fn = _Xthread_self;
  
  #ifdef XTHREADS_WARN
  #ifdef XTHREADS_DEBUG
      setlinebuf(stdout);		/* for debugging messages */
  #endif
  #endif
  
      return 1;
  }
  
  #else /* XTHREADS */
  Status XInitThreads()
  {
      return 0;
  }
  #endif /* XTHREADS */
  