summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorTavian Barnes <tavianator@tavianator.com>2013-03-22 21:41:45 -0400
committerTavian Barnes <tavianator@tavianator.com>2013-03-22 21:41:45 -0400
commitc324c3a9e7558e87ec628d45d3d0577d614ee350 (patch)
treec175f3e81fa765a571f82566decc8f54af0b548a
parent62fac6e15e51422a544de01ce6cb01b0921a6706 (diff)
downloaddimension-faster-futures.tar.xz
Use spinlock for futures when possible.faster-futures
-rw-r--r--configure.ac34
-rw-r--r--libdimension/bench/future.c4
-rw-r--r--libdimension/future-impl.h5
-rw-r--r--libdimension/future.c89
-rw-r--r--libdimension/threads.c48
-rw-r--r--libdimension/threads.h64
6 files changed, 189 insertions, 55 deletions
diff --git a/configure.ac b/configure.ac
index 1141ac8..8faf2bb 100644
--- a/configure.ac
+++ b/configure.ac
@@ -1,4 +1,4 @@
-dnl Copyright (C) 2009-2012 Tavian Barnes <tavianator@tavianator.com>
+dnl Copyright (C) 2009-2013 Tavian Barnes <tavianator@tavianator.com>
dnl
dnl This file is part of The Dimension Build Suite.
dnl
@@ -231,6 +231,38 @@ AC_COMPILE_IFELSE([
AC_MSG_RESULT([no])]
)
+save_CFLAGS="$CFLAGS"
+save_LIBS="$LIBS"
+CFLAGS="$CFLAGS $PTHREAD_CFLAGS"
+LIBS="$LIBS $PTHREAD_LIBS"
+AC_MSG_CHECKING([for pthread_spinlock_t])
+AC_RUN_IFELSE([
+ AC_LANG_PROGRAM(
+ [
+ #include <pthread.h>
+ #include <stdlib.h>
+ ],
+ [
+ pthread_spinlock_t spinlock;
+ if (pthread_spin_init(&spinlock, PTHREAD_PROCESS_PRIVATE) != 0) {
+ return EXIT_FAILURE;
+ }
+ if (pthread_spin_lock(&spinlock) != 0) {
+ return EXIT_FAILURE;
+ }
+ if (pthread_spin_unlock(&spinlock) != 0) {
+ return EXIT_FAILURE;
+ }
+ ]
+ )],
+ [AC_DEFINE([DMNSN_SPINLOCK], [1])
+ AC_MSG_RESULT([yes])],
+ [AC_DEFINE([DMNSN_SPINLOCK], [0])
+ AC_MSG_RESULT([no])]
+)
+CFLAGS="$save_CFLAGS"
+LIBS="$save_LIBS"
+
AC_CHECK_HEADER([GL/glx.h], [have_glx=yes], [have_glx=no])
if test "$enable_gl" = "yes" -a "$have_glx" = "yes"; then
AC_CHECK_LIB([X11], [XOpenDisplay], [], [have_glx=no])
diff --git a/libdimension/bench/future.c b/libdimension/bench/future.c
index 5937c3c..3a9de4a 100644
--- a/libdimension/bench/future.c
+++ b/libdimension/bench/future.c
@@ -53,9 +53,9 @@ dmnsn_bench_thread(void *ptr)
printf("dmnsn_future_increment(): %ld\n", sandglass.grains);
/* Reset the progress. */
- dmnsn_lock_mutex(&future->mutex);
+ dmnsn_lock_future(future);
future->progress = 0;
- dmnsn_unlock_mutex(&future->mutex);
+ dmnsn_unlock_future(future);
/* Now run a bunch of increments concurrently. */
return dmnsn_execute_concurrently(&dmnsn_bench_future, future, nthreads);
diff --git a/libdimension/future-impl.h b/libdimension/future-impl.h
index 4ec6f75..c8e5d63 100644
--- a/libdimension/future-impl.h
+++ b/libdimension/future-impl.h
@@ -42,6 +42,11 @@ struct dmnsn_future {
/** The worker thread. */
pthread_t thread;
+#if DMNSN_SPINLOCK
+ /** If spinlocks are supported, use them instead of mutexes. */
+ pthread_spinlock_t spinlock;
+#endif
+
/** Mutex to guard progress and total. */
pthread_mutex_t mutex;
diff --git a/libdimension/future.c b/libdimension/future.c
index 6ea15ae..555330f 100644
--- a/libdimension/future.c
+++ b/libdimension/future.c
@@ -41,6 +41,9 @@ dmnsn_new_future(void)
future->progress = 0;
future->total = 1;
+#if DMNSN_SPINLOCK
+ dmnsn_initialize_spinlock(&future->spinlock, PTHREAD_PROCESS_PRIVATE);
+#endif
dmnsn_initialize_mutex(&future->mutex);
dmnsn_initialize_cond(&future->cond);
@@ -67,6 +70,9 @@ dmnsn_future_join(dmnsn_future *future)
/* Free the future object */
dmnsn_destroy_cond(&future->cond);
dmnsn_destroy_mutex(&future->mutex);
+#if DMNSN_SPINLOCK
+ dmnsn_destroy_spinlock(&future->spinlock);
+#endif
dmnsn_free(future);
}
@@ -80,11 +86,49 @@ dmnsn_future_cancel(dmnsn_future *future)
pthread_cancel(future->thread);
}
-/**
- * Get the current progress, without locking anything.
- *
- * future->mutex must be locked for this call to be safe.
- */
+/** Lock the appropriate lock for a future. */
+static inline void
+dmnsn_lock_future(const dmnsn_future *future)
+{
+#if DMNSN_SPINLOCK
+ dmnsn_lock_spinlock(&MUTATE(future)->spinlock);
+#else
+ dmnsn_lock_mutex(&MUTATE(future)->mutex);
+#endif
+}
+
+/** Unlock the appropriate lock for a future. */
+static inline void
+dmnsn_unlock_future(const dmnsn_future *future)
+{
+#if DMNSN_SPINLOCK
+ dmnsn_unlock_spinlock(&MUTATE(future)->spinlock);
+#else
+ dmnsn_unlock_mutex(&MUTATE(future)->mutex);
+#endif
+}
+
+/** Lock the future's mutex specifically, if we have the spinlock already. */
+static inline void
+dmnsn_future_exchange_lock(const dmnsn_future *future)
+{
+#if DMNSN_SPINLOCK
+ dmnsn_unlock_spinlock(&MUTATE(future)->spinlock);
+ dmnsn_lock_mutex(&MUTATE(future)->mutex);
+#endif
+}
+
+/** Unlock the future's mutex specifically, if we have the spinlock already. */
+static inline void
+dmnsn_future_exchange_unlock(const dmnsn_future *future)
+{
+#if DMNSN_SPINLOCK
+ dmnsn_unlock_mutex(&MUTATE(future)->mutex);
+ dmnsn_lock_spinlock(&MUTATE(future)->spinlock);
+#endif
+}
+
+/** Get the current progress, without locking anything. */
static inline double
dmnsn_future_progress_unlocked(const dmnsn_future *future)
{
@@ -95,12 +139,11 @@ dmnsn_future_progress_unlocked(const dmnsn_future *future)
double
dmnsn_future_progress(const dmnsn_future *future)
{
- dmnsn_future *mfuture = MUTATE(future);
double progress;
- dmnsn_lock_mutex(&mfuture->mutex);
- progress = dmnsn_future_progress_unlocked(mfuture);
- dmnsn_unlock_mutex(&mfuture->mutex);
+ dmnsn_lock_future(future);
+ progress = dmnsn_future_progress_unlocked(future);
+ dmnsn_unlock_future(future);
return progress;
}
@@ -111,24 +154,26 @@ dmnsn_future_wait(const dmnsn_future *future, double progress)
{
dmnsn_future *mfuture = MUTATE(future);
- dmnsn_lock_mutex(&mfuture->mutex);
+ dmnsn_lock_future(mfuture);
while (dmnsn_future_progress_unlocked(mfuture) < progress) {
/* Set the minimum waited-on value */
if (progress < mfuture->min_wait)
mfuture->min_wait = progress;
- dmnsn_cond_wait(&mfuture->cond, &mfuture->mutex);
+ dmnsn_future_exchange_lock(mfuture);
+ dmnsn_cond_wait(&mfuture->cond, &mfuture->mutex);
+ dmnsn_future_exchange_unlock(mfuture);
}
- dmnsn_unlock_mutex(&mfuture->mutex);
+ dmnsn_unlock_future(mfuture);
}
/* Set the total number of loop iterations */
void
dmnsn_future_set_total(dmnsn_future *future, size_t total)
{
- dmnsn_lock_mutex(&future->mutex);
+ dmnsn_lock_future(future);
future->total = total;
- dmnsn_unlock_mutex(&future->mutex);
+ dmnsn_unlock_future(future);
}
/* Increment the number of completed loop iterations */
@@ -140,22 +185,26 @@ dmnsn_future_increment(dmnsn_future *future)
on cancellation */
pthread_testcancel();
- dmnsn_lock_mutex(&future->mutex);
+ dmnsn_lock_future(future);
++future->progress;
if (dmnsn_future_progress_unlocked(future) >= future->min_wait) {
future->min_wait = 1.0;
- dmnsn_cond_broadcast(&future->cond);
+ dmnsn_future_exchange_lock(future);
+ dmnsn_cond_broadcast(&future->cond);
+ dmnsn_future_exchange_unlock(future);
}
- dmnsn_unlock_mutex(&future->mutex);
+ dmnsn_unlock_future(future);
}
/* Immediately set to 100% completion */
void
dmnsn_future_done(dmnsn_future *future)
{
- dmnsn_lock_mutex(&future->mutex);
+ dmnsn_lock_future(future);
future->progress = future->total;
- dmnsn_cond_broadcast(&future->cond);
- dmnsn_unlock_mutex(&future->mutex);
+ dmnsn_future_exchange_lock(future);
+ dmnsn_cond_broadcast(&future->cond);
+ dmnsn_future_exchange_unlock(future);
+ dmnsn_unlock_future(future);
}
diff --git a/libdimension/threads.c b/libdimension/threads.c
index 0aed16d..9749976 100644
--- a/libdimension/threads.c
+++ b/libdimension/threads.c
@@ -1,5 +1,5 @@
/*************************************************************************
- * Copyright (C) 2010-2011 Tavian Barnes <tavianator@tavianator.com> *
+ * Copyright (C) 2010-2013 Tavian Barnes <tavianator@tavianator.com> *
* *
* This file is part of The Dimension Library. *
* *
@@ -169,7 +169,7 @@ dmnsn_initialize_mutex(pthread_mutex_t *mutex)
}
void
-dmnsn_lock_mutex_impl(pthread_mutex_t *mutex)
+dmnsn_lock_mutex(pthread_mutex_t *mutex)
{
if (pthread_mutex_lock(mutex) != 0) {
dmnsn_error("Couldn't lock mutex.");
@@ -177,7 +177,7 @@ dmnsn_lock_mutex_impl(pthread_mutex_t *mutex)
}
void
-dmnsn_unlock_mutex_impl(pthread_mutex_t *mutex)
+dmnsn_unlock_mutex(pthread_mutex_t *mutex)
{
if (pthread_mutex_unlock(mutex) != 0) {
dmnsn_error("Couldn't unlock mutex.");
@@ -201,7 +201,7 @@ dmnsn_initialize_rwlock(pthread_rwlock_t *rwlock)
}
void
-dmnsn_read_lock_impl(pthread_rwlock_t *rwlock)
+dmnsn_read_lock(pthread_rwlock_t *rwlock)
{
if (pthread_rwlock_rdlock(rwlock) != 0) {
dmnsn_error("Couldn't acquire read lock.");
@@ -209,7 +209,7 @@ dmnsn_read_lock_impl(pthread_rwlock_t *rwlock)
}
void
-dmnsn_write_lock_impl(pthread_rwlock_t *rwlock)
+dmnsn_write_lock(pthread_rwlock_t *rwlock)
{
if (pthread_rwlock_wrlock(rwlock) != 0) {
dmnsn_error("Couldn't acquire write lock.");
@@ -217,7 +217,7 @@ dmnsn_write_lock_impl(pthread_rwlock_t *rwlock)
}
void
-dmnsn_unlock_rwlock_impl(pthread_rwlock_t *rwlock)
+dmnsn_unlock_rwlock(pthread_rwlock_t *rwlock)
{
if (pthread_rwlock_unlock(rwlock) != 0) {
dmnsn_error("Couldn't unlock read-write lock.");
@@ -232,6 +232,42 @@ dmnsn_destroy_rwlock(pthread_rwlock_t *rwlock)
}
}
+#if DMNSN_SPINLOCK
+
+void
+dmnsn_initialize_spinlock(pthread_spinlock_t *lock, int pshared)
+{
+ if (pthread_spin_init(lock, pshared) != 0) {
+ dmnsn_error("Couldn't initialize spinlock.");
+ }
+}
+
+void
+dmnsn_lock_spinlock(pthread_spinlock_t *lock)
+{
+ if (pthread_spin_lock(lock) != 0) {
+ dmnsn_error("Couldn't lock spinlock.");
+ }
+}
+
+void
+dmnsn_unlock_spinlock(pthread_spinlock_t *lock)
+{
+ if (pthread_spin_unlock(lock) != 0) {
+ dmnsn_error("Couldn't unlock spinlock.");
+ }
+}
+
+void
+dmnsn_destroy_spinlock(pthread_spinlock_t *lock)
+{
+ if (pthread_spin_destroy(lock) != 0) {
+ dmnsn_warning("Couldn't destroy spinlock.");
+ }
+}
+
+#endif /* DMNSN_SPINLOCK */
+
void
dmnsn_initialize_cond(pthread_cond_t *cond)
{
diff --git a/libdimension/threads.h b/libdimension/threads.h
index ea9fb2a..7b7aae6 100644
--- a/libdimension/threads.h
+++ b/libdimension/threads.h
@@ -1,5 +1,5 @@
/*************************************************************************
- * Copyright (C) 2010-2011 Tavian Barnes <tavianator@tavianator.com> *
+ * Copyright (C) 2010-2013 Tavian Barnes <tavianator@tavianator.com> *
* *
* This file is part of The Dimension Library. *
* *
@@ -67,24 +67,17 @@ DMNSN_INTERNAL int dmnsn_execute_concurrently(dmnsn_ccthread_fn *ccthread_fn,
*/
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)); {
+DMNSN_INTERNAL void dmnsn_lock_mutex(pthread_mutex_t *mutex);
/**
- * Lock a mutex, bailing out on failure.
- * Contains a }, so must be used in the same block as dmnsn_lock_mutex().
+ * Unlock a mutex, bailing out on failure.
* @param[in,out] mutex The mutex to unlock.
*/
-#define dmnsn_unlock_mutex(mutex) dmnsn_unlock_mutex_impl((mutex)); }
+DMNSN_INTERNAL void dmnsn_unlock_mutex(pthread_mutex_t *mutex);
/**
* Destroy a mutex, warning on failure.
@@ -98,34 +91,23 @@ DMNSN_INTERNAL void dmnsn_destroy_mutex(pthread_mutex_t *mutex);
*/
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)); {
+DMNSN_INTERNAL void dmnsn_read_lock(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_write_lock(rwlock) dmnsn_write_lock_impl((rwlock)); {
+DMNSN_INTERNAL void dmnsn_write_lock(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_read_lock() or
- * dmnsn_write_lock().
+ * Unlock a read-write lock, bailing out on failure.
* @param[in,out] rwlock The read-write lock to lock.
*/
-#define dmnsn_unlock_rwlock(rwlock) dmnsn_unlock_rwlock_impl((rwlock)); }
+DMNSN_INTERNAL void dmnsn_unlock_rwlock(pthread_rwlock_t *rwlock);
/**
* Destroy a read-write lock, warning on failure.
@@ -133,6 +115,36 @@ DMNSN_INTERNAL void dmnsn_unlock_rwlock_impl(pthread_rwlock_t *rwlock);
*/
DMNSN_INTERNAL void dmnsn_destroy_rwlock(pthread_rwlock_t *rwlock);
+#if DMNSN_SPINLOCK
+
+/**
+ * Initialize a read-write lock, bailing out on failure.
+ * @param[out] lock The spinlock to initialize.
+ * @param[in] pshared The process-specific flag (PTHREAD_PROCESS_SHARED or
+ * PTHREAD_PROCESS_PRIVATE).
+ */
+DMNSN_INTERNAL void dmnsn_initialize_spinlock(pthread_spinlock_t *lock, int pshared);
+
+/**
+ * Lock the given spinlock, bailing out on failure.
+ * @param[in] lock The spinlock to lock.
+ */
+DMNSN_INTERNAL void dmnsn_lock_spinlock(pthread_spinlock_t *lock);
+
+/**
+ * Unlock the given spinlock, bailing out on failure.
+ * @param[in] lock The spinlock to unlock.
+ */
+DMNSN_INTERNAL void dmnsn_unlock_spinlock(pthread_spinlock_t *lock);
+
+/**
+ * Destroy a spinlock lock, warning on failure.
+ * @param[in,out] lock The spinlock to destroy.
+ */
+DMNSN_INTERNAL void dmnsn_destroy_spinlock(pthread_spinlock_t *lock);
+
+#endif /* DMNSN_SPINLOCK */
+
/**
* Initialize a condition variable, bailing out on failure.
* @param[out] cond The condition variable to initialize.