summaryrefslogtreecommitdiff
path: root/lib/glthread
diff options
context:
space:
mode:
Diffstat (limited to 'lib/glthread')
-rw-r--r--lib/glthread/lock.c186
-rw-r--r--lib/glthread/lock.h66
-rw-r--r--lib/glthread/threadlib.c4
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. */