Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(458)

Unified Diff: src/untrusted/pthread/nc_rwlock.c

Issue 951583004: Implement pthread_rwlock functions for NaCl newlib (Closed) Base URL: https://chromium.googlesource.com/native_client/src/native_client.git@master
Patch Set: Created 5 years, 9 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « src/untrusted/pthread/nacl.scons ('k') | src/untrusted/pthread/nc_thread.c » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: src/untrusted/pthread/nc_rwlock.c
diff --git a/src/untrusted/pthread/nc_rwlock.c b/src/untrusted/pthread/nc_rwlock.c
new file mode 100644
index 0000000000000000000000000000000000000000..760f2d5fc422c76533eb9a02fd930bb8fa2cb228
--- /dev/null
+++ b/src/untrusted/pthread/nc_rwlock.c
@@ -0,0 +1,295 @@
+/*
+ * Copyright 2015 The Native Client Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+/*
+ * Native Client rwlock implementation
+ *
+ * This implementation is a 'write-preferring' reader-writer lock which
+ * avoids writer starvation by preventing readers from acquiring the lock
+ * while there are waiting writers (with an exception to prevent deadlocks
+ * in the case of recursive read lock (see read_lock_available)). See:
+ * http://en.wikipedia.org/wiki/Readers%E2%80%93writer_lock
+ *
+ * The thundering herd problem is avoided (at least for waiting writers)
+ * by only waking a single writer at a time.
+ */
+
+#include <errno.h>
+
+#include "native_client/src/untrusted/pthread/pthread.h"
+#include "native_client/src/untrusted/pthread/pthread_internal.h"
+#include "native_client/src/untrusted/pthread/pthread_types.h"
+
+int pthread_rwlockattr_init(pthread_rwlockattr_t *attr) {
+ attr->type = PTHREAD_PROCESS_PRIVATE;
+ return 0;
+}
+
+int pthread_rwlockattr_destroy(pthread_rwlockattr_t *attr) {
+ return 0;
+}
+
+int pthread_rwlockattr_getpshared(const pthread_rwlockattr_t *attr,
+ int *pshared) {
+ *pshared = attr->type;
+ return 0;
+}
+
+int pthread_rwlockattr_setpshared(pthread_rwlockattr_t *attr, int pshared) {
+ if (pshared != PTHREAD_PROCESS_PRIVATE && pshared != PTHREAD_PROCESS_SHARED)
+ return EINVAL;
+ attr->type = pshared;
+ return 0;
+}
+
+int pthread_rwlock_init(pthread_rwlock_t *rwlock,
+ const pthread_rwlockattr_t *attr) {
+ if (attr != NULL && attr->type != PTHREAD_PROCESS_PRIVATE)
+ return EINVAL;
+ rwlock->writer_thread_id = NACL_PTHREAD_ILLEGAL_THREAD_ID;
+ rwlock->writers_waiting = 0;
+ rwlock->reader_count = 0;
+ int rc = pthread_mutex_init(&rwlock->mutex, NULL);
+ if (rc != 0)
+ return rc;
+ rc = pthread_cond_init(&rwlock->write_possible, NULL);
+ if (rc != 0)
+ return rc;
+ return pthread_cond_init(&rwlock->read_possible, NULL);
+}
+
+/*
+ * Helper function used by waiting writers to determine if they can take
+ * the lock. The rwlock->mutex must be held when calling this function.
+ * Returns 1 if the write lock can be taken, 0 if it can't.
+ */
+static inline int write_lock_available(pthread_rwlock_t *rwlock) {
+ /*
+ * Write lock is available if there is no current writer and no current
+ * readers.
+ */
+ if (rwlock->writer_thread_id != NACL_PTHREAD_ILLEGAL_THREAD_ID)
+ return 0;
+ if (rwlock->reader_count > 0)
+ return 0;
+ return 1;
+}
+
+/*
+ * Helper function used by waiting readers to determine if they can take
+ * the lock. The rwlock->mutex must be held when calling this function.
+ * Returns 1 if the write lock can be taken, 0 if it can't.
+ */
+static inline int read_lock_available(pthread_rwlock_t *rwlock) {
+ /*
+ * Read lock is unavailable if there is a current writer.
+ */
+ if (rwlock->writer_thread_id != NACL_PTHREAD_ILLEGAL_THREAD_ID)
+ return 0;
+
+ /*
+ * Attempt to reduce writer starvation by blocking readers when there
+ * is a waiting writer. However don't do this if the current thread
+ * already holds one or more rdlocks in order to allow for recursive
+ * rdlocks. See: http://stackoverflow.com/questions/2190090/
+ * how-to-prevent-writer-starvation-in-a-read-write-lock-in-pthreads
+ */
+ if (rwlock->writers_waiting > 0 && __nc_get_tdb()->rdlock_count == 0)
+ return 0;
+ return 1;
+}
+
+/*
+ * Internal function used to acquire the read lock.
+ * This operates in three different ways in order to implement the three public
+ * functions:
+ * pthread_rwlock_rdlock
+ * pthread_rwlock_tryrdlock
+ * pthread_rwlock_timedrdlock
+ */
+static int rdlock_internal(pthread_rwlock_t *rwlock,
+ const struct timespec *abs_timeout,
+ int try_only) {
+ int rc2;
+ int rc = pthread_mutex_lock(&rwlock->mutex);
+ if (rc != 0)
+ return rc;
+
+ /*
+ * Wait repeatedly until the write preconditions are met.
+ * In theory this loop should only execute once because the preconditions
+ * should always be true when the condition is signaled.
+ */
+ while (!read_lock_available(rwlock)) {
+ if (try_only) {
+ rc = EBUSY;
+ } else if (abs_timeout != NULL) {
+ rc = pthread_cond_timedwait(&rwlock->read_possible,
+ &rwlock->mutex,
+ abs_timeout);
+ } else {
+ rc = pthread_cond_wait(&rwlock->read_possible, &rwlock->mutex);
+ }
+ if (rc != 0)
+ goto done;
+ }
+
+ /* Acquire the read lock. */
+ rwlock->reader_count++;
+ __nc_get_tdb()->rdlock_count++;
+done:
+ rc2 = pthread_mutex_unlock(&rwlock->mutex);
+ return rc == 0 ? rc2 : rc;
+}
+
+/*
+ * Internal function used to acquire the write lock.
+ * This operates in three different ways in order to implement the three public
+ * functions:
+ * pthread_rwlock_wrlock
+ * pthread_rwlock_trywrlock
+ * pthread_rwlock_timedwrlock
+ */
+static int rwlock_internal(pthread_rwlock_t *rwlock,
+ const struct timespec *abs_timeout,
+ int try_only) {
+ int rc2;
+ int rc = pthread_mutex_lock(&rwlock->mutex);
+ if (rc != 0)
+ return rc;
+
+ /* Wait repeatedly until the write preconditions are met */
+ while (!write_lock_available(rwlock)) {
+ if (try_only) {
+ rc = EBUSY;
+ } else {
+ /*
+ * Before waiting (and releasing the lock) we increment the
+ * waiting_writers count so the unlocking code knows to wake
+ * a writer first (before any waiting readers).
+ */
+ rwlock->writers_waiting++;
+ if (abs_timeout != NULL) {
+ rc = pthread_cond_timedwait(&rwlock->write_possible,
+ &rwlock->mutex,
+ abs_timeout);
+ } else {
+ rc = pthread_cond_wait(&rwlock->write_possible,
+ &rwlock->mutex);
+ }
+ rwlock->writers_waiting--;
+ }
+ if (rc != 0)
+ goto done;
+ }
+
+ /* Acquire the write lock. */
+ rwlock->writer_thread_id = pthread_self();
+done:
+ rc2 = pthread_mutex_unlock(&rwlock->mutex);
+ return rc == 0 ? rc2 : rc;
+}
+
+int pthread_rwlock_timedrdlock(pthread_rwlock_t *rwlock,
+ const struct timespec *abs_timeout) {
+ return rdlock_internal(rwlock, abs_timeout, 0);
+}
+
+int pthread_rwlock_rdlock(pthread_rwlock_t *rwlock) {
+ return rdlock_internal(rwlock, NULL, 0);
+}
+
+int pthread_rwlock_tryrdlock(pthread_rwlock_t *rwlock) {
+ return rdlock_internal(rwlock, NULL, 1);
+}
+
+int pthread_rwlock_wrlock(pthread_rwlock_t *rwlock) {
+ return rwlock_internal(rwlock, NULL, 0);
+}
+
+int pthread_rwlock_timedwrlock(pthread_rwlock_t *rwlock,
+ const struct timespec *abs_timeout) {
+ return rwlock_internal(rwlock, abs_timeout, 0);
+}
+
+int pthread_rwlock_trywrlock(pthread_rwlock_t *rwlock) {
+ return rwlock_internal(rwlock, NULL, 1);
+}
+
+int pthread_rwlock_unlock(pthread_rwlock_t *rwlock) {
+ int rc2;
+ int rc = pthread_mutex_lock(&rwlock->mutex);
+ if (rc != 0)
+ return rc;
+
+ if (rwlock->writer_thread_id != NACL_PTHREAD_ILLEGAL_THREAD_ID) {
+ /* The write lock is held. Ensure it's the current thread that holds it. */
+ if (rwlock->writer_thread_id != pthread_self()) {
+ rc = EPERM;
+ goto done;
+ }
+
+ /* Release write lock. */
+ rwlock->writer_thread_id = NACL_PTHREAD_ILLEGAL_THREAD_ID;
+ if (rwlock->writers_waiting > 0) {
+ /* Wake a waiting writer if there is one. */
+ rc = pthread_cond_signal(&rwlock->write_possible);
+ } else {
+ /*
+ * Otherwise wake all waiting readers. All of them should be able
+ * to make progress now that the write lock is no longer held.
+ */
+ rc = pthread_cond_broadcast(&rwlock->read_possible);
+ }
+ } else {
+ if (rwlock->reader_count == 0) {
+ rc = EPERM;
+ goto done;
+ }
+
+ /* Release read lock. */
+ rwlock->reader_count--;
+ __nc_get_tdb()->rdlock_count--;
+ if (rwlock->reader_count == 0 && rwlock->writers_waiting > 0) {
+ /* Wake a waiting writer. */
+ rc = pthread_cond_signal(&rwlock->write_possible);
+ }
+ }
+
+done:
+ rc2 = pthread_mutex_unlock(&rwlock->mutex);
+ return rc == 0 ? rc2 : rc;
+}
+
+int pthread_rwlock_destroy(pthread_rwlock_t *rwlock) {
+ /* Return EBUSY if another thread holds the mutex. */
+ int rc = pthread_mutex_trylock(&rwlock->mutex);
+ if (rc != 0) {
+ return rc;
+ }
+
+ /* Return EBUSY if there are active readers or an active writer. */
+ if (rwlock->reader_count != 0) {
+ pthread_mutex_unlock(&rwlock->mutex);
+ return EBUSY;
+ }
+ if (rwlock->writer_thread_id != NACL_PTHREAD_ILLEGAL_THREAD_ID) {
+ pthread_mutex_unlock(&rwlock->mutex);
+ return EBUSY;
+ }
+
+ int rc1 = pthread_cond_destroy(&rwlock->write_possible);
+ int rc2 = pthread_cond_destroy(&rwlock->read_possible);
+
+ /* Finally unlock the mutex and destroy it. */
+ pthread_mutex_unlock(&rwlock->mutex);
+ int rc3 = pthread_mutex_destroy(&rwlock->mutex);
+ if (rc1 != 0)
+ return rc1;
+ if (rc2 != 0)
+ return rc2;
+ return rc3;
+}
« no previous file with comments | « src/untrusted/pthread/nacl.scons ('k') | src/untrusted/pthread/nc_thread.c » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698