diff options
Diffstat (limited to 'lib/glthread')
| -rw-r--r-- | lib/glthread/lock.c | 971 | ||||
| -rw-r--r-- | lib/glthread/lock.h | 565 | ||||
| -rw-r--r-- | lib/glthread/threadlib.c | 64 | 
3 files changed, 471 insertions, 1129 deletions
| diff --git a/lib/glthread/lock.c b/lib/glthread/lock.c index 9e148910..b650c219 100644 --- a/lib/glthread/lock.c +++ b/lib/glthread/lock.c @@ -1,31 +1,21 @@  /* Locking in multithreaded situations. -   Copyright (C) 2005-2018 Free Software Foundation, Inc. +   Copyright (C) 2005-2022 Free Software Foundation, Inc. -   This program is free software: you can redistribute it and/or -   modify it under the terms of either: +   This file is free software: you can redistribute it and/or modify +   it under the terms of the GNU Lesser General Public License as +   published by the Free Software Foundation; either version 2.1 of the +   License, or (at your option) any later version. -     * the GNU Lesser General Public License as published by the Free -       Software Foundation; either version 3 of the License, or (at your -       option) any later version. - -   or - -     * the GNU General Public License as published by the Free -       Software Foundation; either version 2 of the License, or (at your -       option) any later version. - -   or both in parallel, as here. -   This program is distributed in the hope that it will be useful, +   This file is distributed in the hope that it will be useful,     but WITHOUT ANY WARRANTY; without even the implied warranty of     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the -   GNU General Public License for more details. +   GNU Lesser General Public License for more details. -   You should have received a copy of the GNU General Public License -   along with this program; if not, see <https://www.gnu.org/licenses/>.  */ +   You should have received a copy of the GNU Lesser General Public License +   along with this program.  If not, see <https://www.gnu.org/licenses/>.  */  /* Written by Bruno Haible <bruno@clisp.org>, 2005. -   Based on GCC's gthr-posix.h, gthr-posix95.h, gthr-solaris.h, -   gthr-win32.h.  */ +   Based on GCC's gthr-posix.h, gthr-posix95.h.  */  #include <config.h> @@ -33,6 +23,229 @@  /* ========================================================================= */ +#if USE_ISOC_THREADS || USE_ISOC_AND_POSIX_THREADS + +/* -------------------------- gl_lock_t datatype -------------------------- */ + +int +glthread_lock_init (gl_lock_t *lock) +{ +  if (mtx_init (&lock->mutex, mtx_plain) != thrd_success) +    return ENOMEM; +  lock->init_needed = 0; +  return 0; +} + +int +glthread_lock_lock (gl_lock_t *lock) +{ +  if (lock->init_needed) +    call_once (&lock->init_once, lock->init_func); +  if (mtx_lock (&lock->mutex) != thrd_success) +    return EAGAIN; +  return 0; +} + +int +glthread_lock_unlock (gl_lock_t *lock) +{ +  if (lock->init_needed) +    call_once (&lock->init_once, lock->init_func); +  if (mtx_unlock (&lock->mutex) != thrd_success) +    return EINVAL; +  return 0; +} + +int +glthread_lock_destroy (gl_lock_t *lock) +{ +  if (lock->init_needed) +    call_once (&lock->init_once, lock->init_func); +  mtx_destroy (&lock->mutex); +  return 0; +} + +/* ------------------------- gl_rwlock_t datatype ------------------------- */ + +int +glthread_rwlock_init (gl_rwlock_t *lock) +{ +  if (mtx_init (&lock->lock, mtx_plain) != thrd_success +      || cnd_init (&lock->waiting_readers) != thrd_success +      || cnd_init (&lock->waiting_writers) != thrd_success) +    return ENOMEM; +  lock->waiting_writers_count = 0; +  lock->runcount = 0; +  lock->init_needed = 0; +  return 0; +} + +int +glthread_rwlock_rdlock (gl_rwlock_t *lock) +{ +  if (lock->init_needed) +    call_once (&lock->init_once, lock->init_func); +  if (mtx_lock (&lock->lock) != thrd_success) +    return EAGAIN; +  /* Test whether only readers are currently running, and whether the runcount +     field will not overflow, and whether no writer is waiting.  The latter +     condition is because POSIX recommends that "write locks shall take +     precedence over read locks", to avoid "writer starvation".  */ +  while (!(lock->runcount + 1 > 0 && lock->waiting_writers_count == 0)) +    { +      /* This thread has to wait for a while.  Enqueue it among the +         waiting_readers.  */ +      if (cnd_wait (&lock->waiting_readers, &lock->lock) != thrd_success) +        { +          mtx_unlock (&lock->lock); +          return EINVAL; +        } +    } +  lock->runcount++; +  if (mtx_unlock (&lock->lock) != thrd_success) +    return EINVAL; +  return 0; +} + +int +glthread_rwlock_wrlock (gl_rwlock_t *lock) +{ +  if (lock->init_needed) +    call_once (&lock->init_once, lock->init_func); +  if (mtx_lock (&lock->lock) != thrd_success) +    return EAGAIN; +  /* Test whether no readers or writers are currently running.  */ +  while (!(lock->runcount == 0)) +    { +      /* This thread has to wait for a while.  Enqueue it among the +         waiting_writers.  */ +      lock->waiting_writers_count++; +      if (cnd_wait (&lock->waiting_writers, &lock->lock) != thrd_success) +        { +          lock->waiting_writers_count--; +          mtx_unlock (&lock->lock); +          return EINVAL; +        } +      lock->waiting_writers_count--; +    } +  lock->runcount--; /* runcount becomes -1 */ +  if (mtx_unlock (&lock->lock) != thrd_success) +    return EINVAL; +  return 0; +} + +int +glthread_rwlock_unlock (gl_rwlock_t *lock) +{ +  if (lock->init_needed) +    call_once (&lock->init_once, lock->init_func); +  if (mtx_lock (&lock->lock) != thrd_success) +    return EAGAIN; +  if (lock->runcount < 0) +    { +      /* Drop a writer lock.  */ +      if (!(lock->runcount == -1)) +        { +          mtx_unlock (&lock->lock); +          return EINVAL; +        } +      lock->runcount = 0; +    } +  else +    { +      /* Drop a reader lock.  */ +      if (!(lock->runcount > 0)) +        { +          mtx_unlock (&lock->lock); +          return EINVAL; +        } +      lock->runcount--; +    } +  if (lock->runcount == 0) +    { +      /* POSIX recommends that "write locks shall take precedence over read +         locks", to avoid "writer starvation".  */ +      if (lock->waiting_writers_count > 0) +        { +          /* Wake up one of the waiting writers.  */ +          if (cnd_signal (&lock->waiting_writers) != thrd_success) +            { +              mtx_unlock (&lock->lock); +              return EINVAL; +            } +        } +      else +        { +          /* Wake up all waiting readers.  */ +          if (cnd_broadcast (&lock->waiting_readers) != thrd_success) +            { +              mtx_unlock (&lock->lock); +              return EINVAL; +            } +        } +    } +  if (mtx_unlock (&lock->lock) != thrd_success) +    return EINVAL; +  return 0; +} + +int +glthread_rwlock_destroy (gl_rwlock_t *lock) +{ +  if (lock->init_needed) +    call_once (&lock->init_once, lock->init_func); +  mtx_destroy (&lock->lock); +  cnd_destroy (&lock->waiting_readers); +  cnd_destroy (&lock->waiting_writers); +  return 0; +} + +/* --------------------- gl_recursive_lock_t datatype --------------------- */ + +int +glthread_recursive_lock_init (gl_recursive_lock_t *lock) +{ +  if (mtx_init (&lock->mutex, mtx_plain | mtx_recursive) != thrd_success) +    return ENOMEM; +  lock->init_needed = 0; +  return 0; +} + +int +glthread_recursive_lock_lock (gl_recursive_lock_t *lock) +{ +  if (lock->init_needed) +    call_once (&lock->init_once, lock->init_func); +  if (mtx_lock (&lock->mutex) != thrd_success) +    return EAGAIN; +  return 0; +} + +int +glthread_recursive_lock_unlock (gl_recursive_lock_t *lock) +{ +  if (lock->init_needed) +    call_once (&lock->init_once, lock->init_func); +  if (mtx_unlock (&lock->mutex) != thrd_success) +    return EINVAL; +  return 0; +} + +int +glthread_recursive_lock_destroy (gl_recursive_lock_t *lock) +{ +  if (lock->init_needed) +    call_once (&lock->init_once, lock->init_func); +  mtx_destroy (&lock->mutex); +  return 0; +} + +/* -------------------------- gl_once_t datatype -------------------------- */ + +#endif + +/* ========================================================================= */ +  #if USE_POSIX_THREADS  /* -------------------------- gl_lock_t datatype -------------------------- */ @@ -41,7 +254,7 @@  # if HAVE_PTHREAD_RWLOCK && (HAVE_PTHREAD_RWLOCK_RDLOCK_PREFER_WRITER || (defined PTHREAD_RWLOCK_WRITER_NONRECURSIVE_INITIALIZER_NP && (__GNU_LIBRARY__ > 1))) -#  ifdef PTHREAD_RWLOCK_INITIALIZER +#  if defined PTHREAD_RWLOCK_INITIALIZER || defined PTHREAD_RWLOCK_INITIALIZER_NP  #   if !HAVE_PTHREAD_RWLOCK_RDLOCK_PREFER_WRITER       /* glibc with bug https://sourceware.org/bugzilla/show_bug.cgi?id=13701 */ @@ -505,726 +718,32 @@ glthread_once_singlethreaded (pthread_once_t *once_control)      return 0;  } -#endif - -/* ========================================================================= */ - -#if USE_PTH_THREADS - -/* Use the GNU Pth threads library.  */ - -/* -------------------------- gl_lock_t datatype -------------------------- */ - -/* ------------------------- gl_rwlock_t datatype ------------------------- */ - -# if !HAVE_PTH_RWLOCK_ACQUIRE_PREFER_WRITER - -int -glthread_rwlock_init_multithreaded (gl_rwlock_t *lock) -{ -  if (!pth_mutex_init (&lock->lock)) -    return errno; -  if (!pth_cond_init (&lock->waiting_readers)) -    return errno; -  if (!pth_cond_init (&lock->waiting_writers)) -    return errno; -  lock->waiting_writers_count = 0; -  lock->runcount = 0; -  lock->initialized = 1; -  return 0; -} - -int -glthread_rwlock_rdlock_multithreaded (gl_rwlock_t *lock) -{ -  if (!lock->initialized) -    glthread_rwlock_init_multithreaded (lock); -  if (!pth_mutex_acquire (&lock->lock, 0, NULL)) -    return errno; -  /* Test whether only readers are currently running, and whether the runcount -     field will not overflow, and whether no writer is waiting.  The latter -     condition is because POSIX recommends that "write locks shall take -     precedence over read locks", to avoid "writer starvation".  */ -  while (!(lock->runcount + 1 > 0 && lock->waiting_writers_count == 0)) -    { -      /* This thread has to wait for a while.  Enqueue it among the -         waiting_readers.  */ -      if (!pth_cond_await (&lock->waiting_readers, &lock->lock, NULL)) -        { -          int err = errno; -          pth_mutex_release (&lock->lock); -          return err; -        } -    } -  lock->runcount++; -  return (!pth_mutex_release (&lock->lock) ? errno : 0); -} - -int -glthread_rwlock_wrlock_multithreaded (gl_rwlock_t *lock) -{ -  if (!lock->initialized) -    glthread_rwlock_init_multithreaded (lock); -  if (!pth_mutex_acquire (&lock->lock, 0, NULL)) -    return errno; -  /* Test whether no readers or writers are currently running.  */ -  while (!(lock->runcount == 0)) -    { -      /* This thread has to wait for a while.  Enqueue it among the -         waiting_writers.  */ -      lock->waiting_writers_count++; -      if (!pth_cond_await (&lock->waiting_writers, &lock->lock, NULL)) -        { -          int err = errno; -          lock->waiting_writers_count--; -          pth_mutex_release (&lock->lock); -          return err; -        } -      lock->waiting_writers_count--; -    } -  lock->runcount--; /* runcount becomes -1 */ -  return (!pth_mutex_release (&lock->lock) ? errno : 0); -} +# if !(PTHREAD_IN_USE_DETECTION_HARD || USE_POSIX_THREADS_WEAK)  int -glthread_rwlock_unlock_multithreaded (gl_rwlock_t *lock) +glthread_once_multithreaded (pthread_once_t *once_control, +                             void (*init_function) (void))  { -  int err; - -  if (!lock->initialized) -    return EINVAL; -  if (!pth_mutex_acquire (&lock->lock, 0, NULL)) -    return errno; -  if (lock->runcount < 0) -    { -      /* Drop a writer lock.  */ -      if (!(lock->runcount == -1)) -        { -          pth_mutex_release (&lock->lock); -          return EINVAL; -        } -      lock->runcount = 0; -    } -  else +  int err = pthread_once (once_control, init_function); +  if (err == ENOSYS)      { -      /* Drop a reader lock.  */ -      if (!(lock->runcount > 0)) -        { -          pth_mutex_release (&lock->lock); -          return EINVAL; -        } -      lock->runcount--; -    } -  if (lock->runcount == 0) -    { -      /* POSIX recommends that "write locks shall take precedence over read -         locks", to avoid "writer starvation".  */ -      if (lock->waiting_writers_count > 0) -        { -          /* Wake up one of the waiting writers.  */ -          if (!pth_cond_notify (&lock->waiting_writers, FALSE)) -            { -              int err = errno; -              pth_mutex_release (&lock->lock); -              return err; -            } -        } -      else -        { -          /* Wake up all waiting readers.  */ -          if (!pth_cond_notify (&lock->waiting_readers, TRUE)) -            { -              int err = errno; -              pth_mutex_release (&lock->lock); -              return err; -            } -        } +      /* This happens on FreeBSD 11: The pthread_once function in libc returns +         ENOSYS.  */ +      if (glthread_once_singlethreaded (once_control)) +        init_function (); +      return 0;      } -  return (!pth_mutex_release (&lock->lock) ? errno : 0); -} - -int -glthread_rwlock_destroy_multithreaded (gl_rwlock_t *lock) -{ -  lock->initialized = 0; -  return 0; +  return err;  }  # endif -/* --------------------- gl_recursive_lock_t datatype --------------------- */ - -/* -------------------------- gl_once_t datatype -------------------------- */ - -static void -glthread_once_call (void *arg) -{ -  void (**gl_once_temp_addr) (void) = (void (**) (void)) arg; -  void (*initfunction) (void) = *gl_once_temp_addr; -  initfunction (); -} - -int -glthread_once_multithreaded (pth_once_t *once_control, void (*initfunction) (void)) -{ -  void (*temp) (void) = initfunction; -  return (!pth_once (once_control, glthread_once_call, &temp) ? errno : 0); -} - -int -glthread_once_singlethreaded (pth_once_t *once_control) -{ -  /* We know that pth_once_t is an integer type.  */ -  if (*once_control == PTH_ONCE_INIT) -    { -      /* First time use of once_control.  Invert the marker.  */ -      *once_control = ~ PTH_ONCE_INIT; -      return 1; -    } -  else -    return 0; -} - -#endif - -/* ========================================================================= */ - -#if USE_SOLARIS_THREADS - -/* Use the old Solaris threads library.  */ - -/* -------------------------- gl_lock_t datatype -------------------------- */ - -/* ------------------------- gl_rwlock_t datatype ------------------------- */ - -/* --------------------- gl_recursive_lock_t datatype --------------------- */ - -int -glthread_recursive_lock_init_multithreaded (gl_recursive_lock_t *lock) -{ -  int err; - -  err = mutex_init (&lock->mutex, USYNC_THREAD, NULL); -  if (err != 0) -    return err; -  lock->owner = (thread_t) 0; -  lock->depth = 0; -  return 0; -} - -int -glthread_recursive_lock_lock_multithreaded (gl_recursive_lock_t *lock) -{ -  thread_t self = thr_self (); -  if (lock->owner != self) -    { -      int err; - -      err = mutex_lock (&lock->mutex); -      if (err != 0) -        return err; -      lock->owner = self; -    } -  if (++(lock->depth) == 0) /* wraparound? */ -    { -      lock->depth--; -      return EAGAIN; -    } -  return 0; -} - -int -glthread_recursive_lock_unlock_multithreaded (gl_recursive_lock_t *lock) -{ -  if (lock->owner != thr_self ()) -    return EPERM; -  if (lock->depth == 0) -    return EINVAL; -  if (--(lock->depth) == 0) -    { -      lock->owner = (thread_t) 0; -      return mutex_unlock (&lock->mutex); -    } -  else -    return 0; -} - -int -glthread_recursive_lock_destroy_multithreaded (gl_recursive_lock_t *lock) -{ -  if (lock->owner != (thread_t) 0) -    return EBUSY; -  return mutex_destroy (&lock->mutex); -} - -/* -------------------------- gl_once_t datatype -------------------------- */ - -int -glthread_once_multithreaded (gl_once_t *once_control, void (*initfunction) (void)) -{ -  if (!once_control->inited) -    { -      int err; - -      /* Use the mutex to guarantee that if another thread is already calling -         the initfunction, this thread waits until it's finished.  */ -      err = mutex_lock (&once_control->mutex); -      if (err != 0) -        return err; -      if (!once_control->inited) -        { -          once_control->inited = 1; -          initfunction (); -        } -      return mutex_unlock (&once_control->mutex); -    } -  else -    return 0; -} - -int -glthread_once_singlethreaded (gl_once_t *once_control) -{ -  /* We know that gl_once_t contains an integer type.  */ -  if (!once_control->inited) -    { -      /* First time use of once_control.  Invert the marker.  */ -      once_control->inited = ~ 0; -      return 1; -    } -  else -    return 0; -} -  #endif  /* ========================================================================= */  #if USE_WINDOWS_THREADS -/* -------------------------- gl_lock_t datatype -------------------------- */ - -void -glthread_lock_init_func (gl_lock_t *lock) -{ -  InitializeCriticalSection (&lock->lock); -  lock->guard.done = 1; -} - -int -glthread_lock_lock_func (gl_lock_t *lock) -{ -  if (!lock->guard.done) -    { -      if (InterlockedIncrement (&lock->guard.started) == 0) -        /* This thread is the first one to need this lock.  Initialize it.  */ -        glthread_lock_init (lock); -      else -        /* Yield the CPU while waiting for another thread to finish -           initializing this lock.  */ -        while (!lock->guard.done) -          Sleep (0); -    } -  EnterCriticalSection (&lock->lock); -  return 0; -} - -int -glthread_lock_unlock_func (gl_lock_t *lock) -{ -  if (!lock->guard.done) -    return EINVAL; -  LeaveCriticalSection (&lock->lock); -  return 0; -} - -int -glthread_lock_destroy_func (gl_lock_t *lock) -{ -  if (!lock->guard.done) -    return EINVAL; -  DeleteCriticalSection (&lock->lock); -  lock->guard.done = 0; -  return 0; -} - -/* ------------------------- gl_rwlock_t datatype ------------------------- */ - -/* In this file, the waitqueues are implemented as circular arrays.  */ -#define gl_waitqueue_t gl_carray_waitqueue_t - -static void -gl_waitqueue_init (gl_waitqueue_t *wq) -{ -  wq->array = NULL; -  wq->count = 0; -  wq->alloc = 0; -  wq->offset = 0; -} - -/* Enqueues the current thread, represented by an event, in a wait queue. -   Returns INVALID_HANDLE_VALUE if an allocation failure occurs.  */ -static HANDLE -gl_waitqueue_add (gl_waitqueue_t *wq) -{ -  HANDLE event; -  unsigned int index; - -  if (wq->count == wq->alloc) -    { -      unsigned int new_alloc = 2 * wq->alloc + 1; -      HANDLE *new_array = -        (HANDLE *) realloc (wq->array, new_alloc * sizeof (HANDLE)); -      if (new_array == NULL) -        /* No more memory.  */ -        return INVALID_HANDLE_VALUE; -      /* Now is a good opportunity to rotate the array so that its contents -         starts at offset 0.  */ -      if (wq->offset > 0) -        { -          unsigned int old_count = wq->count; -          unsigned int old_alloc = wq->alloc; -          unsigned int old_offset = wq->offset; -          unsigned int i; -          if (old_offset + old_count > old_alloc) -            { -              unsigned int limit = old_offset + old_count - old_alloc; -              for (i = 0; i < limit; i++) -                new_array[old_alloc + i] = new_array[i]; -            } -          for (i = 0; i < old_count; i++) -            new_array[i] = new_array[old_offset + i]; -          wq->offset = 0; -        } -      wq->array = new_array; -      wq->alloc = new_alloc; -    } -  /* Whether the created event is a manual-reset one or an auto-reset one, -     does not matter, since we will wait on it only once.  */ -  event = CreateEvent (NULL, TRUE, FALSE, NULL); -  if (event == INVALID_HANDLE_VALUE) -    /* No way to allocate an event.  */ -    return INVALID_HANDLE_VALUE; -  index = wq->offset + wq->count; -  if (index >= wq->alloc) -    index -= wq->alloc; -  wq->array[index] = event; -  wq->count++; -  return event; -} - -/* Notifies the first thread from a wait queue and dequeues it.  */ -static void -gl_waitqueue_notify_first (gl_waitqueue_t *wq) -{ -  SetEvent (wq->array[wq->offset + 0]); -  wq->offset++; -  wq->count--; -  if (wq->count == 0 || wq->offset == wq->alloc) -    wq->offset = 0; -} - -/* Notifies all threads from a wait queue and dequeues them all.  */ -static void -gl_waitqueue_notify_all (gl_waitqueue_t *wq) -{ -  unsigned int i; - -  for (i = 0; i < wq->count; i++) -    { -      unsigned int index = wq->offset + i; -      if (index >= wq->alloc) -        index -= wq->alloc; -      SetEvent (wq->array[index]); -    } -  wq->count = 0; -  wq->offset = 0; -} - -void -glthread_rwlock_init_func (gl_rwlock_t *lock) -{ -  InitializeCriticalSection (&lock->lock); -  gl_waitqueue_init (&lock->waiting_readers); -  gl_waitqueue_init (&lock->waiting_writers); -  lock->runcount = 0; -  lock->guard.done = 1; -} - -int -glthread_rwlock_rdlock_func (gl_rwlock_t *lock) -{ -  if (!lock->guard.done) -    { -      if (InterlockedIncrement (&lock->guard.started) == 0) -        /* This thread is the first one to need this lock.  Initialize it.  */ -        glthread_rwlock_init (lock); -      else -        /* Yield the CPU while waiting for another thread to finish -           initializing this lock.  */ -        while (!lock->guard.done) -          Sleep (0); -    } -  EnterCriticalSection (&lock->lock); -  /* Test whether only readers are currently running, and whether the runcount -     field will not overflow, and whether no writer is waiting.  The latter -     condition is because POSIX recommends that "write locks shall take -     precedence over read locks", to avoid "writer starvation".  */ -  if (!(lock->runcount + 1 > 0 && lock->waiting_writers.count == 0)) -    { -      /* This thread has to wait for a while.  Enqueue it among the -         waiting_readers.  */ -      HANDLE event = gl_waitqueue_add (&lock->waiting_readers); -      if (event != INVALID_HANDLE_VALUE) -        { -          DWORD result; -          LeaveCriticalSection (&lock->lock); -          /* Wait until another thread signals this event.  */ -          result = WaitForSingleObject (event, INFINITE); -          if (result == WAIT_FAILED || result == WAIT_TIMEOUT) -            abort (); -          CloseHandle (event); -          /* The thread which signalled the event already did the bookkeeping: -             removed us from the waiting_readers, incremented lock->runcount.  */ -          if (!(lock->runcount > 0)) -            abort (); -          return 0; -        } -      else -        { -          /* Allocation failure.  Weird.  */ -          do -            { -              LeaveCriticalSection (&lock->lock); -              Sleep (1); -              EnterCriticalSection (&lock->lock); -            } -          while (!(lock->runcount + 1 > 0)); -        } -    } -  lock->runcount++; -  LeaveCriticalSection (&lock->lock); -  return 0; -} - -int -glthread_rwlock_wrlock_func (gl_rwlock_t *lock) -{ -  if (!lock->guard.done) -    { -      if (InterlockedIncrement (&lock->guard.started) == 0) -        /* This thread is the first one to need this lock.  Initialize it.  */ -        glthread_rwlock_init (lock); -      else -        /* Yield the CPU while waiting for another thread to finish -           initializing this lock.  */ -        while (!lock->guard.done) -          Sleep (0); -    } -  EnterCriticalSection (&lock->lock); -  /* Test whether no readers or writers are currently running.  */ -  if (!(lock->runcount == 0)) -    { -      /* This thread has to wait for a while.  Enqueue it among the -         waiting_writers.  */ -      HANDLE event = gl_waitqueue_add (&lock->waiting_writers); -      if (event != INVALID_HANDLE_VALUE) -        { -          DWORD result; -          LeaveCriticalSection (&lock->lock); -          /* Wait until another thread signals this event.  */ -          result = WaitForSingleObject (event, INFINITE); -          if (result == WAIT_FAILED || result == WAIT_TIMEOUT) -            abort (); -          CloseHandle (event); -          /* The thread which signalled the event already did the bookkeeping: -             removed us from the waiting_writers, set lock->runcount = -1.  */ -          if (!(lock->runcount == -1)) -            abort (); -          return 0; -        } -      else -        { -          /* Allocation failure.  Weird.  */ -          do -            { -              LeaveCriticalSection (&lock->lock); -              Sleep (1); -              EnterCriticalSection (&lock->lock); -            } -          while (!(lock->runcount == 0)); -        } -    } -  lock->runcount--; /* runcount becomes -1 */ -  LeaveCriticalSection (&lock->lock); -  return 0; -} - -int -glthread_rwlock_unlock_func (gl_rwlock_t *lock) -{ -  if (!lock->guard.done) -    return EINVAL; -  EnterCriticalSection (&lock->lock); -  if (lock->runcount < 0) -    { -      /* Drop a writer lock.  */ -      if (!(lock->runcount == -1)) -        abort (); -      lock->runcount = 0; -    } -  else -    { -      /* Drop a reader lock.  */ -      if (!(lock->runcount > 0)) -        { -          LeaveCriticalSection (&lock->lock); -          return EPERM; -        } -      lock->runcount--; -    } -  if (lock->runcount == 0) -    { -      /* POSIX recommends that "write locks shall take precedence over read -         locks", to avoid "writer starvation".  */ -      if (lock->waiting_writers.count > 0) -        { -          /* Wake up one of the waiting writers.  */ -          lock->runcount--; -          gl_waitqueue_notify_first (&lock->waiting_writers); -        } -      else -        { -          /* Wake up all waiting readers.  */ -          lock->runcount += lock->waiting_readers.count; -          gl_waitqueue_notify_all (&lock->waiting_readers); -        } -    } -  LeaveCriticalSection (&lock->lock); -  return 0; -} - -int -glthread_rwlock_destroy_func (gl_rwlock_t *lock) -{ -  if (!lock->guard.done) -    return EINVAL; -  if (lock->runcount != 0) -    return EBUSY; -  DeleteCriticalSection (&lock->lock); -  if (lock->waiting_readers.array != NULL) -    free (lock->waiting_readers.array); -  if (lock->waiting_writers.array != NULL) -    free (lock->waiting_writers.array); -  lock->guard.done = 0; -  return 0; -} - -/* --------------------- gl_recursive_lock_t datatype --------------------- */ - -void -glthread_recursive_lock_init_func (gl_recursive_lock_t *lock) -{ -  lock->owner = 0; -  lock->depth = 0; -  InitializeCriticalSection (&lock->lock); -  lock->guard.done = 1; -} - -int -glthread_recursive_lock_lock_func (gl_recursive_lock_t *lock) -{ -  if (!lock->guard.done) -    { -      if (InterlockedIncrement (&lock->guard.started) == 0) -        /* This thread is the first one to need this lock.  Initialize it.  */ -        glthread_recursive_lock_init (lock); -      else -        /* Yield the CPU while waiting for another thread to finish -           initializing this lock.  */ -        while (!lock->guard.done) -          Sleep (0); -    } -  { -    DWORD self = GetCurrentThreadId (); -    if (lock->owner != self) -      { -        EnterCriticalSection (&lock->lock); -        lock->owner = self; -      } -    if (++(lock->depth) == 0) /* wraparound? */ -      { -        lock->depth--; -        return EAGAIN; -      } -  } -  return 0; -} - -int -glthread_recursive_lock_unlock_func (gl_recursive_lock_t *lock) -{ -  if (lock->owner != GetCurrentThreadId ()) -    return EPERM; -  if (lock->depth == 0) -    return EINVAL; -  if (--(lock->depth) == 0) -    { -      lock->owner = 0; -      LeaveCriticalSection (&lock->lock); -    } -  return 0; -} - -int -glthread_recursive_lock_destroy_func (gl_recursive_lock_t *lock) -{ -  if (lock->owner != 0) -    return EBUSY; -  DeleteCriticalSection (&lock->lock); -  lock->guard.done = 0; -  return 0; -} - -/* -------------------------- gl_once_t datatype -------------------------- */ - -void -glthread_once_func (gl_once_t *once_control, void (*initfunction) (void)) -{ -  if (once_control->inited <= 0) -    { -      if (InterlockedIncrement (&once_control->started) == 0) -        { -          /* This thread is the first one to come to this once_control.  */ -          InitializeCriticalSection (&once_control->lock); -          EnterCriticalSection (&once_control->lock); -          once_control->inited = 0; -          initfunction (); -          once_control->inited = 1; -          LeaveCriticalSection (&once_control->lock); -        } -      else -        { -          /* Undo last operation.  */ -          InterlockedDecrement (&once_control->started); -          /* Some other thread has already started the initialization. -             Yield the CPU while waiting for the other thread to finish -             initializing and taking the lock.  */ -          while (once_control->inited < 0) -            Sleep (0); -          if (once_control->inited <= 0) -            { -              /* Take the lock.  This blocks until the other thread has -                 finished calling the initfunction.  */ -              EnterCriticalSection (&once_control->lock); -              LeaveCriticalSection (&once_control->lock); -              if (!(once_control->inited > 0)) -                abort (); -            } -        } -    } -} -  #endif  /* ========================================================================= */ diff --git a/lib/glthread/lock.h b/lib/glthread/lock.h index 2d8d5abb..47eed8fe 100644 --- a/lib/glthread/lock.h +++ b/lib/glthread/lock.h @@ -1,31 +1,21 @@  /* Locking in multithreaded situations. -   Copyright (C) 2005-2018 Free Software Foundation, Inc. +   Copyright (C) 2005-2022 Free Software Foundation, Inc. -   This program is free software: you can redistribute it and/or -   modify it under the terms of either: +   This file is free software: you can redistribute it and/or modify +   it under the terms of the GNU Lesser General Public License as +   published by the Free Software Foundation; either version 2.1 of the +   License, or (at your option) any later version. -     * the GNU Lesser General Public License as published by the Free -       Software Foundation; either version 3 of the License, or (at your -       option) any later version. - -   or - -     * the GNU General Public License as published by the Free -       Software Foundation; either version 2 of the License, or (at your -       option) any later version. - -   or both in parallel, as here. -   This program is distributed in the hope that it will be useful, +   This file is distributed in the hope that it will be useful,     but WITHOUT ANY WARRANTY; without even the implied warranty of     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the -   GNU General Public License for more details. +   GNU Lesser General Public License for more details. -   You should have received a copy of the GNU General Public License -   along with this program; if not, see <https://www.gnu.org/licenses/>.  */ +   You should have received a copy of the GNU Lesser General Public License +   along with this program.  If not, see <https://www.gnu.org/licenses/>.  */  /* Written by Bruno Haible <bruno@clisp.org>, 2005. -   Based on GCC's gthr-posix.h, gthr-posix95.h, gthr-solaris.h, -   gthr-win32.h.  */ +   Based on GCC's gthr-posix.h, gthr-posix95.h, gthr-win32.h.  */  /* This file contains locking primitives for use with a given thread library.     It does not contain primitives for creating threads or for other @@ -90,6 +80,127 @@  #include <errno.h>  #include <stdlib.h> +#if !defined c11_threads_in_use +# if HAVE_THREADS_H && USE_POSIX_THREADS_FROM_LIBC +#  define c11_threads_in_use() 1 +# elif HAVE_THREADS_H && USE_POSIX_THREADS_WEAK +#  include <threads.h> +#  pragma weak thrd_exit +#  define c11_threads_in_use() (thrd_exit != NULL) +# else +#  define c11_threads_in_use() 0 +# endif +#endif + +/* ========================================================================= */ + +#if USE_ISOC_THREADS || USE_ISOC_AND_POSIX_THREADS + +/* Use the ISO C threads library.  */ + +# include <threads.h> + +# ifdef __cplusplus +extern "C" { +# endif + +/* -------------------------- gl_lock_t datatype -------------------------- */ + +typedef struct +        { +          int volatile init_needed; +          once_flag init_once; +          void (*init_func) (void); +          mtx_t mutex; +        } +        gl_lock_t; +# define gl_lock_define(STORAGECLASS, NAME) \ +    STORAGECLASS gl_lock_t NAME; +# define gl_lock_define_initialized(STORAGECLASS, NAME) \ +    static void _atomic_init_##NAME (void);       \ +    STORAGECLASS gl_lock_t NAME =                 \ +      { 1, ONCE_FLAG_INIT, _atomic_init_##NAME }; \ +    static void _atomic_init_##NAME (void)        \ +    {                                             \ +      if (glthread_lock_init (&(NAME)))           \ +        abort ();                                 \ +    } +extern int glthread_lock_init (gl_lock_t *lock); +extern int glthread_lock_lock (gl_lock_t *lock); +extern int glthread_lock_unlock (gl_lock_t *lock); +extern int glthread_lock_destroy (gl_lock_t *lock); + +/* ------------------------- gl_rwlock_t datatype ------------------------- */ + +typedef struct +        { +          int volatile init_needed; +          once_flag init_once; +          void (*init_func) (void); +          mtx_t lock; /* protects the remaining fields */ +          cnd_t waiting_readers; /* waiting readers */ +          cnd_t waiting_writers; /* waiting writers */ +          unsigned int waiting_writers_count; /* number of waiting writers */ +          int runcount; /* number of readers running, or -1 when a writer runs */ +        } +        gl_rwlock_t; +# define gl_rwlock_define(STORAGECLASS, NAME) \ +    STORAGECLASS gl_rwlock_t NAME; +# define gl_rwlock_define_initialized(STORAGECLASS, NAME) \ +    static void _atomic_init_##NAME (void);       \ +    STORAGECLASS gl_rwlock_t NAME =               \ +      { 1, ONCE_FLAG_INIT, _atomic_init_##NAME }; \ +    static void _atomic_init_##NAME (void)        \ +    {                                             \ +      if (glthread_rwlock_init (&(NAME)))         \ +        abort ();                                 \ +    } +extern int glthread_rwlock_init (gl_rwlock_t *lock); +extern int glthread_rwlock_rdlock (gl_rwlock_t *lock); +extern int glthread_rwlock_wrlock (gl_rwlock_t *lock); +extern int glthread_rwlock_unlock (gl_rwlock_t *lock); +extern int glthread_rwlock_destroy (gl_rwlock_t *lock); + +/* --------------------- gl_recursive_lock_t datatype --------------------- */ + +typedef struct +        { +          int volatile init_needed; +          once_flag init_once; +          void (*init_func) (void); +          mtx_t mutex; +        } +        gl_recursive_lock_t; +# define gl_recursive_lock_define(STORAGECLASS, NAME) \ +    STORAGECLASS gl_recursive_lock_t NAME; +# define gl_recursive_lock_define_initialized(STORAGECLASS, NAME) \ +    static void _atomic_init_##NAME (void);       \ +    STORAGECLASS gl_recursive_lock_t NAME =       \ +      { 1, ONCE_FLAG_INIT, _atomic_init_##NAME }; \ +    static void _atomic_init_##NAME (void)        \ +    {                                             \ +      if (glthread_recursive_lock_init (&(NAME))) \ +        abort ();                                 \ +    } +extern int glthread_recursive_lock_init (gl_recursive_lock_t *lock); +extern int glthread_recursive_lock_lock (gl_recursive_lock_t *lock); +extern int glthread_recursive_lock_unlock (gl_recursive_lock_t *lock); +extern int glthread_recursive_lock_destroy (gl_recursive_lock_t *lock); + +/* -------------------------- gl_once_t datatype -------------------------- */ + +typedef once_flag gl_once_t; +# define gl_once_define(STORAGECLASS, NAME) \ +    STORAGECLASS once_flag NAME = ONCE_FLAG_INIT; +# define glthread_once(ONCE_CONTROL, INITFUNCTION) \ +    (call_once (ONCE_CONTROL, INITFUNCTION), 0) + +# ifdef __cplusplus +} +# endif + +#endif +  /* ========================================================================= */  #if USE_POSIX_THREADS @@ -165,7 +276,8 @@ extern int glthread_in_use (void);           pthread_rwlockattr_init       */  #   pragma weak pthread_mutexattr_gettype -#   define pthread_in_use() (pthread_mutexattr_gettype != NULL) +#   define pthread_in_use() \ +      (pthread_mutexattr_gettype != NULL || c11_threads_in_use ())  #  endif  # else @@ -198,7 +310,7 @@ typedef pthread_mutex_t gl_lock_t;  # if HAVE_PTHREAD_RWLOCK && (HAVE_PTHREAD_RWLOCK_RDLOCK_PREFER_WRITER || (defined PTHREAD_RWLOCK_WRITER_NONRECURSIVE_INITIALIZER_NP && (__GNU_LIBRARY__ > 1))) -#  ifdef PTHREAD_RWLOCK_INITIALIZER +#  if defined PTHREAD_RWLOCK_INITIALIZER || defined PTHREAD_RWLOCK_INITIALIZER_NP  typedef pthread_rwlock_t gl_rwlock_t;  #   define gl_rwlock_define(STORAGECLASS, NAME) \ @@ -206,8 +318,13 @@ typedef pthread_rwlock_t gl_rwlock_t;  #   define gl_rwlock_define_initialized(STORAGECLASS, NAME) \        STORAGECLASS pthread_rwlock_t NAME = gl_rwlock_initializer;  #   if HAVE_PTHREAD_RWLOCK_RDLOCK_PREFER_WRITER -#    define gl_rwlock_initializer \ -       PTHREAD_RWLOCK_INITIALIZER +#    if defined PTHREAD_RWLOCK_INITIALIZER +#     define gl_rwlock_initializer \ +        PTHREAD_RWLOCK_INITIALIZER +#    else +#     define gl_rwlock_initializer \ +        PTHREAD_RWLOCK_INITIALIZER_NP +#    endif  #    define glthread_rwlock_init(LOCK) \         (pthread_in_use () ? pthread_rwlock_init (LOCK, NULL) : 0)  #   else /* glibc with bug https://sourceware.org/bugzilla/show_bug.cgi?id=13701 */ @@ -390,287 +507,20 @@ extern int glthread_recursive_lock_destroy_multithreaded (gl_recursive_lock_t *l  typedef pthread_once_t gl_once_t;  # define gl_once_define(STORAGECLASS, NAME) \      STORAGECLASS pthread_once_t NAME = PTHREAD_ONCE_INIT; -# define glthread_once(ONCE_CONTROL, INITFUNCTION) \ -    (pthread_in_use ()                                                         \ -     ? pthread_once (ONCE_CONTROL, INITFUNCTION)                               \ -     : (glthread_once_singlethreaded (ONCE_CONTROL) ? (INITFUNCTION (), 0) : 0)) -extern int glthread_once_singlethreaded (pthread_once_t *once_control); - -# ifdef __cplusplus -} -# endif - -#endif - -/* ========================================================================= */ - -#if USE_PTH_THREADS - -/* Use the GNU Pth threads library.  */ - -# include <pth.h> - -# ifdef __cplusplus -extern "C" { -# endif - -# if USE_PTH_THREADS_WEAK - -/* Use weak references to the GNU Pth threads library.  */ - -#  pragma weak pth_mutex_init -#  pragma weak pth_mutex_acquire -#  pragma weak pth_mutex_release -#  pragma weak pth_rwlock_init -#  pragma weak pth_rwlock_acquire -#  pragma weak pth_rwlock_release -#  pragma weak pth_once - -#  pragma weak pth_cancel -#  define pth_in_use() (pth_cancel != NULL) - -# else - -#  define pth_in_use() 1 - -# endif - -/* -------------------------- gl_lock_t datatype -------------------------- */ - -typedef pth_mutex_t gl_lock_t; -# define gl_lock_define(STORAGECLASS, NAME) \ -    STORAGECLASS pth_mutex_t NAME; -# define gl_lock_define_initialized(STORAGECLASS, NAME) \ -    STORAGECLASS pth_mutex_t NAME = gl_lock_initializer; -# define gl_lock_initializer \ -    PTH_MUTEX_INIT -# define glthread_lock_init(LOCK) \ -    (pth_in_use () && !pth_mutex_init (LOCK) ? errno : 0) -# define glthread_lock_lock(LOCK) \ -    (pth_in_use () && !pth_mutex_acquire (LOCK, 0, NULL) ? errno : 0) -# define glthread_lock_unlock(LOCK) \ -    (pth_in_use () && !pth_mutex_release (LOCK) ? errno : 0) -# define glthread_lock_destroy(LOCK) \ -    ((void)(LOCK), 0) - -/* ------------------------- gl_rwlock_t datatype ------------------------- */ - -/* Pth pth_rwlock_acquire always prefers readers.  No autoconf test so far.  */ -# if HAVE_PTH_RWLOCK_ACQUIRE_PREFER_WRITER - -typedef pth_rwlock_t gl_rwlock_t; -#  define gl_rwlock_define(STORAGECLASS, NAME) \ -     STORAGECLASS pth_rwlock_t NAME; -#  define gl_rwlock_define_initialized(STORAGECLASS, NAME) \ -     STORAGECLASS pth_rwlock_t NAME = gl_rwlock_initializer; -#  define gl_rwlock_initializer \ -     PTH_RWLOCK_INIT -#  define glthread_rwlock_init(LOCK) \ -     (pth_in_use () && !pth_rwlock_init (LOCK) ? errno : 0) -#  define glthread_rwlock_rdlock(LOCK) \ -     (pth_in_use () && !pth_rwlock_acquire (LOCK, PTH_RWLOCK_RD, 0, NULL) ? errno : 0) -#  define glthread_rwlock_wrlock(LOCK) \ -     (pth_in_use () && !pth_rwlock_acquire (LOCK, PTH_RWLOCK_RW, 0, NULL) ? errno : 0) -#  define glthread_rwlock_unlock(LOCK) \ -     (pth_in_use () && !pth_rwlock_release (LOCK) ? errno : 0) -#  define glthread_rwlock_destroy(LOCK) \ -     ((void)(LOCK), 0) - -# else - -typedef struct -        { -          int initialized; -          pth_mutex_t lock; /* protects the remaining fields */ -          pth_cond_t waiting_readers; /* waiting readers */ -          pth_cond_t waiting_writers; /* waiting writers */ -          unsigned int waiting_writers_count; /* number of waiting writers */ -          int runcount; /* number of readers running, or -1 when a writer runs */ -        } -        gl_rwlock_t; -#  define gl_rwlock_define(STORAGECLASS, NAME) \ -     STORAGECLASS gl_rwlock_t NAME; -#  define gl_rwlock_define_initialized(STORAGECLASS, NAME) \ -     STORAGECLASS gl_rwlock_t NAME = gl_rwlock_initializer; -#  define gl_rwlock_initializer \ -     { 0 } -#  define glthread_rwlock_init(LOCK) \ -     (pth_in_use () ? glthread_rwlock_init_multithreaded (LOCK) : 0) -#  define glthread_rwlock_rdlock(LOCK) \ -     (pth_in_use () ? glthread_rwlock_rdlock_multithreaded (LOCK) : 0) -#  define glthread_rwlock_wrlock(LOCK) \ -     (pth_in_use () ? glthread_rwlock_wrlock_multithreaded (LOCK) : 0) -#  define glthread_rwlock_unlock(LOCK) \ -     (pth_in_use () ? glthread_rwlock_unlock_multithreaded (LOCK) : 0) -#  define glthread_rwlock_destroy(LOCK) \ -     (pth_in_use () ? glthread_rwlock_destroy_multithreaded (LOCK) : 0) -extern int glthread_rwlock_init_multithreaded (gl_rwlock_t *lock); -extern int glthread_rwlock_rdlock_multithreaded (gl_rwlock_t *lock); -extern int glthread_rwlock_wrlock_multithreaded (gl_rwlock_t *lock); -extern int glthread_rwlock_unlock_multithreaded (gl_rwlock_t *lock); -extern int glthread_rwlock_destroy_multithreaded (gl_rwlock_t *lock); - -# endif - -/* --------------------- gl_recursive_lock_t datatype --------------------- */ - -/* In Pth, mutexes are recursive by default.  */ -typedef pth_mutex_t gl_recursive_lock_t; -#  define gl_recursive_lock_define(STORAGECLASS, NAME) \ -     STORAGECLASS pth_mutex_t NAME; -#  define gl_recursive_lock_define_initialized(STORAGECLASS, NAME) \ -     STORAGECLASS pth_mutex_t NAME = gl_recursive_lock_initializer; -#  define gl_recursive_lock_initializer \ -     PTH_MUTEX_INIT -#  define glthread_recursive_lock_init(LOCK) \ -     (pth_in_use () && !pth_mutex_init (LOCK) ? errno : 0) -#  define glthread_recursive_lock_lock(LOCK) \ -     (pth_in_use () && !pth_mutex_acquire (LOCK, 0, NULL) ? errno : 0) -#  define glthread_recursive_lock_unlock(LOCK) \ -     (pth_in_use () && !pth_mutex_release (LOCK) ? errno : 0) -#  define glthread_recursive_lock_destroy(LOCK) \ -     ((void)(LOCK), 0) - -/* -------------------------- gl_once_t datatype -------------------------- */ - -typedef pth_once_t gl_once_t; -# define gl_once_define(STORAGECLASS, NAME) \ -    STORAGECLASS pth_once_t NAME = PTH_ONCE_INIT; -# define glthread_once(ONCE_CONTROL, INITFUNCTION) \ -    (pth_in_use ()                                                             \ -     ? glthread_once_multithreaded (ONCE_CONTROL, INITFUNCTION)                \ -     : (glthread_once_singlethreaded (ONCE_CONTROL) ? (INITFUNCTION (), 0) : 0)) -extern int glthread_once_multithreaded (pth_once_t *once_control, void (*initfunction) (void)); -extern int glthread_once_singlethreaded (pth_once_t *once_control); - -# ifdef __cplusplus -} -# endif - -#endif - -/* ========================================================================= */ - -#if USE_SOLARIS_THREADS - -/* Use the old Solaris threads library.  */ - -# include <thread.h> -# include <synch.h> - -# ifdef __cplusplus -extern "C" { -# endif - -# if USE_SOLARIS_THREADS_WEAK - -/* Use weak references to the old Solaris threads library.  */ - -#  pragma weak mutex_init -#  pragma weak mutex_lock -#  pragma weak mutex_unlock -#  pragma weak mutex_destroy -#  pragma weak rwlock_init -#  pragma weak rw_rdlock -#  pragma weak rw_wrlock -#  pragma weak rw_unlock -#  pragma weak rwlock_destroy -#  pragma weak thr_self - -#  pragma weak thr_suspend -#  define thread_in_use() (thr_suspend != NULL) - +# if PTHREAD_IN_USE_DETECTION_HARD || USE_POSIX_THREADS_WEAK +#  define glthread_once(ONCE_CONTROL, INITFUNCTION) \ +     (pthread_in_use ()                                                        \ +      ? pthread_once (ONCE_CONTROL, INITFUNCTION)                              \ +      : (glthread_once_singlethreaded (ONCE_CONTROL) ? (INITFUNCTION (), 0) : 0))  # else - -#  define thread_in_use() 1 - +#  define glthread_once(ONCE_CONTROL, INITFUNCTION) \ +     (pthread_in_use ()                                                        \ +      ? glthread_once_multithreaded (ONCE_CONTROL, INITFUNCTION)               \ +      : (glthread_once_singlethreaded (ONCE_CONTROL) ? (INITFUNCTION (), 0) : 0)) +extern int glthread_once_multithreaded (pthread_once_t *once_control, +                                        void (*init_function) (void));  # endif - -/* -------------------------- gl_lock_t datatype -------------------------- */ - -typedef mutex_t gl_lock_t; -# define gl_lock_define(STORAGECLASS, NAME) \ -    STORAGECLASS mutex_t NAME; -# define gl_lock_define_initialized(STORAGECLASS, NAME) \ -    STORAGECLASS mutex_t NAME = gl_lock_initializer; -# define gl_lock_initializer \ -    DEFAULTMUTEX -# define glthread_lock_init(LOCK) \ -    (thread_in_use () ? mutex_init (LOCK, USYNC_THREAD, NULL) : 0) -# define glthread_lock_lock(LOCK) \ -    (thread_in_use () ? mutex_lock (LOCK) : 0) -# define glthread_lock_unlock(LOCK) \ -    (thread_in_use () ? mutex_unlock (LOCK) : 0) -# define glthread_lock_destroy(LOCK) \ -    (thread_in_use () ? mutex_destroy (LOCK) : 0) - -/* ------------------------- gl_rwlock_t datatype ------------------------- */ - -typedef rwlock_t gl_rwlock_t; -# define gl_rwlock_define(STORAGECLASS, NAME) \ -    STORAGECLASS rwlock_t NAME; -# define gl_rwlock_define_initialized(STORAGECLASS, NAME) \ -    STORAGECLASS rwlock_t NAME = gl_rwlock_initializer; -# define gl_rwlock_initializer \ -    DEFAULTRWLOCK -# define glthread_rwlock_init(LOCK) \ -    (thread_in_use () ? rwlock_init (LOCK, USYNC_THREAD, NULL) : 0) -# define glthread_rwlock_rdlock(LOCK) \ -    (thread_in_use () ? rw_rdlock (LOCK) : 0) -# define glthread_rwlock_wrlock(LOCK) \ -    (thread_in_use () ? rw_wrlock (LOCK) : 0) -# define glthread_rwlock_unlock(LOCK) \ -    (thread_in_use () ? rw_unlock (LOCK) : 0) -# define glthread_rwlock_destroy(LOCK) \ -    (thread_in_use () ? rwlock_destroy (LOCK) : 0) - -/* --------------------- gl_recursive_lock_t datatype --------------------- */ - -/* Old Solaris threads did not have recursive locks. -   We have to implement them ourselves.  */ - -typedef struct -        { -          mutex_t mutex; -          thread_t owner; -          unsigned long depth; -        } -        gl_recursive_lock_t; -# define gl_recursive_lock_define(STORAGECLASS, NAME) \ -    STORAGECLASS gl_recursive_lock_t NAME; -# define gl_recursive_lock_define_initialized(STORAGECLASS, NAME) \ -    STORAGECLASS gl_recursive_lock_t NAME = gl_recursive_lock_initializer; -# define gl_recursive_lock_initializer \ -    { DEFAULTMUTEX, (thread_t) 0, 0 } -# define glthread_recursive_lock_init(LOCK) \ -    (thread_in_use () ? glthread_recursive_lock_init_multithreaded (LOCK) : 0) -# define glthread_recursive_lock_lock(LOCK) \ -    (thread_in_use () ? glthread_recursive_lock_lock_multithreaded (LOCK) : 0) -# define glthread_recursive_lock_unlock(LOCK) \ -    (thread_in_use () ? glthread_recursive_lock_unlock_multithreaded (LOCK) : 0) -# define glthread_recursive_lock_destroy(LOCK) \ -    (thread_in_use () ? glthread_recursive_lock_destroy_multithreaded (LOCK) : 0) -extern int glthread_recursive_lock_init_multithreaded (gl_recursive_lock_t *lock); -extern int glthread_recursive_lock_lock_multithreaded (gl_recursive_lock_t *lock); -extern int glthread_recursive_lock_unlock_multithreaded (gl_recursive_lock_t *lock); -extern int glthread_recursive_lock_destroy_multithreaded (gl_recursive_lock_t *lock); - -/* -------------------------- gl_once_t datatype -------------------------- */ - -typedef struct -        { -          volatile int inited; -          mutex_t mutex; -        } -        gl_once_t; -# define gl_once_define(STORAGECLASS, NAME) \ -    STORAGECLASS gl_once_t NAME = { 0, DEFAULTMUTEX }; -# define glthread_once(ONCE_CONTROL, INITFUNCTION) \ -    (thread_in_use ()                                                          \ -     ? glthread_once_multithreaded (ONCE_CONTROL, INITFUNCTION)                \ -     : (glthread_once_singlethreaded (ONCE_CONTROL) ? (INITFUNCTION (), 0) : 0)) -extern int glthread_once_multithreaded (gl_once_t *once_control, void (*initfunction) (void)); -extern int glthread_once_singlethreaded (gl_once_t *once_control); +extern int glthread_once_singlethreaded (pthread_once_t *once_control);  # ifdef __cplusplus  } @@ -685,6 +535,11 @@ extern int glthread_once_singlethreaded (gl_once_t *once_control);  # define WIN32_LEAN_AND_MEAN  /* avoid including junk */  # include <windows.h> +# include "windows-mutex.h" +# include "windows-rwlock.h" +# include "windows-recmutex.h" +# include "windows-once.h" +  # ifdef __cplusplus  extern "C" {  # endif @@ -700,127 +555,69 @@ extern "C" {  /* There is no way to statically initialize a CRITICAL_SECTION.  It needs     to be done lazily, once only.  For this we need spinlocks.  */ -typedef struct { volatile int done; volatile long started; } gl_spinlock_t; -  /* -------------------------- gl_lock_t datatype -------------------------- */ -typedef struct -        { -          gl_spinlock_t guard; /* protects the initialization */ -          CRITICAL_SECTION lock; -        } -        gl_lock_t; +typedef glwthread_mutex_t gl_lock_t;  # define gl_lock_define(STORAGECLASS, NAME) \      STORAGECLASS gl_lock_t NAME;  # define gl_lock_define_initialized(STORAGECLASS, NAME) \      STORAGECLASS gl_lock_t NAME = gl_lock_initializer;  # define gl_lock_initializer \ -    { { 0, -1 } } +    GLWTHREAD_MUTEX_INIT  # define glthread_lock_init(LOCK) \ -    (glthread_lock_init_func (LOCK), 0) +    (glwthread_mutex_init (LOCK), 0)  # define glthread_lock_lock(LOCK) \ -    glthread_lock_lock_func (LOCK) +    glwthread_mutex_lock (LOCK)  # define glthread_lock_unlock(LOCK) \ -    glthread_lock_unlock_func (LOCK) +    glwthread_mutex_unlock (LOCK)  # define glthread_lock_destroy(LOCK) \ -    glthread_lock_destroy_func (LOCK) -extern void glthread_lock_init_func (gl_lock_t *lock); -extern int glthread_lock_lock_func (gl_lock_t *lock); -extern int glthread_lock_unlock_func (gl_lock_t *lock); -extern int glthread_lock_destroy_func (gl_lock_t *lock); +    glwthread_mutex_destroy (LOCK)  /* ------------------------- gl_rwlock_t datatype ------------------------- */ -/* It is impossible to implement read-write locks using plain locks, without -   introducing an extra thread dedicated to managing read-write locks. -   Therefore here we need to use the low-level Event type.  */ - -typedef struct -        { -          HANDLE *array; /* array of waiting threads, each represented by an event */ -          unsigned int count; /* number of waiting threads */ -          unsigned int alloc; /* length of allocated array */ -          unsigned int offset; /* index of first waiting thread in array */ -        } -        gl_carray_waitqueue_t; -typedef struct -        { -          gl_spinlock_t guard; /* protects the initialization */ -          CRITICAL_SECTION lock; /* protects the remaining fields */ -          gl_carray_waitqueue_t waiting_readers; /* waiting readers */ -          gl_carray_waitqueue_t waiting_writers; /* waiting writers */ -          int runcount; /* number of readers running, or -1 when a writer runs */ -        } -        gl_rwlock_t; +typedef glwthread_rwlock_t gl_rwlock_t;  # define gl_rwlock_define(STORAGECLASS, NAME) \      STORAGECLASS gl_rwlock_t NAME;  # define gl_rwlock_define_initialized(STORAGECLASS, NAME) \      STORAGECLASS gl_rwlock_t NAME = gl_rwlock_initializer;  # define gl_rwlock_initializer \ -    { { 0, -1 } } +    GLWTHREAD_RWLOCK_INIT  # define glthread_rwlock_init(LOCK) \ -    (glthread_rwlock_init_func (LOCK), 0) +    (glwthread_rwlock_init (LOCK), 0)  # define glthread_rwlock_rdlock(LOCK) \ -    glthread_rwlock_rdlock_func (LOCK) +    glwthread_rwlock_rdlock (LOCK)  # define glthread_rwlock_wrlock(LOCK) \ -    glthread_rwlock_wrlock_func (LOCK) +    glwthread_rwlock_wrlock (LOCK)  # define glthread_rwlock_unlock(LOCK) \ -    glthread_rwlock_unlock_func (LOCK) +    glwthread_rwlock_unlock (LOCK)  # define glthread_rwlock_destroy(LOCK) \ -    glthread_rwlock_destroy_func (LOCK) -extern void glthread_rwlock_init_func (gl_rwlock_t *lock); -extern int glthread_rwlock_rdlock_func (gl_rwlock_t *lock); -extern int glthread_rwlock_wrlock_func (gl_rwlock_t *lock); -extern int glthread_rwlock_unlock_func (gl_rwlock_t *lock); -extern int glthread_rwlock_destroy_func (gl_rwlock_t *lock); +    glwthread_rwlock_destroy (LOCK)  /* --------------------- gl_recursive_lock_t datatype --------------------- */ -/* The native Windows documentation says that CRITICAL_SECTION already -   implements a recursive lock.  But we need not rely on it: It's easy to -   implement a recursive lock without this assumption.  */ - -typedef struct -        { -          gl_spinlock_t guard; /* protects the initialization */ -          DWORD owner; -          unsigned long depth; -          CRITICAL_SECTION lock; -        } -        gl_recursive_lock_t; +typedef glwthread_recmutex_t gl_recursive_lock_t;  # define gl_recursive_lock_define(STORAGECLASS, NAME) \      STORAGECLASS gl_recursive_lock_t NAME;  # define gl_recursive_lock_define_initialized(STORAGECLASS, NAME) \      STORAGECLASS gl_recursive_lock_t NAME = gl_recursive_lock_initializer;  # define gl_recursive_lock_initializer \ -    { { 0, -1 }, 0, 0 } +    GLWTHREAD_RECMUTEX_INIT  # define glthread_recursive_lock_init(LOCK) \ -    (glthread_recursive_lock_init_func (LOCK), 0) +    (glwthread_recmutex_init (LOCK), 0)  # define glthread_recursive_lock_lock(LOCK) \ -    glthread_recursive_lock_lock_func (LOCK) +    glwthread_recmutex_lock (LOCK)  # define glthread_recursive_lock_unlock(LOCK) \ -    glthread_recursive_lock_unlock_func (LOCK) +    glwthread_recmutex_unlock (LOCK)  # define glthread_recursive_lock_destroy(LOCK) \ -    glthread_recursive_lock_destroy_func (LOCK) -extern void glthread_recursive_lock_init_func (gl_recursive_lock_t *lock); -extern int glthread_recursive_lock_lock_func (gl_recursive_lock_t *lock); -extern int glthread_recursive_lock_unlock_func (gl_recursive_lock_t *lock); -extern int glthread_recursive_lock_destroy_func (gl_recursive_lock_t *lock); +    glwthread_recmutex_destroy (LOCK)  /* -------------------------- gl_once_t datatype -------------------------- */ -typedef struct -        { -          volatile int inited; -          volatile long started; -          CRITICAL_SECTION lock; -        } -        gl_once_t; +typedef glwthread_once_t gl_once_t;  # define gl_once_define(STORAGECLASS, NAME) \ -    STORAGECLASS gl_once_t NAME = { -1, -1 }; +    STORAGECLASS gl_once_t NAME = GLWTHREAD_ONCE_INIT;  # define glthread_once(ONCE_CONTROL, INITFUNCTION) \ -    (glthread_once_func (ONCE_CONTROL, INITFUNCTION), 0) -extern void glthread_once_func (gl_once_t *once_control, void (*initfunction) (void)); +    (glwthread_once (ONCE_CONTROL, INITFUNCTION), 0)  # ifdef __cplusplus  } @@ -830,7 +627,7 @@ extern void glthread_once_func (gl_once_t *once_control, void (*initfunction) (v  /* ========================================================================= */ -#if !(USE_POSIX_THREADS || USE_PTH_THREADS || USE_SOLARIS_THREADS || USE_WINDOWS_THREADS) +#if !(USE_ISOC_THREADS || USE_POSIX_THREADS || USE_ISOC_AND_POSIX_THREADS || USE_WINDOWS_THREADS)  /* Provide dummy implementation if threads are not supported.  */ diff --git a/lib/glthread/threadlib.c b/lib/glthread/threadlib.c index 3534b61e..88a76bad 100644 --- a/lib/glthread/threadlib.c +++ b/lib/glthread/threadlib.c @@ -1,27 +1,18 @@  /* Multithreading primitives. -   Copyright (C) 2005-2018 Free Software Foundation, Inc. +   Copyright (C) 2005-2022 Free Software Foundation, Inc. -   This program is free software: you can redistribute it and/or -   modify it under the terms of either: +   This file is free software: you can redistribute it and/or modify +   it under the terms of the GNU Lesser General Public License as +   published by the Free Software Foundation; either version 2.1 of the +   License, or (at your option) any later version. -     * the GNU Lesser General Public License as published by the Free -       Software Foundation; either version 3 of the License, or (at your -       option) any later version. - -   or - -     * the GNU General Public License as published by the Free -       Software Foundation; either version 2 of the License, or (at your -       option) any later version. - -   or both in parallel, as here. -   This program is distributed in the hope that it will be useful, +   This file is distributed in the hope that it will be useful,     but WITHOUT ANY WARRANTY; without even the implied warranty of     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the -   GNU General Public License for more details. +   GNU Lesser General Public License for more details. -   You should have received a copy of the GNU General Public License -   along with this program; if not, see <https://www.gnu.org/licenses/>.  */ +   You should have received a copy of the GNU Lesser General Public License +   along with this program.  If not, see <https://www.gnu.org/licenses/>.  */  /* Written by Bruno Haible <bruno@clisp.org>, 2005.  */ @@ -29,15 +20,48 @@  /* ========================================================================= */ -#if USE_POSIX_THREADS +#if USE_POSIX_THREADS || USE_ISOC_AND_POSIX_THREADS  /* Use the POSIX threads library.  */ +# include <errno.h>  # include <pthread.h>  # include <stdlib.h>  # if PTHREAD_IN_USE_DETECTION_HARD +#  if defined __FreeBSD__ || defined __DragonFly__                 /* FreeBSD */ + +/* Test using pthread_key_create.  */ + +int +glthread_in_use (void) +{ +  static int tested; +  static int result; /* 1: linked with -lpthread, 0: only with libc */ + +  if (!tested) +    { +      pthread_key_t key; +      int err = pthread_key_create (&key, NULL); + +      if (err == ENOSYS) +        result = 0; +      else +        { +          result = 1; +          if (err == 0) +            pthread_key_delete (key); +        } +      tested = 1; +    } +  return result; +} + +#  else                                                     /* Solaris, HP-UX */ + +/* Test using pthread_create.  */ +  /* The function to be executed by a dummy thread.  */  static void *  dummy_thread_func (void *arg) @@ -71,6 +95,8 @@ glthread_in_use (void)    return result;  } +#  endif +  # endif  #endif | 
