summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorTavian Barnes <tavianator@gmail.com>2011-08-02 23:33:27 -0600
committerTavian Barnes <tavianator@gmail.com>2011-08-02 23:33:27 -0600
commit5bb6d7edbf4edbc135a4d9aeda19cd7994cb7077 (patch)
tree719e01ed959714ad804621981b639758f3568492
parentebe36194a9f2e97345a296f7fe8c844690d03e85 (diff)
downloaddimension-5bb6d7edbf4edbc135a4d9aeda19cd7994cb7077.tar.xz
Wrap pthread API to reduce duplicated error tests.
-rw-r--r--libdimension/error.c64
-rw-r--r--libdimension/profile.c51
-rw-r--r--libdimension/progress.c123
-rw-r--r--libdimension/prtree.c29
-rw-r--r--libdimension/tests/prtree.c2
-rw-r--r--libdimension/threads.c138
-rw-r--r--libdimension/threads.h151
7 files changed, 369 insertions, 189 deletions
diff --git a/libdimension/error.c b/libdimension/error.c
index cfeed94..6276c5c 100644
--- a/libdimension/error.c
+++ b/libdimension/error.c
@@ -36,6 +36,29 @@
abort(); \
} while (0)
+/** dmnsn_local_lock_mutex implementation. */
+static void
+dmnsn_local_lock_mutex_impl(pthread_mutex_t *mutex)
+{
+ if (pthread_mutex_lock(mutex) != 0) {
+ DMNSN_LOCAL_ERROR("Couldn't lock mutex.");
+ }
+}
+
+/** dmnsn_local_unlock_mutex implementation. */
+static void
+dmnsn_local_unlock_mutex_impl(pthread_mutex_t *mutex)
+{
+ if (pthread_mutex_unlock(mutex) != 0) {
+ DMNSN_LOCAL_ERROR("Couldn't lock mutex.");
+ }
+}
+
+/** Lock a mutex, bailing out without dmnsn_error() on error. */
+#define dmnsn_local_lock_mutex(mutex) dmnsn_local_lock_mutex_impl((mutex)); {
+/** Unlock a mutex, bailing out without dmnsn_error() on error. */
+#define dmnsn_local_unlock_mutex(mutex) dmnsn_local_unlock_mutex_impl((mutex)); }
+
/** The default fatal error handler. */
static void dmnsn_default_fatal_error_fn(void);
@@ -54,13 +77,10 @@ void
dmnsn_report_error(bool die, const char *func, const char *file,
unsigned int line, const char *str)
{
- if (pthread_mutex_lock(&dmnsn_always_die_mutex) != 0) {
- DMNSN_LOCAL_ERROR("Couldn't lock mutex.");
- }
- bool always_die = dmnsn_always_die;
- if (pthread_mutex_unlock(&dmnsn_always_die_mutex) != 0) {
- DMNSN_LOCAL_ERROR("Couldn't unlock mutex.");
- }
+ bool always_die;
+ dmnsn_local_lock_mutex(&dmnsn_always_die_mutex);
+ always_die = dmnsn_always_die;
+ dmnsn_local_unlock_mutex(&dmnsn_always_die_mutex);
fprintf(stderr, "Dimension %s: %s, %s:%u: %s\n",
die ? "ERROR" : "WARNING", func, file, line, str);
@@ -87,39 +107,27 @@ dmnsn_report_error(bool die, const char *func, const char *file,
void
dmnsn_die_on_warnings(bool always_die)
{
- if (pthread_mutex_lock(&dmnsn_always_die_mutex) != 0) {
- DMNSN_LOCAL_ERROR("Couldn't lock mutex.");
- }
- dmnsn_always_die = always_die;
- if (pthread_mutex_unlock(&dmnsn_always_die_mutex) != 0) {
- DMNSN_LOCAL_ERROR("Couldn't unlock mutex.");
- }
+ dmnsn_local_lock_mutex(&dmnsn_always_die_mutex);
+ dmnsn_always_die = always_die;
+ dmnsn_local_unlock_mutex(&dmnsn_always_die_mutex);
}
dmnsn_fatal_error_fn *
dmnsn_get_fatal_error_fn(void)
{
dmnsn_fatal_error_fn *fatal;
- if (pthread_mutex_lock(&dmnsn_fatal_mutex) != 0) {
- DMNSN_LOCAL_ERROR("Couldn't lock fatal error handler mutex.");
- }
- fatal = dmnsn_fatal;
- if (pthread_mutex_unlock(&dmnsn_fatal_mutex) != 0) {
- DMNSN_LOCAL_ERROR("Couldn't unlock fatal error handler mutex.");
- }
+ dmnsn_local_lock_mutex(&dmnsn_fatal_mutex);
+ fatal = dmnsn_fatal;
+ dmnsn_local_unlock_mutex(&dmnsn_fatal_mutex);
return fatal;
}
void
dmnsn_set_fatal_error_fn(dmnsn_fatal_error_fn *fatal)
{
- if (pthread_mutex_lock(&dmnsn_fatal_mutex) != 0) {
- DMNSN_LOCAL_ERROR("Couldn't lock fatal error handler mutex.");
- }
- dmnsn_fatal = fatal;
- if (pthread_mutex_unlock(&dmnsn_fatal_mutex) != 0) {
- DMNSN_LOCAL_ERROR("Couldn't unlock fatal error handler mutex.");
- }
+ dmnsn_local_lock_mutex(&dmnsn_fatal_mutex);
+ dmnsn_fatal = fatal;
+ dmnsn_local_unlock_mutex(&dmnsn_fatal_mutex);
}
static void
diff --git a/libdimension/profile.c b/libdimension/profile.c
index 8fca36a..f7e8e41 100644
--- a/libdimension/profile.c
+++ b/libdimension/profile.c
@@ -65,13 +65,9 @@ dmnsn_delete_thread_profile(void *ptr)
{
dmnsn_dictionary *thread_profile = ptr;
- if (pthread_mutex_lock(&dmnsn_profile_mutex) != 0) {
- dmnsn_error("Couldn't lock mutex.");
- }
- dmnsn_dictionary_apply(thread_profile, dmnsn_profile_globalize);
- if (pthread_mutex_unlock(&dmnsn_profile_mutex) != 0) {
- dmnsn_error("Couldn't unlock mutex.");
- }
+ dmnsn_lock_mutex(&dmnsn_profile_mutex);
+ dmnsn_dictionary_apply(thread_profile, dmnsn_profile_globalize);
+ dmnsn_unlock_mutex(&dmnsn_profile_mutex);
dmnsn_delete_dictionary(thread_profile);
}
@@ -80,31 +76,18 @@ dmnsn_delete_thread_profile(void *ptr)
static void
dmnsn_initialize_thread_profile(void)
{
- if (pthread_key_create(&dmnsn_thread_profile, dmnsn_delete_thread_profile)
- != 0)
- {
- dmnsn_error("pthread_key_create() failed.");
- }
+ dmnsn_key_create(&dmnsn_thread_profile, dmnsn_delete_thread_profile);
- if (pthread_mutex_lock(&dmnsn_profile_mutex) != 0) {
- dmnsn_error("Couldn't lock mutex.");
- }
- dmnsn_profile = dmnsn_new_dictionary(sizeof(dmnsn_branch));
- if (pthread_mutex_unlock(&dmnsn_profile_mutex) != 0) {
- dmnsn_error("Couldn't unlock mutex.");
- }
+ dmnsn_lock_mutex(&dmnsn_profile_mutex);
+ dmnsn_profile = dmnsn_new_dictionary(sizeof(dmnsn_branch));
+ dmnsn_unlock_mutex(&dmnsn_profile_mutex);
}
/** Get the thread-specific profile data. */
static dmnsn_dictionary *
dmnsn_get_thread_profile(void)
{
- if (pthread_once(&dmnsn_thread_profile_once, dmnsn_initialize_thread_profile)
- != 0)
- {
- dmnsn_error("pthread_once() failed.");
- }
-
+ dmnsn_once(&dmnsn_thread_profile_once, dmnsn_initialize_thread_profile);
return pthread_getspecific(dmnsn_thread_profile);
}
@@ -112,9 +95,7 @@ dmnsn_get_thread_profile(void)
static void
dmnsn_set_thread_profile(dmnsn_dictionary *thread_profile)
{
- if (pthread_setspecific(dmnsn_thread_profile, thread_profile) != 0) {
- dmnsn_error("pthread_setspecific() failed.");
- }
+ dmnsn_setspecific(dmnsn_thread_profile, thread_profile);
}
bool
@@ -178,13 +159,9 @@ dmnsn_print_bad_predictions(void)
dmnsn_set_thread_profile(NULL);
}
- if (pthread_mutex_lock(&dmnsn_profile_mutex) != 0) {
- dmnsn_error("Couldn't lock mutex.");
- }
- dmnsn_dictionary_apply(dmnsn_profile, dmnsn_print_bad_prediction);
- dmnsn_delete_dictionary(dmnsn_profile);
- dmnsn_profile = NULL;
- if (pthread_mutex_unlock(&dmnsn_profile_mutex) != 0) {
- dmnsn_error("Couldn't unlock mutex.");
- }
+ dmnsn_lock_mutex(&dmnsn_profile_mutex);
+ dmnsn_dictionary_apply(dmnsn_profile, dmnsn_print_bad_prediction);
+ dmnsn_delete_dictionary(dmnsn_profile);
+ dmnsn_profile = NULL;
+ dmnsn_unlock_mutex(&dmnsn_profile_mutex);
}
diff --git a/libdimension/progress.c b/libdimension/progress.c
index 22696cb..7344679 100644
--- a/libdimension/progress.c
+++ b/libdimension/progress.c
@@ -26,13 +26,6 @@
#include "dimension-impl.h"
#include <pthread.h>
-/** Read-lock a progress object. */
-static void dmnsn_progress_rdlock(const dmnsn_progress *progress);
-/** Write-lock a progress object. */
-static void dmnsn_progress_wrlock(dmnsn_progress *progress);
-/** Unlock a progress object. */
-static void dmnsn_progress_unlock(const dmnsn_progress *progress);
-
/* Allocate a new dmnsn_progress* */
dmnsn_progress *
dmnsn_new_progress(void)
@@ -44,19 +37,13 @@ dmnsn_new_progress(void)
/* Initialize the rwlock, condition variable, and mutex */
progress->rwlock = dmnsn_malloc(sizeof(pthread_rwlock_t));
- if (pthread_rwlock_init(progress->rwlock, NULL) != 0) {
- dmnsn_error("Couldn't initialize read-write lock.");
- }
+ dmnsn_initialize_rwlock(progress->rwlock);
progress->cond = dmnsn_malloc(sizeof(pthread_cond_t));
- if (pthread_cond_init(progress->cond, NULL) != 0) {
- dmnsn_error("Couldn't initialize condition variable.");
- }
+ dmnsn_initialize_cond(progress->cond);
progress->mutex = dmnsn_malloc(sizeof(pthread_mutex_t));
- if (pthread_mutex_init(progress->mutex, NULL) != 0) {
- dmnsn_error("Couldn't initialize mutex.");
- }
+ dmnsn_initialize_mutex(progress->mutex);
progress->min_wait = 1.0;
progress->min_waitp = &progress->min_wait;
@@ -81,18 +68,16 @@ dmnsn_finish_progress(dmnsn_progress *progress)
}
/* Free the progress object */
- if (pthread_rwlock_destroy(progress->rwlock) != 0) {
- dmnsn_warning("Leaking rwlock.");
- }
- if (pthread_mutex_destroy(progress->mutex) != 0) {
- dmnsn_warning("Leaking mutex.");
- }
- if (pthread_cond_destroy(progress->cond) != 0) {
- dmnsn_warning("Leaking condition variable.");
- }
- dmnsn_free(progress->rwlock);
+
+ dmnsn_destroy_mutex(progress->mutex);
dmnsn_free(progress->mutex);
+
+ dmnsn_destroy_cond(progress->cond);
dmnsn_free(progress->cond);
+
+ dmnsn_destroy_rwlock(progress->rwlock);
+ dmnsn_free(progress->rwlock);
+
dmnsn_free(progress);
}
@@ -105,9 +90,9 @@ dmnsn_get_progress(const dmnsn_progress *progress)
{
double prog;
- dmnsn_progress_rdlock(progress);
+ dmnsn_read_lock(progress->rwlock);
prog = (double)progress->progress/progress->total;
- dmnsn_progress_unlock(progress);
+ dmnsn_unlock_rwlock(progress->rwlock);
return prog;
}
@@ -116,100 +101,52 @@ dmnsn_get_progress(const dmnsn_progress *progress)
void
dmnsn_wait_progress(const dmnsn_progress *progress, double prog)
{
- if (pthread_mutex_lock(progress->mutex) == 0) {
+ dmnsn_lock_mutex(progress->mutex);
while (dmnsn_get_progress(progress) < prog) {
/* Set the minimum waited-on value */
if (prog < progress->min_wait)
*progress->min_waitp = prog;
- if (pthread_cond_wait(progress->cond, progress->mutex) != 0) {
- dmnsn_error("Couldn't wait on condition variable.");
- }
- }
-
- if (pthread_mutex_unlock(progress->mutex) != 0) {
- dmnsn_error("Couldn't unlock condition mutex.");
+ dmnsn_cond_wait(progress->cond, progress->mutex);
}
- } else {
- dmnsn_error("Couldn't lock condition mutex.");
- }
+ dmnsn_unlock_mutex(progress->mutex);
}
/* Set the total number of loop iterations */
void
dmnsn_set_progress_total(dmnsn_progress *progress, size_t total)
{
- dmnsn_progress_wrlock(progress);
+ dmnsn_write_lock(progress->rwlock);
progress->total = total;
- dmnsn_progress_unlock(progress);
+ dmnsn_unlock_rwlock(progress->rwlock);
}
/* Increment the number of completed loop iterations */
void
dmnsn_increment_progress(dmnsn_progress *progress)
{
- dmnsn_progress_wrlock(progress);
+ dmnsn_write_lock(progress->rwlock);
++progress->progress;
- dmnsn_progress_unlock(progress);
-
- if (pthread_mutex_lock(progress->mutex) != 0) {
- dmnsn_error("Couldn't lock condition mutex.");
- }
+ dmnsn_unlock_rwlock(progress->rwlock);
- if (dmnsn_get_progress(progress) >= progress->min_wait) {
- progress->min_wait = 1.0;
+ dmnsn_lock_mutex(progress->mutex);
+ if (dmnsn_get_progress(progress) >= progress->min_wait) {
+ progress->min_wait = 1.0;
- if (pthread_cond_broadcast(progress->cond) != 0) {
- dmnsn_error("Couldn't signal condition variable.");
+ dmnsn_cond_broadcast(progress->cond);
}
- }
-
- if (pthread_mutex_unlock(progress->mutex) != 0) {
- dmnsn_error("Couldn't unlock condition mutex.");
- }
+ dmnsn_unlock_mutex(progress->mutex);
}
/* Immediately set to 100% completion */
void
dmnsn_done_progress(dmnsn_progress *progress)
{
- dmnsn_progress_wrlock(progress);
+ dmnsn_write_lock(progress->rwlock);
progress->progress = progress->total;
- dmnsn_progress_unlock(progress);
-
- if (pthread_mutex_lock(progress->mutex) != 0) {
- dmnsn_error("Couldn't lock condition mutex.");
- }
- if (pthread_cond_broadcast(progress->cond) != 0) {
- dmnsn_error("Couldn't signal condition variable.");
- }
- if (pthread_mutex_unlock(progress->mutex) != 0) {
- dmnsn_error("Couldn't unlock condition mutex.");
- }
-}
-
-/* Thread synchronization */
-
-static void
-dmnsn_progress_rdlock(const dmnsn_progress *progress)
-{
- if (pthread_rwlock_rdlock(progress->rwlock) != 0) {
- dmnsn_error("Couldn't acquire read-lock.");
- }
-}
+ dmnsn_unlock_rwlock(progress->rwlock);
-static void
-dmnsn_progress_wrlock(dmnsn_progress *progress)
-{
- if (pthread_rwlock_wrlock(progress->rwlock) != 0) {
- dmnsn_error("Couldn't acquire write-lock.");
- }
-}
-
-static void
-dmnsn_progress_unlock(const dmnsn_progress *progress)
-{
- if (pthread_rwlock_unlock(progress->rwlock) != 0) {
- dmnsn_error("Couldn't unlock read-write lock.");
- }
+ dmnsn_lock_mutex(progress->mutex);
+ dmnsn_cond_broadcast(progress->cond);
+ dmnsn_unlock_mutex(progress->mutex);
}
diff --git a/libdimension/prtree.c b/libdimension/prtree.c
index 4c7a2e5..0663acf 100644
--- a/libdimension/prtree.c
+++ b/libdimension/prtree.c
@@ -448,13 +448,9 @@ dmnsn_new_prtree(const dmnsn_array *objects)
prtree->bounding_box = dmnsn_zero_bounding_box();
}
- if (pthread_mutex_lock(&dmnsn_prtree_seq_mutex) != 0) {
- dmnsn_error("Couldn't lock mutex.");
- }
- prtree->id = dmnsn_prtree_seq++;
- if (pthread_mutex_unlock(&dmnsn_prtree_seq_mutex) != 0) {
- dmnsn_error("Couldn't unlock mutex.");
- }
+ dmnsn_lock_mutex(&dmnsn_prtree_seq_mutex);
+ prtree->id = dmnsn_prtree_seq++;
+ dmnsn_unlock_mutex(&dmnsn_prtree_seq_mutex);
return prtree;
}
@@ -545,20 +541,13 @@ dmnsn_delete_prtree_caches(void *caches)
static void
dmnsn_initialize_prtree_caches(void)
{
- if (pthread_key_create(&dmnsn_prtree_caches, dmnsn_delete_prtree_caches) != 0)
- {
- dmnsn_error("pthread_key_create() failed.");
- }
+ dmnsn_key_create(&dmnsn_prtree_caches, dmnsn_delete_prtree_caches);
}
static dmnsn_array *
dmnsn_get_prtree_caches(void)
{
- if (pthread_once(&dmnsn_prtree_caches_once, dmnsn_initialize_prtree_caches)
- != 0)
- {
- dmnsn_error("pthread_once() failed.");
- }
+ dmnsn_once(&dmnsn_prtree_caches_once, dmnsn_initialize_prtree_caches);
return pthread_getspecific(dmnsn_prtree_caches);
}
@@ -567,8 +556,8 @@ dmnsn_get_prtree_caches(void)
DMNSN_DESTRUCTOR static void
dmnsn_delete_main_prtree_caches(void)
{
- dmnsn_delete_array(dmnsn_get_prtree_caches());
- pthread_key_delete(dmnsn_prtree_caches);
+ dmnsn_delete_prtree_caches(dmnsn_get_prtree_caches());
+ dmnsn_key_delete(dmnsn_prtree_caches);
}
static dmnsn_intersection_cache *
@@ -577,9 +566,7 @@ dmnsn_get_intersection_cache(size_t id)
dmnsn_array *caches = dmnsn_get_prtree_caches();
if (!caches) {
caches = dmnsn_new_array(sizeof(dmnsn_intersection_cache));
- if (pthread_setspecific(dmnsn_prtree_caches, caches) != 0) {
- dmnsn_error("pthread_setspecific() failed.");
- }
+ dmnsn_setspecific(dmnsn_prtree_caches, caches);
}
while (dmnsn_array_size(caches) <= id) {
diff --git a/libdimension/tests/prtree.c b/libdimension/tests/prtree.c
index 5fb2f10..ddfa46a 100644
--- a/libdimension/tests/prtree.c
+++ b/libdimension/tests/prtree.c
@@ -22,7 +22,7 @@
*/
#include "dimension-impl.h"
-#include "../../libdimension/prtree.c" /* For DMNSN_PRTREE_B */
+#define DMNSN_PRTREE_B 8
#include <stdio.h>
#include <stdlib.h>
diff --git a/libdimension/threads.c b/libdimension/threads.c
index 04874de..11b01cf 100644
--- a/libdimension/threads.c
+++ b/libdimension/threads.c
@@ -124,3 +124,141 @@ dmnsn_execute_concurrently(dmnsn_ccthread_fn *ccthread_fn,
return ret;
}
+
+/* pthread wrappers */
+
+void
+dmnsn_initialize_mutex(pthread_mutex_t *mutex)
+{
+ if (pthread_mutex_init(mutex, NULL) != 0) {
+ dmnsn_error("Couldn't initialize mutex.");
+ }
+}
+
+void
+dmnsn_lock_mutex_impl(pthread_mutex_t *mutex)
+{
+ if (pthread_mutex_lock(mutex) != 0) {
+ dmnsn_error("Couldn't lock mutex.");
+ }
+}
+
+void
+dmnsn_unlock_mutex_impl(pthread_mutex_t *mutex)
+{
+ if (pthread_mutex_unlock(mutex) != 0) {
+ dmnsn_error("Couldn't unlock mutex.");
+ }
+}
+
+void
+dmnsn_destroy_mutex(pthread_mutex_t *mutex)
+{
+ if (pthread_mutex_destroy(mutex) != 0) {
+ dmnsn_warning("Couldn't destroy mutex.");
+ }
+}
+
+void
+dmnsn_initialize_rwlock(pthread_rwlock_t *rwlock)
+{
+ if (pthread_rwlock_init(rwlock, NULL) != 0) {
+ dmnsn_error("Couldn't initialize read-write lock.");
+ }
+}
+
+void
+dmnsn_read_lock_impl(pthread_rwlock_t *rwlock)
+{
+ if (pthread_rwlock_rdlock(rwlock) != 0) {
+ dmnsn_error("Couldn't acquire read lock.");
+ }
+}
+
+void
+dmnsn_write_lock_impl(pthread_rwlock_t *rwlock)
+{
+ if (pthread_rwlock_wrlock(rwlock) != 0) {
+ dmnsn_error("Couldn't acquire write lock.");
+ }
+}
+
+void
+dmnsn_unlock_rwlock_impl(pthread_rwlock_t *rwlock)
+{
+ if (pthread_rwlock_unlock(rwlock) != 0) {
+ dmnsn_error("Couldn't unlock read-write lock.");
+ }
+}
+
+void
+dmnsn_destroy_rwlock(pthread_rwlock_t *rwlock)
+{
+ if (pthread_rwlock_destroy(rwlock) != 0) {
+ dmnsn_warning("Couldn't destroy read-write lock.");
+ }
+}
+
+void
+dmnsn_initialize_cond(pthread_cond_t *cond)
+{
+ if (pthread_cond_init(cond, NULL) != 0) {
+ dmnsn_error("Couldn't initialize condition variable.");
+ }
+}
+
+void
+dmnsn_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex)
+{
+ if (pthread_cond_wait(cond, mutex) != 0) {
+ dmnsn_error("Couldn't wait on condition variable.");
+ }
+}
+
+void
+dmnsn_cond_broadcast(pthread_cond_t *cond)
+{
+ if (pthread_cond_broadcast(cond) != 0) {
+ dmnsn_error("Couldn't signal condition variable.");
+ }
+}
+
+void
+dmnsn_destroy_cond(pthread_cond_t *cond)
+{
+ if (pthread_cond_destroy(cond) != 0) {
+ dmnsn_warning("Couldn't destroy condition variable.");
+ }
+}
+
+void
+dmnsn_once(pthread_once_t *once, dmnsn_once_fn *once_fn)
+{
+ if (pthread_once(once, once_fn) != 0) {
+ dmnsn_error("Couldn't call one-shot function.");
+ }
+}
+
+void
+dmnsn_key_create(pthread_key_t *key, dmnsn_callback_fn *destructor)
+{
+ if (pthread_key_create(key, destructor) != 0) {
+ dmnsn_error("Couldn't initialize thread-specific pointer.");
+ }
+}
+
+void
+dmnsn_setspecific(pthread_key_t key, const void *value)
+{
+ if (pthread_setspecific(key, value) != 0) {
+ dmnsn_error("Couldn't set thread-specific pointer.");
+ }
+}
+
+void
+dmnsn_key_delete(pthread_key_t key)
+{
+ if (pthread_key_delete(key) != 0) {
+ dmnsn_warning("Couldn't destroy thread-specific pointer.");
+ }
+}
diff --git a/libdimension/threads.h b/libdimension/threads.h
index 8e88e7c..229acb9 100644
--- a/libdimension/threads.h
+++ b/libdimension/threads.h
@@ -23,6 +23,8 @@
* Background threading interface.
*/
+#include <pthread.h>
+
/**
* Thread callback type.
* @param[in,out] ptr An arbitrary pointer.
@@ -31,6 +33,15 @@
typedef int dmnsn_thread_fn(void *ptr);
/**
+ * Create a thread that cleans up after itself on errors.
+ * @param[in,out] progress The progress object to associate with the thread.
+ * @param[in] thread_fn The thread callback.
+ * @param[in,out] arg The pointer to pass to the thread callback.
+ */
+DMNSN_INTERNAL void dmnsn_new_thread(dmnsn_progress *progress,
+ dmnsn_thread_fn *thread_fn, void *arg);
+
+/**
* Thread callback type for parallel tasks.
* @param[in,out] ptr An arbitrary pointer.
* @param[in] thread An ID for this thread, in [0, \p nthreads).
@@ -41,15 +52,6 @@ typedef int dmnsn_ccthread_fn(void *ptr, unsigned int thread,
unsigned int nthreads);
/**
- * Create a thread that cleans up after itself on errors.
- * @param[in,out] progress The progress object to associate with the thread.
- * @param[in] thread_fn The thread callback.
- * @param[in,out] arg The pointer to pass to the thread callback.
- */
-DMNSN_INTERNAL void dmnsn_new_thread(dmnsn_progress *progress,
- dmnsn_thread_fn *thread_fn, void *arg);
-
-/**
* Run \p nthreads threads in parallel.
* @param[in] ccthread_fn The routine to run in each concurrent thread.
* @param[in,out] arg The pointer to pass to the thread callbacks.
@@ -58,3 +60,134 @@ DMNSN_INTERNAL void dmnsn_new_thread(dmnsn_progress *progress,
*/
DMNSN_INTERNAL int dmnsn_execute_concurrently(dmnsn_ccthread_fn *ccthread_fn,
void *arg, unsigned int nthreads);
+
+/**
+ * Initialize a mutex, bailing out on failure.
+ * @param[out] mutex The mutex to initialize.
+ */
+DMNSN_INTERNAL void dmnsn_initialize_mutex(pthread_mutex_t *mutex);
+
+/** dmnsn_lock_mutex() implementation. */
+DMNSN_INTERNAL void dmnsn_lock_mutex_impl(pthread_mutex_t *mutex);
+/** dmnsn_unlock_mutex() implementation. */
+DMNSN_INTERNAL void dmnsn_unlock_mutex_impl(pthread_mutex_t *mutex);
+
+/**
+ * Lock a mutex, bailing out on failure.
+ * Contains a {, so must be used in the same block as dmnsn_unlock_mutex().
+ * @param[in,out] mutex The mutex to lock.
+ */
+#define dmnsn_lock_mutex(mutex) dmnsn_lock_mutex_impl((mutex)); {
+
+/**
+ * Lock a mutex, bailing out on failure.
+ * Contains a }, so must be used in the same block as dmnsn_lock_mutex().
+ * @param[in,out] mutex The mutex to unlock.
+ */
+#define dmnsn_unlock_mutex(mutex) dmnsn_unlock_mutex_impl((mutex)); }
+
+/**
+ * Destroy a mutex, warning on failure.
+ * @param[in,out] mutex The mutex to destroy.
+ */
+DMNSN_INTERNAL void dmnsn_destroy_mutex(pthread_mutex_t *mutex);
+
+/**
+ * Initialize a read-write lock, bailing out on failure.
+ * @param[out] rwlock The read-write lock to initialize.
+ */
+DMNSN_INTERNAL void dmnsn_initialize_rwlock(pthread_rwlock_t *rwlock);
+
+/** dmnsn_read_lock() implementation. */
+DMNSN_INTERNAL void dmnsn_read_lock_impl(pthread_rwlock_t *rwlock);
+/** dmnsn_write_lock() implementation. */
+DMNSN_INTERNAL void dmnsn_write_lock_impl(pthread_rwlock_t *rwlock);
+/** dmnsn_unlock_rwlock() implementation. */
+DMNSN_INTERNAL void dmnsn_unlock_rwlock_impl(pthread_rwlock_t *rwlock);
+
+/**
+ * Lock a read-write lock, bailing out on failure.
+ * Contains a {, so must be used in the same block as dmnsn_unlock_rwlock().
+ * @param[in,out] rwlock The read-write lock to lock.
+ */
+#define dmnsn_read_lock(rwlock) dmnsn_read_lock_impl((rwlock)); {
+
+/**
+ * Lock a read-write lock, bailing out on failure.
+ * Contains a {, so must be used in the same block as dmnsn_unlock_rwlock().
+ * @param[in,out] rwlock The read-write lock to lock.
+ */
+#define dmnsn_write_lock(rwlock) dmnsn_write_lock_impl((rwlock)); {
+
+/**
+ * Lock a read-write lock, bailing out on failure.
+ * Contains a }, so must be used in the same block as dmnsn_read_lock() or
+ * dmnsn_write_lock().
+ * @param[in,out] rwlock The read-write lock to lock.
+ */
+#define dmnsn_unlock_rwlock(rwlock) dmnsn_unlock_rwlock_impl((rwlock)); }
+
+/**
+ * Destroy a read-write lock, warning on failure.
+ * @param[in,out] rwlock The read-write lock to destroy.
+ */
+DMNSN_INTERNAL void dmnsn_destroy_rwlock(pthread_rwlock_t *rwlock);
+
+/**
+ * Initialize a condition variable, bailing out on failure.
+ * @param[out] cond The condition variable to initialize.
+ */
+DMNSN_INTERNAL void dmnsn_initialize_cond(pthread_cond_t *cond);
+
+/**
+ * Wait on a condition variable, bailing out on error.
+ * @param[in] cond The condition variable to wait on.
+ * @param[in] mutex The associated mutex.
+ */
+DMNSN_INTERNAL void dmnsn_cond_wait(pthread_cond_t *cond,
+ pthread_mutex_t *mutex);
+
+/**
+ * Signal a condition variable, bailing out on error.
+ * @param[in] cond The condition variable to signal.
+ */
+DMNSN_INTERNAL void dmnsn_cond_broadcast(pthread_cond_t *cond);
+
+/**
+ * Destroy a condition variable, warning on failure.
+ * @param[in,out] cond The condition variable to destroy.
+ */
+DMNSN_INTERNAL void dmnsn_destroy_cond(pthread_cond_t *cond);
+
+/**
+ * Once-called callback type.
+ */
+typedef void dmnsn_once_fn(void);
+
+/**
+ * Call a function exactly once, bailing out on failure.
+ * @param[in,out] once The once control.
+ * @param[in] once_fn The function to call.
+ */
+DMNSN_INTERNAL void dmnsn_once(pthread_once_t *once, dmnsn_once_fn *once_fn);
+
+/**
+ * Initialize a thread-local storage key, bailing out on failure.
+ * @param[out] key The key to initialize.
+ * @param[in] destructor An optional destructor callback.
+ */
+DMNSN_INTERNAL void dmnsn_key_create(pthread_key_t *key,
+ dmnsn_callback_fn *destructor);
+
+/**
+ * Set a thread-specific pointer, bailing out on failure.
+ * @param[in] key The thread-local storage key.
+ * @param[in] value The value to set.
+ */
+DMNSN_INTERNAL void dmnsn_setspecific(pthread_key_t key, const void *value);
+
+/**
+ * Destroy a thread-local storage key, warning out on failure.
+ * @param[out] key The key to destroy.
+ */
+DMNSN_INTERNAL void dmnsn_key_delete(pthread_key_t key);