Blame lib/glthread/lock.c

Packit 8f70b4
/* Locking in multithreaded situations.
Packit 8f70b4
   Copyright (C) 2005-2018 Free Software Foundation, Inc.
Packit 8f70b4
Packit 8f70b4
   This program is free software; you can redistribute it and/or modify
Packit 8f70b4
   it under the terms of the GNU General Public License as published by
Packit 8f70b4
   the Free Software Foundation; either version 3, or (at your option)
Packit 8f70b4
   any later version.
Packit 8f70b4
Packit 8f70b4
   This program is distributed in the hope that it will be useful,
Packit 8f70b4
   but WITHOUT ANY WARRANTY; without even the implied warranty of
Packit 8f70b4
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
Packit 8f70b4
   GNU General Public License for more details.
Packit 8f70b4
Packit 8f70b4
   You should have received a copy of the GNU General Public License
Packit 8f70b4
   along with this program; if not, see <https://www.gnu.org/licenses/>.  */
Packit 8f70b4
Packit 8f70b4
/* Written by Bruno Haible <bruno@clisp.org>, 2005.
Packit 8f70b4
   Based on GCC's gthr-posix.h, gthr-posix95.h, gthr-solaris.h,
Packit 8f70b4
   gthr-win32.h.  */
Packit 8f70b4
Packit 8f70b4
#include <config.h>
Packit 8f70b4
Packit 8f70b4
#include "glthread/lock.h"
Packit 8f70b4
Packit 8f70b4
/* ========================================================================= */
Packit 8f70b4
Packit 8f70b4
#if USE_POSIX_THREADS
Packit 8f70b4
Packit 8f70b4
/* -------------------------- gl_lock_t datatype -------------------------- */
Packit 8f70b4
Packit 8f70b4
/* ------------------------- gl_rwlock_t datatype ------------------------- */
Packit 8f70b4
Packit 8f70b4
# if HAVE_PTHREAD_RWLOCK && (HAVE_PTHREAD_RWLOCK_RDLOCK_PREFER_WRITER || (defined PTHREAD_RWLOCK_WRITER_NONRECURSIVE_INITIALIZER_NP && (__GNU_LIBRARY__ > 1)))
Packit 8f70b4
Packit 8f70b4
#  ifdef PTHREAD_RWLOCK_INITIALIZER
Packit 8f70b4
Packit 8f70b4
#   if !HAVE_PTHREAD_RWLOCK_RDLOCK_PREFER_WRITER
Packit 8f70b4
     /* glibc with bug https://sourceware.org/bugzilla/show_bug.cgi?id=13701 */
Packit 8f70b4
Packit 8f70b4
int
Packit 8f70b4
glthread_rwlock_init_for_glibc (pthread_rwlock_t *lock)
Packit 8f70b4
{
Packit 8f70b4
  pthread_rwlockattr_t attributes;
Packit 8f70b4
  int err;
Packit 8f70b4
Packit 8f70b4
  err = pthread_rwlockattr_init (&attributes);
Packit 8f70b4
  if (err != 0)
Packit 8f70b4
    return err;
Packit 8f70b4
  /* Note: PTHREAD_RWLOCK_PREFER_WRITER_NONRECURSIVE_NP is the only value that
Packit 8f70b4
     causes the writer to be preferred. PTHREAD_RWLOCK_PREFER_WRITER_NP does not
Packit 8f70b4
     do this; see
Packit 8f70b4
     http://man7.org/linux/man-pages/man3/pthread_rwlockattr_setkind_np.3.html */
Packit 8f70b4
  err = pthread_rwlockattr_setkind_np (&attributes,
Packit 8f70b4
                                       PTHREAD_RWLOCK_PREFER_WRITER_NONRECURSIVE_NP);
Packit 8f70b4
  if (err == 0)
Packit 8f70b4
    err = pthread_rwlock_init(lock, &attributes);
Packit 8f70b4
  /* pthread_rwlockattr_destroy always returns 0.  It cannot influence the
Packit 8f70b4
     return value.  */
Packit 8f70b4
  pthread_rwlockattr_destroy (&attributes);
Packit 8f70b4
  return err;
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
#   endif
Packit 8f70b4
#  else
Packit 8f70b4
Packit 8f70b4
int
Packit 8f70b4
glthread_rwlock_init_multithreaded (gl_rwlock_t *lock)
Packit 8f70b4
{
Packit 8f70b4
  int err;
Packit 8f70b4
Packit 8f70b4
  err = pthread_rwlock_init (&lock->rwlock, NULL);
Packit 8f70b4
  if (err != 0)
Packit 8f70b4
    return err;
Packit 8f70b4
  lock->initialized = 1;
Packit 8f70b4
  return 0;
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
int
Packit 8f70b4
glthread_rwlock_rdlock_multithreaded (gl_rwlock_t *lock)
Packit 8f70b4
{
Packit 8f70b4
  if (!lock->initialized)
Packit 8f70b4
    {
Packit 8f70b4
      int err;
Packit 8f70b4
Packit 8f70b4
      err = pthread_mutex_lock (&lock->guard);
Packit 8f70b4
      if (err != 0)
Packit 8f70b4
        return err;
Packit 8f70b4
      if (!lock->initialized)
Packit 8f70b4
        {
Packit 8f70b4
          err = glthread_rwlock_init_multithreaded (lock);
Packit 8f70b4
          if (err != 0)
Packit 8f70b4
            {
Packit 8f70b4
              pthread_mutex_unlock (&lock->guard);
Packit 8f70b4
              return err;
Packit 8f70b4
            }
Packit 8f70b4
        }
Packit 8f70b4
      err = pthread_mutex_unlock (&lock->guard);
Packit 8f70b4
      if (err != 0)
Packit 8f70b4
        return err;
Packit 8f70b4
    }
Packit 8f70b4
  return pthread_rwlock_rdlock (&lock->rwlock);
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
int
Packit 8f70b4
glthread_rwlock_wrlock_multithreaded (gl_rwlock_t *lock)
Packit 8f70b4
{
Packit 8f70b4
  if (!lock->initialized)
Packit 8f70b4
    {
Packit 8f70b4
      int err;
Packit 8f70b4
Packit 8f70b4
      err = pthread_mutex_lock (&lock->guard);
Packit 8f70b4
      if (err != 0)
Packit 8f70b4
        return err;
Packit 8f70b4
      if (!lock->initialized)
Packit 8f70b4
        {
Packit 8f70b4
          err = glthread_rwlock_init_multithreaded (lock);
Packit 8f70b4
          if (err != 0)
Packit 8f70b4
            {
Packit 8f70b4
              pthread_mutex_unlock (&lock->guard);
Packit 8f70b4
              return err;
Packit 8f70b4
            }
Packit 8f70b4
        }
Packit 8f70b4
      err = pthread_mutex_unlock (&lock->guard);
Packit 8f70b4
      if (err != 0)
Packit 8f70b4
        return err;
Packit 8f70b4
    }
Packit 8f70b4
  return pthread_rwlock_wrlock (&lock->rwlock);
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
int
Packit 8f70b4
glthread_rwlock_unlock_multithreaded (gl_rwlock_t *lock)
Packit 8f70b4
{
Packit 8f70b4
  if (!lock->initialized)
Packit 8f70b4
    return EINVAL;
Packit 8f70b4
  return pthread_rwlock_unlock (&lock->rwlock);
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
int
Packit 8f70b4
glthread_rwlock_destroy_multithreaded (gl_rwlock_t *lock)
Packit 8f70b4
{
Packit 8f70b4
  int err;
Packit 8f70b4
Packit 8f70b4
  if (!lock->initialized)
Packit 8f70b4
    return EINVAL;
Packit 8f70b4
  err = pthread_rwlock_destroy (&lock->rwlock);
Packit 8f70b4
  if (err != 0)
Packit 8f70b4
    return err;
Packit 8f70b4
  lock->initialized = 0;
Packit 8f70b4
  return 0;
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
#  endif
Packit 8f70b4
Packit 8f70b4
# else
Packit 8f70b4
Packit 8f70b4
int
Packit 8f70b4
glthread_rwlock_init_multithreaded (gl_rwlock_t *lock)
Packit 8f70b4
{
Packit 8f70b4
  int err;
Packit 8f70b4
Packit 8f70b4
  err = pthread_mutex_init (&lock->lock, NULL);
Packit 8f70b4
  if (err != 0)
Packit 8f70b4
    return err;
Packit 8f70b4
  err = pthread_cond_init (&lock->waiting_readers, NULL);
Packit 8f70b4
  if (err != 0)
Packit 8f70b4
    return err;
Packit 8f70b4
  err = pthread_cond_init (&lock->waiting_writers, NULL);
Packit 8f70b4
  if (err != 0)
Packit 8f70b4
    return err;
Packit 8f70b4
  lock->waiting_writers_count = 0;
Packit 8f70b4
  lock->runcount = 0;
Packit 8f70b4
  return 0;
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
int
Packit 8f70b4
glthread_rwlock_rdlock_multithreaded (gl_rwlock_t *lock)
Packit 8f70b4
{
Packit 8f70b4
  int err;
Packit 8f70b4
Packit 8f70b4
  err = pthread_mutex_lock (&lock->lock);
Packit 8f70b4
  if (err != 0)
Packit 8f70b4
    return err;
Packit 8f70b4
  /* Test whether only readers are currently running, and whether the runcount
Packit 8f70b4
     field will not overflow, and whether no writer is waiting.  The latter
Packit 8f70b4
     condition is because POSIX recommends that "write locks shall take
Packit 8f70b4
     precedence over read locks", to avoid "writer starvation".  */
Packit 8f70b4
  while (!(lock->runcount + 1 > 0 && lock->waiting_writers_count == 0))
Packit 8f70b4
    {
Packit 8f70b4
      /* This thread has to wait for a while.  Enqueue it among the
Packit 8f70b4
         waiting_readers.  */
Packit 8f70b4
      err = pthread_cond_wait (&lock->waiting_readers, &lock->lock);
Packit 8f70b4
      if (err != 0)
Packit 8f70b4
        {
Packit 8f70b4
          pthread_mutex_unlock (&lock->lock);
Packit 8f70b4
          return err;
Packit 8f70b4
        }
Packit 8f70b4
    }
Packit 8f70b4
  lock->runcount++;
Packit 8f70b4
  return pthread_mutex_unlock (&lock->lock);
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
int
Packit 8f70b4
glthread_rwlock_wrlock_multithreaded (gl_rwlock_t *lock)
Packit 8f70b4
{
Packit 8f70b4
  int err;
Packit 8f70b4
Packit 8f70b4
  err = pthread_mutex_lock (&lock->lock);
Packit 8f70b4
  if (err != 0)
Packit 8f70b4
    return err;
Packit 8f70b4
  /* Test whether no readers or writers are currently running.  */
Packit 8f70b4
  while (!(lock->runcount == 0))
Packit 8f70b4
    {
Packit 8f70b4
      /* This thread has to wait for a while.  Enqueue it among the
Packit 8f70b4
         waiting_writers.  */
Packit 8f70b4
      lock->waiting_writers_count++;
Packit 8f70b4
      err = pthread_cond_wait (&lock->waiting_writers, &lock->lock);
Packit 8f70b4
      if (err != 0)
Packit 8f70b4
        {
Packit 8f70b4
          lock->waiting_writers_count--;
Packit 8f70b4
          pthread_mutex_unlock (&lock->lock);
Packit 8f70b4
          return err;
Packit 8f70b4
        }
Packit 8f70b4
      lock->waiting_writers_count--;
Packit 8f70b4
    }
Packit 8f70b4
  lock->runcount--; /* runcount becomes -1 */
Packit 8f70b4
  return pthread_mutex_unlock (&lock->lock);
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
int
Packit 8f70b4
glthread_rwlock_unlock_multithreaded (gl_rwlock_t *lock)
Packit 8f70b4
{
Packit 8f70b4
  int err;
Packit 8f70b4
Packit 8f70b4
  err = pthread_mutex_lock (&lock->lock);
Packit 8f70b4
  if (err != 0)
Packit 8f70b4
    return err;
Packit 8f70b4
  if (lock->runcount < 0)
Packit 8f70b4
    {
Packit 8f70b4
      /* Drop a writer lock.  */
Packit 8f70b4
      if (!(lock->runcount == -1))
Packit 8f70b4
        {
Packit 8f70b4
          pthread_mutex_unlock (&lock->lock);
Packit 8f70b4
          return EINVAL;
Packit 8f70b4
        }
Packit 8f70b4
      lock->runcount = 0;
Packit 8f70b4
    }
Packit 8f70b4
  else
Packit 8f70b4
    {
Packit 8f70b4
      /* Drop a reader lock.  */
Packit 8f70b4
      if (!(lock->runcount > 0))
Packit 8f70b4
        {
Packit 8f70b4
          pthread_mutex_unlock (&lock->lock);
Packit 8f70b4
          return EINVAL;
Packit 8f70b4
        }
Packit 8f70b4
      lock->runcount--;
Packit 8f70b4
    }
Packit 8f70b4
  if (lock->runcount == 0)
Packit 8f70b4
    {
Packit 8f70b4
      /* POSIX recommends that "write locks shall take precedence over read
Packit 8f70b4
         locks", to avoid "writer starvation".  */
Packit 8f70b4
      if (lock->waiting_writers_count > 0)
Packit 8f70b4
        {
Packit 8f70b4
          /* Wake up one of the waiting writers.  */
Packit 8f70b4
          err = pthread_cond_signal (&lock->waiting_writers);
Packit 8f70b4
          if (err != 0)
Packit 8f70b4
            {
Packit 8f70b4
              pthread_mutex_unlock (&lock->lock);
Packit 8f70b4
              return err;
Packit 8f70b4
            }
Packit 8f70b4
        }
Packit 8f70b4
      else
Packit 8f70b4
        {
Packit 8f70b4
          /* Wake up all waiting readers.  */
Packit 8f70b4
          err = pthread_cond_broadcast (&lock->waiting_readers);
Packit 8f70b4
          if (err != 0)
Packit 8f70b4
            {
Packit 8f70b4
              pthread_mutex_unlock (&lock->lock);
Packit 8f70b4
              return err;
Packit 8f70b4
            }
Packit 8f70b4
        }
Packit 8f70b4
    }
Packit 8f70b4
  return pthread_mutex_unlock (&lock->lock);
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
int
Packit 8f70b4
glthread_rwlock_destroy_multithreaded (gl_rwlock_t *lock)
Packit 8f70b4
{
Packit 8f70b4
  int err;
Packit 8f70b4
Packit 8f70b4
  err = pthread_mutex_destroy (&lock->lock);
Packit 8f70b4
  if (err != 0)
Packit 8f70b4
    return err;
Packit 8f70b4
  err = pthread_cond_destroy (&lock->waiting_readers);
Packit 8f70b4
  if (err != 0)
Packit 8f70b4
    return err;
Packit 8f70b4
  err = pthread_cond_destroy (&lock->waiting_writers);
Packit 8f70b4
  if (err != 0)
Packit 8f70b4
    return err;
Packit 8f70b4
  return 0;
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
# endif
Packit 8f70b4
Packit 8f70b4
/* --------------------- gl_recursive_lock_t datatype --------------------- */
Packit 8f70b4
Packit 8f70b4
# if HAVE_PTHREAD_MUTEX_RECURSIVE
Packit 8f70b4
Packit 8f70b4
#  if defined PTHREAD_RECURSIVE_MUTEX_INITIALIZER || defined PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP
Packit 8f70b4
Packit 8f70b4
int
Packit 8f70b4
glthread_recursive_lock_init_multithreaded (gl_recursive_lock_t *lock)
Packit 8f70b4
{
Packit 8f70b4
  pthread_mutexattr_t attributes;
Packit 8f70b4
  int err;
Packit 8f70b4
Packit 8f70b4
  err = pthread_mutexattr_init (&attributes);
Packit 8f70b4
  if (err != 0)
Packit 8f70b4
    return err;
Packit 8f70b4
  err = pthread_mutexattr_settype (&attributes, PTHREAD_MUTEX_RECURSIVE);
Packit 8f70b4
  if (err != 0)
Packit 8f70b4
    {
Packit 8f70b4
      pthread_mutexattr_destroy (&attributes);
Packit 8f70b4
      return err;
Packit 8f70b4
    }
Packit 8f70b4
  err = pthread_mutex_init (lock, &attributes);
Packit 8f70b4
  if (err != 0)
Packit 8f70b4
    {
Packit 8f70b4
      pthread_mutexattr_destroy (&attributes);
Packit 8f70b4
      return err;
Packit 8f70b4
    }
Packit 8f70b4
  err = pthread_mutexattr_destroy (&attributes);
Packit 8f70b4
  if (err != 0)
Packit 8f70b4
    return err;
Packit 8f70b4
  return 0;
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
#  else
Packit 8f70b4
Packit 8f70b4
int
Packit 8f70b4
glthread_recursive_lock_init_multithreaded (gl_recursive_lock_t *lock)
Packit 8f70b4
{
Packit 8f70b4
  pthread_mutexattr_t attributes;
Packit 8f70b4
  int err;
Packit 8f70b4
Packit 8f70b4
  err = pthread_mutexattr_init (&attributes);
Packit 8f70b4
  if (err != 0)
Packit 8f70b4
    return err;
Packit 8f70b4
  err = pthread_mutexattr_settype (&attributes, PTHREAD_MUTEX_RECURSIVE);
Packit 8f70b4
  if (err != 0)
Packit 8f70b4
    {
Packit 8f70b4
      pthread_mutexattr_destroy (&attributes);
Packit 8f70b4
      return err;
Packit 8f70b4
    }
Packit 8f70b4
  err = pthread_mutex_init (&lock->recmutex, &attributes);
Packit 8f70b4
  if (err != 0)
Packit 8f70b4
    {
Packit 8f70b4
      pthread_mutexattr_destroy (&attributes);
Packit 8f70b4
      return err;
Packit 8f70b4
    }
Packit 8f70b4
  err = pthread_mutexattr_destroy (&attributes);
Packit 8f70b4
  if (err != 0)
Packit 8f70b4
    return err;
Packit 8f70b4
  lock->initialized = 1;
Packit 8f70b4
  return 0;
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
int
Packit 8f70b4
glthread_recursive_lock_lock_multithreaded (gl_recursive_lock_t *lock)
Packit 8f70b4
{
Packit 8f70b4
  if (!lock->initialized)
Packit 8f70b4
    {
Packit 8f70b4
      int err;
Packit 8f70b4
Packit 8f70b4
      err = pthread_mutex_lock (&lock->guard);
Packit 8f70b4
      if (err != 0)
Packit 8f70b4
        return err;
Packit 8f70b4
      if (!lock->initialized)
Packit 8f70b4
        {
Packit 8f70b4
          err = glthread_recursive_lock_init_multithreaded (lock);
Packit 8f70b4
          if (err != 0)
Packit 8f70b4
            {
Packit 8f70b4
              pthread_mutex_unlock (&lock->guard);
Packit 8f70b4
              return err;
Packit 8f70b4
            }
Packit 8f70b4
        }
Packit 8f70b4
      err = pthread_mutex_unlock (&lock->guard);
Packit 8f70b4
      if (err != 0)
Packit 8f70b4
        return err;
Packit 8f70b4
    }
Packit 8f70b4
  return pthread_mutex_lock (&lock->recmutex);
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
int
Packit 8f70b4
glthread_recursive_lock_unlock_multithreaded (gl_recursive_lock_t *lock)
Packit 8f70b4
{
Packit 8f70b4
  if (!lock->initialized)
Packit 8f70b4
    return EINVAL;
Packit 8f70b4
  return pthread_mutex_unlock (&lock->recmutex);
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
int
Packit 8f70b4
glthread_recursive_lock_destroy_multithreaded (gl_recursive_lock_t *lock)
Packit 8f70b4
{
Packit 8f70b4
  int err;
Packit 8f70b4
Packit 8f70b4
  if (!lock->initialized)
Packit 8f70b4
    return EINVAL;
Packit 8f70b4
  err = pthread_mutex_destroy (&lock->recmutex);
Packit 8f70b4
  if (err != 0)
Packit 8f70b4
    return err;
Packit 8f70b4
  lock->initialized = 0;
Packit 8f70b4
  return 0;
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
#  endif
Packit 8f70b4
Packit 8f70b4
# else
Packit 8f70b4
Packit 8f70b4
int
Packit 8f70b4
glthread_recursive_lock_init_multithreaded (gl_recursive_lock_t *lock)
Packit 8f70b4
{
Packit 8f70b4
  int err;
Packit 8f70b4
Packit 8f70b4
  err = pthread_mutex_init (&lock->mutex, NULL);
Packit 8f70b4
  if (err != 0)
Packit 8f70b4
    return err;
Packit 8f70b4
  lock->owner = (pthread_t) 0;
Packit 8f70b4
  lock->depth = 0;
Packit 8f70b4
  return 0;
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
int
Packit 8f70b4
glthread_recursive_lock_lock_multithreaded (gl_recursive_lock_t *lock)
Packit 8f70b4
{
Packit 8f70b4
  pthread_t self = pthread_self ();
Packit 8f70b4
  if (lock->owner != self)
Packit 8f70b4
    {
Packit 8f70b4
      int err;
Packit 8f70b4
Packit 8f70b4
      err = pthread_mutex_lock (&lock->mutex);
Packit 8f70b4
      if (err != 0)
Packit 8f70b4
        return err;
Packit 8f70b4
      lock->owner = self;
Packit 8f70b4
    }
Packit 8f70b4
  if (++(lock->depth) == 0) /* wraparound? */
Packit 8f70b4
    {
Packit 8f70b4
      lock->depth--;
Packit 8f70b4
      return EAGAIN;
Packit 8f70b4
    }
Packit 8f70b4
  return 0;
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
int
Packit 8f70b4
glthread_recursive_lock_unlock_multithreaded (gl_recursive_lock_t *lock)
Packit 8f70b4
{
Packit 8f70b4
  if (lock->owner != pthread_self ())
Packit 8f70b4
    return EPERM;
Packit 8f70b4
  if (lock->depth == 0)
Packit 8f70b4
    return EINVAL;
Packit 8f70b4
  if (--(lock->depth) == 0)
Packit 8f70b4
    {
Packit 8f70b4
      lock->owner = (pthread_t) 0;
Packit 8f70b4
      return pthread_mutex_unlock (&lock->mutex);
Packit 8f70b4
    }
Packit 8f70b4
  else
Packit 8f70b4
    return 0;
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
int
Packit 8f70b4
glthread_recursive_lock_destroy_multithreaded (gl_recursive_lock_t *lock)
Packit 8f70b4
{
Packit 8f70b4
  if (lock->owner != (pthread_t) 0)
Packit 8f70b4
    return EBUSY;
Packit 8f70b4
  return pthread_mutex_destroy (&lock->mutex);
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
# endif
Packit 8f70b4
Packit 8f70b4
/* -------------------------- gl_once_t datatype -------------------------- */
Packit 8f70b4
Packit 8f70b4
static const pthread_once_t fresh_once = PTHREAD_ONCE_INIT;
Packit 8f70b4
Packit 8f70b4
int
Packit 8f70b4
glthread_once_singlethreaded (pthread_once_t *once_control)
Packit 8f70b4
{
Packit 8f70b4
  /* We don't know whether pthread_once_t is an integer type, a floating-point
Packit 8f70b4
     type, a pointer type, or a structure type.  */
Packit 8f70b4
  char *firstbyte = (char *)once_control;
Packit 8f70b4
  if (*firstbyte == *(const char *)&fresh_once)
Packit 8f70b4
    {
Packit 8f70b4
      /* First time use of once_control.  Invert the first byte.  */
Packit 8f70b4
      *firstbyte = ~ *(const char *)&fresh_once;
Packit 8f70b4
      return 1;
Packit 8f70b4
    }
Packit 8f70b4
  else
Packit 8f70b4
    return 0;
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
#endif
Packit 8f70b4
Packit 8f70b4
/* ========================================================================= */
Packit 8f70b4
Packit 8f70b4
#if USE_PTH_THREADS
Packit 8f70b4
Packit 8f70b4
/* Use the GNU Pth threads library.  */
Packit 8f70b4
Packit 8f70b4
/* -------------------------- gl_lock_t datatype -------------------------- */
Packit 8f70b4
Packit 8f70b4
/* ------------------------- gl_rwlock_t datatype ------------------------- */
Packit 8f70b4
Packit 8f70b4
# if !HAVE_PTH_RWLOCK_ACQUIRE_PREFER_WRITER
Packit 8f70b4
Packit 8f70b4
int
Packit 8f70b4
glthread_rwlock_init_multithreaded (gl_rwlock_t *lock)
Packit 8f70b4
{
Packit 8f70b4
  if (!pth_mutex_init (&lock->lock))
Packit 8f70b4
    return errno;
Packit 8f70b4
  if (!pth_cond_init (&lock->waiting_readers))
Packit 8f70b4
    return errno;
Packit 8f70b4
  if (!pth_cond_init (&lock->waiting_writers))
Packit 8f70b4
    return errno;
Packit 8f70b4
  lock->waiting_writers_count = 0;
Packit 8f70b4
  lock->runcount = 0;
Packit 8f70b4
  lock->initialized = 1;
Packit 8f70b4
  return 0;
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
int
Packit 8f70b4
glthread_rwlock_rdlock_multithreaded (gl_rwlock_t *lock)
Packit 8f70b4
{
Packit 8f70b4
  if (!lock->initialized)
Packit 8f70b4
    glthread_rwlock_init_multithreaded (lock);
Packit 8f70b4
  if (!pth_mutex_acquire (&lock->lock, 0, NULL))
Packit 8f70b4
    return errno;
Packit 8f70b4
  /* Test whether only readers are currently running, and whether the runcount
Packit 8f70b4
     field will not overflow, and whether no writer is waiting.  The latter
Packit 8f70b4
     condition is because POSIX recommends that "write locks shall take
Packit 8f70b4
     precedence over read locks", to avoid "writer starvation".  */
Packit 8f70b4
  while (!(lock->runcount + 1 > 0 && lock->waiting_writers_count == 0))
Packit 8f70b4
    {
Packit 8f70b4
      /* This thread has to wait for a while.  Enqueue it among the
Packit 8f70b4
         waiting_readers.  */
Packit 8f70b4
      if (!pth_cond_await (&lock->waiting_readers, &lock->lock, NULL))
Packit 8f70b4
        {
Packit 8f70b4
          int err = errno;
Packit 8f70b4
          pth_mutex_release (&lock->lock);
Packit 8f70b4
          return err;
Packit 8f70b4
        }
Packit 8f70b4
    }
Packit 8f70b4
  lock->runcount++;
Packit 8f70b4
  return (!pth_mutex_release (&lock->lock) ? errno : 0);
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
int
Packit 8f70b4
glthread_rwlock_wrlock_multithreaded (gl_rwlock_t *lock)
Packit 8f70b4
{
Packit 8f70b4
  if (!lock->initialized)
Packit 8f70b4
    glthread_rwlock_init_multithreaded (lock);
Packit 8f70b4
  if (!pth_mutex_acquire (&lock->lock, 0, NULL))
Packit 8f70b4
    return errno;
Packit 8f70b4
  /* Test whether no readers or writers are currently running.  */
Packit 8f70b4
  while (!(lock->runcount == 0))
Packit 8f70b4
    {
Packit 8f70b4
      /* This thread has to wait for a while.  Enqueue it among the
Packit 8f70b4
         waiting_writers.  */
Packit 8f70b4
      lock->waiting_writers_count++;
Packit 8f70b4
      if (!pth_cond_await (&lock->waiting_writers, &lock->lock, NULL))
Packit 8f70b4
        {
Packit 8f70b4
          int err = errno;
Packit 8f70b4
          lock->waiting_writers_count--;
Packit 8f70b4
          pth_mutex_release (&lock->lock);
Packit 8f70b4
          return err;
Packit 8f70b4
        }
Packit 8f70b4
      lock->waiting_writers_count--;
Packit 8f70b4
    }
Packit 8f70b4
  lock->runcount--; /* runcount becomes -1 */
Packit 8f70b4
  return (!pth_mutex_release (&lock->lock) ? errno : 0);
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
int
Packit 8f70b4
glthread_rwlock_unlock_multithreaded (gl_rwlock_t *lock)
Packit 8f70b4
{
Packit 8f70b4
  int err;
Packit 8f70b4
Packit 8f70b4
  if (!lock->initialized)
Packit 8f70b4
    return EINVAL;
Packit 8f70b4
  if (!pth_mutex_acquire (&lock->lock, 0, NULL))
Packit 8f70b4
    return errno;
Packit 8f70b4
  if (lock->runcount < 0)
Packit 8f70b4
    {
Packit 8f70b4
      /* Drop a writer lock.  */
Packit 8f70b4
      if (!(lock->runcount == -1))
Packit 8f70b4
        {
Packit 8f70b4
          pth_mutex_release (&lock->lock);
Packit 8f70b4
          return EINVAL;
Packit 8f70b4
        }
Packit 8f70b4
      lock->runcount = 0;
Packit 8f70b4
    }
Packit 8f70b4
  else
Packit 8f70b4
    {
Packit 8f70b4
      /* Drop a reader lock.  */
Packit 8f70b4
      if (!(lock->runcount > 0))
Packit 8f70b4
        {
Packit 8f70b4
          pth_mutex_release (&lock->lock);
Packit 8f70b4
          return EINVAL;
Packit 8f70b4
        }
Packit 8f70b4
      lock->runcount--;
Packit 8f70b4
    }
Packit 8f70b4
  if (lock->runcount == 0)
Packit 8f70b4
    {
Packit 8f70b4
      /* POSIX recommends that "write locks shall take precedence over read
Packit 8f70b4
         locks", to avoid "writer starvation".  */
Packit 8f70b4
      if (lock->waiting_writers_count > 0)
Packit 8f70b4
        {
Packit 8f70b4
          /* Wake up one of the waiting writers.  */
Packit 8f70b4
          if (!pth_cond_notify (&lock->waiting_writers, FALSE))
Packit 8f70b4
            {
Packit 8f70b4
              int err = errno;
Packit 8f70b4
              pth_mutex_release (&lock->lock);
Packit 8f70b4
              return err;
Packit 8f70b4
            }
Packit 8f70b4
        }
Packit 8f70b4
      else
Packit 8f70b4
        {
Packit 8f70b4
          /* Wake up all waiting readers.  */
Packit 8f70b4
          if (!pth_cond_notify (&lock->waiting_readers, TRUE))
Packit 8f70b4
            {
Packit 8f70b4
              int err = errno;
Packit 8f70b4
              pth_mutex_release (&lock->lock);
Packit 8f70b4
              return err;
Packit 8f70b4
            }
Packit 8f70b4
        }
Packit 8f70b4
    }
Packit 8f70b4
  return (!pth_mutex_release (&lock->lock) ? errno : 0);
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
int
Packit 8f70b4
glthread_rwlock_destroy_multithreaded (gl_rwlock_t *lock)
Packit 8f70b4
{
Packit 8f70b4
  lock->initialized = 0;
Packit 8f70b4
  return 0;
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
# endif
Packit 8f70b4
Packit 8f70b4
/* --------------------- gl_recursive_lock_t datatype --------------------- */
Packit 8f70b4
Packit 8f70b4
/* -------------------------- gl_once_t datatype -------------------------- */
Packit 8f70b4
Packit 8f70b4
static void
Packit 8f70b4
glthread_once_call (void *arg)
Packit 8f70b4
{
Packit 8f70b4
  void (**gl_once_temp_addr) (void) = (void (**) (void)) arg;
Packit 8f70b4
  void (*initfunction) (void) = *gl_once_temp_addr;
Packit 8f70b4
  initfunction ();
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
int
Packit 8f70b4
glthread_once_multithreaded (pth_once_t *once_control, void (*initfunction) (void))
Packit 8f70b4
{
Packit 8f70b4
  void (*temp) (void) = initfunction;
Packit 8f70b4
  return (!pth_once (once_control, glthread_once_call, &temp) ? errno : 0);
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
int
Packit 8f70b4
glthread_once_singlethreaded (pth_once_t *once_control)
Packit 8f70b4
{
Packit 8f70b4
  /* We know that pth_once_t is an integer type.  */
Packit 8f70b4
  if (*once_control == PTH_ONCE_INIT)
Packit 8f70b4
    {
Packit 8f70b4
      /* First time use of once_control.  Invert the marker.  */
Packit 8f70b4
      *once_control = ~ PTH_ONCE_INIT;
Packit 8f70b4
      return 1;
Packit 8f70b4
    }
Packit 8f70b4
  else
Packit 8f70b4
    return 0;
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
#endif
Packit 8f70b4
Packit 8f70b4
/* ========================================================================= */
Packit 8f70b4
Packit 8f70b4
#if USE_SOLARIS_THREADS
Packit 8f70b4
Packit 8f70b4
/* Use the old Solaris threads library.  */
Packit 8f70b4
Packit 8f70b4
/* -------------------------- gl_lock_t datatype -------------------------- */
Packit 8f70b4
Packit 8f70b4
/* ------------------------- gl_rwlock_t datatype ------------------------- */
Packit 8f70b4
Packit 8f70b4
/* --------------------- gl_recursive_lock_t datatype --------------------- */
Packit 8f70b4
Packit 8f70b4
int
Packit 8f70b4
glthread_recursive_lock_init_multithreaded (gl_recursive_lock_t *lock)
Packit 8f70b4
{
Packit 8f70b4
  int err;
Packit 8f70b4
Packit 8f70b4
  err = mutex_init (&lock->mutex, USYNC_THREAD, NULL);
Packit 8f70b4
  if (err != 0)
Packit 8f70b4
    return err;
Packit 8f70b4
  lock->owner = (thread_t) 0;
Packit 8f70b4
  lock->depth = 0;
Packit 8f70b4
  return 0;
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
int
Packit 8f70b4
glthread_recursive_lock_lock_multithreaded (gl_recursive_lock_t *lock)
Packit 8f70b4
{
Packit 8f70b4
  thread_t self = thr_self ();
Packit 8f70b4
  if (lock->owner != self)
Packit 8f70b4
    {
Packit 8f70b4
      int err;
Packit 8f70b4
Packit 8f70b4
      err = mutex_lock (&lock->mutex);
Packit 8f70b4
      if (err != 0)
Packit 8f70b4
        return err;
Packit 8f70b4
      lock->owner = self;
Packit 8f70b4
    }
Packit 8f70b4
  if (++(lock->depth) == 0) /* wraparound? */
Packit 8f70b4
    {
Packit 8f70b4
      lock->depth--;
Packit 8f70b4
      return EAGAIN;
Packit 8f70b4
    }
Packit 8f70b4
  return 0;
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
int
Packit 8f70b4
glthread_recursive_lock_unlock_multithreaded (gl_recursive_lock_t *lock)
Packit 8f70b4
{
Packit 8f70b4
  if (lock->owner != thr_self ())
Packit 8f70b4
    return EPERM;
Packit 8f70b4
  if (lock->depth == 0)
Packit 8f70b4
    return EINVAL;
Packit 8f70b4
  if (--(lock->depth) == 0)
Packit 8f70b4
    {
Packit 8f70b4
      lock->owner = (thread_t) 0;
Packit 8f70b4
      return mutex_unlock (&lock->mutex);
Packit 8f70b4
    }
Packit 8f70b4
  else
Packit 8f70b4
    return 0;
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
int
Packit 8f70b4
glthread_recursive_lock_destroy_multithreaded (gl_recursive_lock_t *lock)
Packit 8f70b4
{
Packit 8f70b4
  if (lock->owner != (thread_t) 0)
Packit 8f70b4
    return EBUSY;
Packit 8f70b4
  return mutex_destroy (&lock->mutex);
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
/* -------------------------- gl_once_t datatype -------------------------- */
Packit 8f70b4
Packit 8f70b4
int
Packit 8f70b4
glthread_once_multithreaded (gl_once_t *once_control, void (*initfunction) (void))
Packit 8f70b4
{
Packit 8f70b4
  if (!once_control->inited)
Packit 8f70b4
    {
Packit 8f70b4
      int err;
Packit 8f70b4
Packit 8f70b4
      /* Use the mutex to guarantee that if another thread is already calling
Packit 8f70b4
         the initfunction, this thread waits until it's finished.  */
Packit 8f70b4
      err = mutex_lock (&once_control->mutex);
Packit 8f70b4
      if (err != 0)
Packit 8f70b4
        return err;
Packit 8f70b4
      if (!once_control->inited)
Packit 8f70b4
        {
Packit 8f70b4
          once_control->inited = 1;
Packit 8f70b4
          initfunction ();
Packit 8f70b4
        }
Packit 8f70b4
      return mutex_unlock (&once_control->mutex);
Packit 8f70b4
    }
Packit 8f70b4
  else
Packit 8f70b4
    return 0;
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
int
Packit 8f70b4
glthread_once_singlethreaded (gl_once_t *once_control)
Packit 8f70b4
{
Packit 8f70b4
  /* We know that gl_once_t contains an integer type.  */
Packit 8f70b4
  if (!once_control->inited)
Packit 8f70b4
    {
Packit 8f70b4
      /* First time use of once_control.  Invert the marker.  */
Packit 8f70b4
      once_control->inited = ~ 0;
Packit 8f70b4
      return 1;
Packit 8f70b4
    }
Packit 8f70b4
  else
Packit 8f70b4
    return 0;
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
#endif
Packit 8f70b4
Packit 8f70b4
/* ========================================================================= */
Packit 8f70b4
Packit 8f70b4
#if USE_WINDOWS_THREADS
Packit 8f70b4
Packit 8f70b4
/* -------------------------- gl_lock_t datatype -------------------------- */
Packit 8f70b4
Packit 8f70b4
void
Packit 8f70b4
glthread_lock_init_func (gl_lock_t *lock)
Packit 8f70b4
{
Packit 8f70b4
  InitializeCriticalSection (&lock->lock);
Packit 8f70b4
  lock->guard.done = 1;
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
int
Packit 8f70b4
glthread_lock_lock_func (gl_lock_t *lock)
Packit 8f70b4
{
Packit 8f70b4
  if (!lock->guard.done)
Packit 8f70b4
    {
Packit 8f70b4
      if (InterlockedIncrement (&lock->guard.started) == 0)
Packit 8f70b4
        /* This thread is the first one to need this lock.  Initialize it.  */
Packit 8f70b4
        glthread_lock_init (lock);
Packit 8f70b4
      else
Packit 8f70b4
        /* Yield the CPU while waiting for another thread to finish
Packit 8f70b4
           initializing this lock.  */
Packit 8f70b4
        while (!lock->guard.done)
Packit 8f70b4
          Sleep (0);
Packit 8f70b4
    }
Packit 8f70b4
  EnterCriticalSection (&lock->lock);
Packit 8f70b4
  return 0;
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
int
Packit 8f70b4
glthread_lock_unlock_func (gl_lock_t *lock)
Packit 8f70b4
{
Packit 8f70b4
  if (!lock->guard.done)
Packit 8f70b4
    return EINVAL;
Packit 8f70b4
  LeaveCriticalSection (&lock->lock);
Packit 8f70b4
  return 0;
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
int
Packit 8f70b4
glthread_lock_destroy_func (gl_lock_t *lock)
Packit 8f70b4
{
Packit 8f70b4
  if (!lock->guard.done)
Packit 8f70b4
    return EINVAL;
Packit 8f70b4
  DeleteCriticalSection (&lock->lock);
Packit 8f70b4
  lock->guard.done = 0;
Packit 8f70b4
  return 0;
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
/* ------------------------- gl_rwlock_t datatype ------------------------- */
Packit 8f70b4
Packit 8f70b4
/* In this file, the waitqueues are implemented as circular arrays.  */
Packit 8f70b4
#define gl_waitqueue_t gl_carray_waitqueue_t
Packit 8f70b4
Packit 8f70b4
static void
Packit 8f70b4
gl_waitqueue_init (gl_waitqueue_t *wq)
Packit 8f70b4
{
Packit 8f70b4
  wq->array = NULL;
Packit 8f70b4
  wq->count = 0;
Packit 8f70b4
  wq->alloc = 0;
Packit 8f70b4
  wq->offset = 0;
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
/* Enqueues the current thread, represented by an event, in a wait queue.
Packit 8f70b4
   Returns INVALID_HANDLE_VALUE if an allocation failure occurs.  */
Packit 8f70b4
static HANDLE
Packit 8f70b4
gl_waitqueue_add (gl_waitqueue_t *wq)
Packit 8f70b4
{
Packit 8f70b4
  HANDLE event;
Packit 8f70b4
  unsigned int index;
Packit 8f70b4
Packit 8f70b4
  if (wq->count == wq->alloc)
Packit 8f70b4
    {
Packit 8f70b4
      unsigned int new_alloc = 2 * wq->alloc + 1;
Packit 8f70b4
      HANDLE *new_array =
Packit 8f70b4
        (HANDLE *) realloc (wq->array, new_alloc * sizeof (HANDLE));
Packit 8f70b4
      if (new_array == NULL)
Packit 8f70b4
        /* No more memory.  */
Packit 8f70b4
        return INVALID_HANDLE_VALUE;
Packit 8f70b4
      /* Now is a good opportunity to rotate the array so that its contents
Packit 8f70b4
         starts at offset 0.  */
Packit 8f70b4
      if (wq->offset > 0)
Packit 8f70b4
        {
Packit 8f70b4
          unsigned int old_count = wq->count;
Packit 8f70b4
          unsigned int old_alloc = wq->alloc;
Packit 8f70b4
          unsigned int old_offset = wq->offset;
Packit 8f70b4
          unsigned int i;
Packit 8f70b4
          if (old_offset + old_count > old_alloc)
Packit 8f70b4
            {
Packit 8f70b4
              unsigned int limit = old_offset + old_count - old_alloc;
Packit 8f70b4
              for (i = 0; i < limit; i++)
Packit 8f70b4
                new_array[old_alloc + i] = new_array[i];
Packit 8f70b4
            }
Packit 8f70b4
          for (i = 0; i < old_count; i++)
Packit 8f70b4
            new_array[i] = new_array[old_offset + i];
Packit 8f70b4
          wq->offset = 0;
Packit 8f70b4
        }
Packit 8f70b4
      wq->array = new_array;
Packit 8f70b4
      wq->alloc = new_alloc;
Packit 8f70b4
    }
Packit 8f70b4
  /* Whether the created event is a manual-reset one or an auto-reset one,
Packit 8f70b4
     does not matter, since we will wait on it only once.  */
Packit 8f70b4
  event = CreateEvent (NULL, TRUE, FALSE, NULL);
Packit 8f70b4
  if (event == INVALID_HANDLE_VALUE)
Packit 8f70b4
    /* No way to allocate an event.  */
Packit 8f70b4
    return INVALID_HANDLE_VALUE;
Packit 8f70b4
  index = wq->offset + wq->count;
Packit 8f70b4
  if (index >= wq->alloc)
Packit 8f70b4
    index -= wq->alloc;
Packit 8f70b4
  wq->array[index] = event;
Packit 8f70b4
  wq->count++;
Packit 8f70b4
  return event;
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
/* Notifies the first thread from a wait queue and dequeues it.  */
Packit 8f70b4
static void
Packit 8f70b4
gl_waitqueue_notify_first (gl_waitqueue_t *wq)
Packit 8f70b4
{
Packit 8f70b4
  SetEvent (wq->array[wq->offset + 0]);
Packit 8f70b4
  wq->offset++;
Packit 8f70b4
  wq->count--;
Packit 8f70b4
  if (wq->count == 0 || wq->offset == wq->alloc)
Packit 8f70b4
    wq->offset = 0;
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
/* Notifies all threads from a wait queue and dequeues them all.  */
Packit 8f70b4
static void
Packit 8f70b4
gl_waitqueue_notify_all (gl_waitqueue_t *wq)
Packit 8f70b4
{
Packit 8f70b4
  unsigned int i;
Packit 8f70b4
Packit 8f70b4
  for (i = 0; i < wq->count; i++)
Packit 8f70b4
    {
Packit 8f70b4
      unsigned int index = wq->offset + i;
Packit 8f70b4
      if (index >= wq->alloc)
Packit 8f70b4
        index -= wq->alloc;
Packit 8f70b4
      SetEvent (wq->array[index]);
Packit 8f70b4
    }
Packit 8f70b4
  wq->count = 0;
Packit 8f70b4
  wq->offset = 0;
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
void
Packit 8f70b4
glthread_rwlock_init_func (gl_rwlock_t *lock)
Packit 8f70b4
{
Packit 8f70b4
  InitializeCriticalSection (&lock->lock);
Packit 8f70b4
  gl_waitqueue_init (&lock->waiting_readers);
Packit 8f70b4
  gl_waitqueue_init (&lock->waiting_writers);
Packit 8f70b4
  lock->runcount = 0;
Packit 8f70b4
  lock->guard.done = 1;
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
int
Packit 8f70b4
glthread_rwlock_rdlock_func (gl_rwlock_t *lock)
Packit 8f70b4
{
Packit 8f70b4
  if (!lock->guard.done)
Packit 8f70b4
    {
Packit 8f70b4
      if (InterlockedIncrement (&lock->guard.started) == 0)
Packit 8f70b4
        /* This thread is the first one to need this lock.  Initialize it.  */
Packit 8f70b4
        glthread_rwlock_init (lock);
Packit 8f70b4
      else
Packit 8f70b4
        /* Yield the CPU while waiting for another thread to finish
Packit 8f70b4
           initializing this lock.  */
Packit 8f70b4
        while (!lock->guard.done)
Packit 8f70b4
          Sleep (0);
Packit 8f70b4
    }
Packit 8f70b4
  EnterCriticalSection (&lock->lock);
Packit 8f70b4
  /* Test whether only readers are currently running, and whether the runcount
Packit 8f70b4
     field will not overflow, and whether no writer is waiting.  The latter
Packit 8f70b4
     condition is because POSIX recommends that "write locks shall take
Packit 8f70b4
     precedence over read locks", to avoid "writer starvation".  */
Packit 8f70b4
  if (!(lock->runcount + 1 > 0 && lock->waiting_writers.count == 0))
Packit 8f70b4
    {
Packit 8f70b4
      /* This thread has to wait for a while.  Enqueue it among the
Packit 8f70b4
         waiting_readers.  */
Packit 8f70b4
      HANDLE event = gl_waitqueue_add (&lock->waiting_readers);
Packit 8f70b4
      if (event != INVALID_HANDLE_VALUE)
Packit 8f70b4
        {
Packit 8f70b4
          DWORD result;
Packit 8f70b4
          LeaveCriticalSection (&lock->lock);
Packit 8f70b4
          /* Wait until another thread signals this event.  */
Packit 8f70b4
          result = WaitForSingleObject (event, INFINITE);
Packit 8f70b4
          if (result == WAIT_FAILED || result == WAIT_TIMEOUT)
Packit 8f70b4
            abort ();
Packit 8f70b4
          CloseHandle (event);
Packit 8f70b4
          /* The thread which signalled the event already did the bookkeeping:
Packit 8f70b4
             removed us from the waiting_readers, incremented lock->runcount.  */
Packit 8f70b4
          if (!(lock->runcount > 0))
Packit 8f70b4
            abort ();
Packit 8f70b4
          return 0;
Packit 8f70b4
        }
Packit 8f70b4
      else
Packit 8f70b4
        {
Packit 8f70b4
          /* Allocation failure.  Weird.  */
Packit 8f70b4
          do
Packit 8f70b4
            {
Packit 8f70b4
              LeaveCriticalSection (&lock->lock);
Packit 8f70b4
              Sleep (1);
Packit 8f70b4
              EnterCriticalSection (&lock->lock);
Packit 8f70b4
            }
Packit 8f70b4
          while (!(lock->runcount + 1 > 0));
Packit 8f70b4
        }
Packit 8f70b4
    }
Packit 8f70b4
  lock->runcount++;
Packit 8f70b4
  LeaveCriticalSection (&lock->lock);
Packit 8f70b4
  return 0;
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
int
Packit 8f70b4
glthread_rwlock_wrlock_func (gl_rwlock_t *lock)
Packit 8f70b4
{
Packit 8f70b4
  if (!lock->guard.done)
Packit 8f70b4
    {
Packit 8f70b4
      if (InterlockedIncrement (&lock->guard.started) == 0)
Packit 8f70b4
        /* This thread is the first one to need this lock.  Initialize it.  */
Packit 8f70b4
        glthread_rwlock_init (lock);
Packit 8f70b4
      else
Packit 8f70b4
        /* Yield the CPU while waiting for another thread to finish
Packit 8f70b4
           initializing this lock.  */
Packit 8f70b4
        while (!lock->guard.done)
Packit 8f70b4
          Sleep (0);
Packit 8f70b4
    }
Packit 8f70b4
  EnterCriticalSection (&lock->lock);
Packit 8f70b4
  /* Test whether no readers or writers are currently running.  */
Packit 8f70b4
  if (!(lock->runcount == 0))
Packit 8f70b4
    {
Packit 8f70b4
      /* This thread has to wait for a while.  Enqueue it among the
Packit 8f70b4
         waiting_writers.  */
Packit 8f70b4
      HANDLE event = gl_waitqueue_add (&lock->waiting_writers);
Packit 8f70b4
      if (event != INVALID_HANDLE_VALUE)
Packit 8f70b4
        {
Packit 8f70b4
          DWORD result;
Packit 8f70b4
          LeaveCriticalSection (&lock->lock);
Packit 8f70b4
          /* Wait until another thread signals this event.  */
Packit 8f70b4
          result = WaitForSingleObject (event, INFINITE);
Packit 8f70b4
          if (result == WAIT_FAILED || result == WAIT_TIMEOUT)
Packit 8f70b4
            abort ();
Packit 8f70b4
          CloseHandle (event);
Packit 8f70b4
          /* The thread which signalled the event already did the bookkeeping:
Packit 8f70b4
             removed us from the waiting_writers, set lock->runcount = -1.  */
Packit 8f70b4
          if (!(lock->runcount == -1))
Packit 8f70b4
            abort ();
Packit 8f70b4
          return 0;
Packit 8f70b4
        }
Packit 8f70b4
      else
Packit 8f70b4
        {
Packit 8f70b4
          /* Allocation failure.  Weird.  */
Packit 8f70b4
          do
Packit 8f70b4
            {
Packit 8f70b4
              LeaveCriticalSection (&lock->lock);
Packit 8f70b4
              Sleep (1);
Packit 8f70b4
              EnterCriticalSection (&lock->lock);
Packit 8f70b4
            }
Packit 8f70b4
          while (!(lock->runcount == 0));
Packit 8f70b4
        }
Packit 8f70b4
    }
Packit 8f70b4
  lock->runcount--; /* runcount becomes -1 */
Packit 8f70b4
  LeaveCriticalSection (&lock->lock);
Packit 8f70b4
  return 0;
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
int
Packit 8f70b4
glthread_rwlock_unlock_func (gl_rwlock_t *lock)
Packit 8f70b4
{
Packit 8f70b4
  if (!lock->guard.done)
Packit 8f70b4
    return EINVAL;
Packit 8f70b4
  EnterCriticalSection (&lock->lock);
Packit 8f70b4
  if (lock->runcount < 0)
Packit 8f70b4
    {
Packit 8f70b4
      /* Drop a writer lock.  */
Packit 8f70b4
      if (!(lock->runcount == -1))
Packit 8f70b4
        abort ();
Packit 8f70b4
      lock->runcount = 0;
Packit 8f70b4
    }
Packit 8f70b4
  else
Packit 8f70b4
    {
Packit 8f70b4
      /* Drop a reader lock.  */
Packit 8f70b4
      if (!(lock->runcount > 0))
Packit 8f70b4
        {
Packit 8f70b4
          LeaveCriticalSection (&lock->lock);
Packit 8f70b4
          return EPERM;
Packit 8f70b4
        }
Packit 8f70b4
      lock->runcount--;
Packit 8f70b4
    }
Packit 8f70b4
  if (lock->runcount == 0)
Packit 8f70b4
    {
Packit 8f70b4
      /* POSIX recommends that "write locks shall take precedence over read
Packit 8f70b4
         locks", to avoid "writer starvation".  */
Packit 8f70b4
      if (lock->waiting_writers.count > 0)
Packit 8f70b4
        {
Packit 8f70b4
          /* Wake up one of the waiting writers.  */
Packit 8f70b4
          lock->runcount--;
Packit 8f70b4
          gl_waitqueue_notify_first (&lock->waiting_writers);
Packit 8f70b4
        }
Packit 8f70b4
      else
Packit 8f70b4
        {
Packit 8f70b4
          /* Wake up all waiting readers.  */
Packit 8f70b4
          lock->runcount += lock->waiting_readers.count;
Packit 8f70b4
          gl_waitqueue_notify_all (&lock->waiting_readers);
Packit 8f70b4
        }
Packit 8f70b4
    }
Packit 8f70b4
  LeaveCriticalSection (&lock->lock);
Packit 8f70b4
  return 0;
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
int
Packit 8f70b4
glthread_rwlock_destroy_func (gl_rwlock_t *lock)
Packit 8f70b4
{
Packit 8f70b4
  if (!lock->guard.done)
Packit 8f70b4
    return EINVAL;
Packit 8f70b4
  if (lock->runcount != 0)
Packit 8f70b4
    return EBUSY;
Packit 8f70b4
  DeleteCriticalSection (&lock->lock);
Packit 8f70b4
  if (lock->waiting_readers.array != NULL)
Packit 8f70b4
    free (lock->waiting_readers.array);
Packit 8f70b4
  if (lock->waiting_writers.array != NULL)
Packit 8f70b4
    free (lock->waiting_writers.array);
Packit 8f70b4
  lock->guard.done = 0;
Packit 8f70b4
  return 0;
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
/* --------------------- gl_recursive_lock_t datatype --------------------- */
Packit 8f70b4
Packit 8f70b4
void
Packit 8f70b4
glthread_recursive_lock_init_func (gl_recursive_lock_t *lock)
Packit 8f70b4
{
Packit 8f70b4
  lock->owner = 0;
Packit 8f70b4
  lock->depth = 0;
Packit 8f70b4
  InitializeCriticalSection (&lock->lock);
Packit 8f70b4
  lock->guard.done = 1;
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
int
Packit 8f70b4
glthread_recursive_lock_lock_func (gl_recursive_lock_t *lock)
Packit 8f70b4
{
Packit 8f70b4
  if (!lock->guard.done)
Packit 8f70b4
    {
Packit 8f70b4
      if (InterlockedIncrement (&lock->guard.started) == 0)
Packit 8f70b4
        /* This thread is the first one to need this lock.  Initialize it.  */
Packit 8f70b4
        glthread_recursive_lock_init (lock);
Packit 8f70b4
      else
Packit 8f70b4
        /* Yield the CPU while waiting for another thread to finish
Packit 8f70b4
           initializing this lock.  */
Packit 8f70b4
        while (!lock->guard.done)
Packit 8f70b4
          Sleep (0);
Packit 8f70b4
    }
Packit 8f70b4
  {
Packit 8f70b4
    DWORD self = GetCurrentThreadId ();
Packit 8f70b4
    if (lock->owner != self)
Packit 8f70b4
      {
Packit 8f70b4
        EnterCriticalSection (&lock->lock);
Packit 8f70b4
        lock->owner = self;
Packit 8f70b4
      }
Packit 8f70b4
    if (++(lock->depth) == 0) /* wraparound? */
Packit 8f70b4
      {
Packit 8f70b4
        lock->depth--;
Packit 8f70b4
        return EAGAIN;
Packit 8f70b4
      }
Packit 8f70b4
  }
Packit 8f70b4
  return 0;
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
int
Packit 8f70b4
glthread_recursive_lock_unlock_func (gl_recursive_lock_t *lock)
Packit 8f70b4
{
Packit 8f70b4
  if (lock->owner != GetCurrentThreadId ())
Packit 8f70b4
    return EPERM;
Packit 8f70b4
  if (lock->depth == 0)
Packit 8f70b4
    return EINVAL;
Packit 8f70b4
  if (--(lock->depth) == 0)
Packit 8f70b4
    {
Packit 8f70b4
      lock->owner = 0;
Packit 8f70b4
      LeaveCriticalSection (&lock->lock);
Packit 8f70b4
    }
Packit 8f70b4
  return 0;
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
int
Packit 8f70b4
glthread_recursive_lock_destroy_func (gl_recursive_lock_t *lock)
Packit 8f70b4
{
Packit 8f70b4
  if (lock->owner != 0)
Packit 8f70b4
    return EBUSY;
Packit 8f70b4
  DeleteCriticalSection (&lock->lock);
Packit 8f70b4
  lock->guard.done = 0;
Packit 8f70b4
  return 0;
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
/* -------------------------- gl_once_t datatype -------------------------- */
Packit 8f70b4
Packit 8f70b4
void
Packit 8f70b4
glthread_once_func (gl_once_t *once_control, void (*initfunction) (void))
Packit 8f70b4
{
Packit 8f70b4
  if (once_control->inited <= 0)
Packit 8f70b4
    {
Packit 8f70b4
      if (InterlockedIncrement (&once_control->started) == 0)
Packit 8f70b4
        {
Packit 8f70b4
          /* This thread is the first one to come to this once_control.  */
Packit 8f70b4
          InitializeCriticalSection (&once_control->lock);
Packit 8f70b4
          EnterCriticalSection (&once_control->lock);
Packit 8f70b4
          once_control->inited = 0;
Packit 8f70b4
          initfunction ();
Packit 8f70b4
          once_control->inited = 1;
Packit 8f70b4
          LeaveCriticalSection (&once_control->lock);
Packit 8f70b4
        }
Packit 8f70b4
      else
Packit 8f70b4
        {
Packit 8f70b4
          /* Undo last operation.  */
Packit 8f70b4
          InterlockedDecrement (&once_control->started);
Packit 8f70b4
          /* Some other thread has already started the initialization.
Packit 8f70b4
             Yield the CPU while waiting for the other thread to finish
Packit 8f70b4
             initializing and taking the lock.  */
Packit 8f70b4
          while (once_control->inited < 0)
Packit 8f70b4
            Sleep (0);
Packit 8f70b4
          if (once_control->inited <= 0)
Packit 8f70b4
            {
Packit 8f70b4
              /* Take the lock.  This blocks until the other thread has
Packit 8f70b4
                 finished calling the initfunction.  */
Packit 8f70b4
              EnterCriticalSection (&once_control->lock);
Packit 8f70b4
              LeaveCriticalSection (&once_control->lock);
Packit 8f70b4
              if (!(once_control->inited > 0))
Packit 8f70b4
                abort ();
Packit 8f70b4
            }
Packit 8f70b4
        }
Packit 8f70b4
    }
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
#endif
Packit 8f70b4
Packit 8f70b4
/* ========================================================================= */