diff options
| author | Jörg Frings-Fürst <debian@jff-webhosting.net> | 2017-12-02 12:05:34 +0100 | 
|---|---|---|
| committer | Jörg Frings-Fürst <debian@jff-webhosting.net> | 2017-12-02 12:05:34 +0100 | 
| commit | 7c78c92a28ef43d68b172adf97fbd8a27be3baec (patch) | |
| tree | 3a98b0d01865f5e00912521c58386eb008a70d07 /lib/glthread | |
| parent | 4d76768442551c97a85e6f133cb818d223012746 (diff) | |
| parent | 3ee36dc9787cee6ab5314af8f9c01b05a50e7d9d (diff) | |
Merge branch 'feature/upstream' into develop
Diffstat (limited to 'lib/glthread')
| -rw-r--r-- | lib/glthread/lock.c | 186 | ||||
| -rw-r--r-- | lib/glthread/lock.h | 66 | ||||
| -rw-r--r-- | lib/glthread/threadlib.c | 4 | 
3 files changed, 236 insertions, 20 deletions
| diff --git a/lib/glthread/lock.c b/lib/glthread/lock.c index 459955dd..df05dbc0 100644 --- a/lib/glthread/lock.c +++ b/lib/glthread/lock.c @@ -1,5 +1,5 @@  /* Locking in multithreaded situations. -   Copyright (C) 2005-2016 Free Software Foundation, Inc. +   Copyright (C) 2005-2017 Free Software Foundation, Inc.     This program is free software: you can redistribute it and/or     modify it under the terms of either: @@ -21,7 +21,7 @@     GNU 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 <http://www.gnu.org/licenses/>.  */ +   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, @@ -39,9 +39,38 @@  /* ------------------------- gl_rwlock_t datatype ------------------------- */ -# if HAVE_PTHREAD_RWLOCK +# if HAVE_PTHREAD_RWLOCK && (HAVE_PTHREAD_RWLOCK_RDLOCK_PREFER_WRITER || (defined PTHREAD_RWLOCK_WRITER_NONRECURSIVE_INITIALIZER_NP && (__GNU_LIBRARY__ > 1))) -#  if !defined PTHREAD_RWLOCK_INITIALIZER +#  ifdef PTHREAD_RWLOCK_INITIALIZER + +#   if !HAVE_PTHREAD_RWLOCK_RDLOCK_PREFER_WRITER +     /* glibc with bug https://sourceware.org/bugzilla/show_bug.cgi?id=13701 */ + +int +glthread_rwlock_init_for_glibc (pthread_rwlock_t *lock) +{ +  pthread_rwlockattr_t attributes; +  int err; + +  err = pthread_rwlockattr_init (&attributes); +  if (err != 0) +    return err; +  /* Note: PTHREAD_RWLOCK_PREFER_WRITER_NONRECURSIVE_NP is the only value that +     causes the writer to be preferred. PTHREAD_RWLOCK_PREFER_WRITER_NP does not +     do this; see +     http://man7.org/linux/man-pages/man3/pthread_rwlockattr_setkind_np.3.html */ +  err = pthread_rwlockattr_setkind_np (&attributes, +                                       PTHREAD_RWLOCK_PREFER_WRITER_NONRECURSIVE_NP); +  if (err == 0) +    err = pthread_rwlock_init(lock, &attributes); +  /* pthread_rwlockattr_destroy always returns 0.  It cannot influence the +     return value.  */ +  pthread_rwlockattr_destroy (&attributes); +  return err; +} + +#   endif +#  else  int  glthread_rwlock_init_multithreaded (gl_rwlock_t *lock) @@ -161,11 +190,9 @@ glthread_rwlock_rdlock_multithreaded (gl_rwlock_t *lock)    if (err != 0)      return err;    /* Test whether only readers are currently running, and whether the runcount -     field will not overflow.  */ -  /* POSIX says: "It is implementation-defined whether the calling thread -     acquires the lock when a writer does not hold the lock and there are -     writers blocked on the lock."  Let's say, no: give the writers a higher -     priority.  */ +     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 @@ -490,6 +517,141 @@ glthread_once_singlethreaded (pthread_once_t *once_control)  /* ------------------------- 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); +} + +int +glthread_rwlock_unlock_multithreaded (gl_rwlock_t *lock) +{ +  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 +    { +      /* 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; +            } +        } +    } +  return (!pth_mutex_release (&lock->lock) ? errno : 0); +} + +int +glthread_rwlock_destroy_multithreaded (gl_rwlock_t *lock) +{ +  lock->initialized = 0; +  return 0; +} + +# endif +  /* --------------------- gl_recursive_lock_t datatype --------------------- */  /* -------------------------- gl_once_t datatype -------------------------- */ @@ -805,8 +967,10 @@ glthread_rwlock_rdlock_func (gl_rwlock_t *lock)      }    EnterCriticalSection (&lock->lock);    /* Test whether only readers are currently running, and whether the runcount -     field will not overflow.  */ -  if (!(lock->runcount + 1 > 0)) +     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.  */ diff --git a/lib/glthread/lock.h b/lib/glthread/lock.h index 2e001689..06285558 100644 --- a/lib/glthread/lock.h +++ b/lib/glthread/lock.h @@ -1,5 +1,5 @@  /* Locking in multithreaded situations. -   Copyright (C) 2005-2016 Free Software Foundation, Inc. +   Copyright (C) 2005-2017 Free Software Foundation, Inc.     This program is free software: you can redistribute it and/or     modify it under the terms of either: @@ -21,7 +21,7 @@     GNU 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 <http://www.gnu.org/licenses/>.  */ +   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, @@ -148,6 +148,11 @@ extern int glthread_in_use (void);  #  pragma weak pthread_mutexattr_init  #  pragma weak pthread_mutexattr_settype  #  pragma weak pthread_mutexattr_destroy +#  pragma weak pthread_rwlockattr_init +#  if __GNU_LIBRARY__ > 1 +#   pragma weak pthread_rwlockattr_setkind_np +#  endif +#  pragma weak pthread_rwlockattr_destroy  #  ifndef pthread_self  #   pragma weak pthread_self  #  endif @@ -185,7 +190,7 @@ typedef pthread_mutex_t gl_lock_t;  /* ------------------------- gl_rwlock_t datatype ------------------------- */ -# if HAVE_PTHREAD_RWLOCK +# if HAVE_PTHREAD_RWLOCK && (HAVE_PTHREAD_RWLOCK_RDLOCK_PREFER_WRITER || (defined PTHREAD_RWLOCK_WRITER_NONRECURSIVE_INITIALIZER_NP && (__GNU_LIBRARY__ > 1)))  #  ifdef PTHREAD_RWLOCK_INITIALIZER @@ -194,10 +199,18 @@ typedef pthread_rwlock_t gl_rwlock_t;        STORAGECLASS pthread_rwlock_t NAME;  #   define gl_rwlock_define_initialized(STORAGECLASS, NAME) \        STORAGECLASS pthread_rwlock_t NAME = gl_rwlock_initializer; -#   define gl_rwlock_initializer \ -      PTHREAD_RWLOCK_INITIALIZER -#   define glthread_rwlock_init(LOCK) \ -      (pthread_in_use () ? pthread_rwlock_init (LOCK, NULL) : 0) +#   if HAVE_PTHREAD_RWLOCK_RDLOCK_PREFER_WRITER +#    define gl_rwlock_initializer \ +       PTHREAD_RWLOCK_INITIALIZER +#    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 */ +#    define gl_rwlock_initializer \ +       PTHREAD_RWLOCK_WRITER_NONRECURSIVE_INITIALIZER_NP +#    define glthread_rwlock_init(LOCK) \ +       (pthread_in_use () ? glthread_rwlock_init_for_glibc (LOCK) : 0) +extern int glthread_rwlock_init_for_glibc (pthread_rwlock_t *lock); +#   endif  #   define glthread_rwlock_rdlock(LOCK) \        (pthread_in_use () ? pthread_rwlock_rdlock (LOCK) : 0)  #   define glthread_rwlock_wrlock(LOCK) \ @@ -436,6 +449,9 @@ typedef pth_mutex_t gl_lock_t;  /* ------------------------- 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; @@ -454,6 +470,42 @@ typedef pth_rwlock_t gl_rwlock_t;  #  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.  */ diff --git a/lib/glthread/threadlib.c b/lib/glthread/threadlib.c index 70d4eef4..82b8861b 100644 --- a/lib/glthread/threadlib.c +++ b/lib/glthread/threadlib.c @@ -1,5 +1,5 @@  /* Multithreading primitives. -   Copyright (C) 2005-2016 Free Software Foundation, Inc. +   Copyright (C) 2005-2017 Free Software Foundation, Inc.     This program is free software: you can redistribute it and/or     modify it under the terms of either: @@ -21,7 +21,7 @@     GNU 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 <http://www.gnu.org/licenses/>.  */ +   along with this program; if not, see <https://www.gnu.org/licenses/>.  */  /* Written by Bruno Haible <bruno@clisp.org>, 2005.  */ | 
