From c324c3a9e7558e87ec628d45d3d0577d614ee350 Mon Sep 17 00:00:00 2001 From: Tavian Barnes Date: Fri, 22 Mar 2013 21:41:45 -0400 Subject: Use spinlock for futures when possible. --- configure.ac | 34 ++++++++++++++++- libdimension/bench/future.c | 4 +- libdimension/future-impl.h | 5 +++ libdimension/future.c | 89 +++++++++++++++++++++++++++++++++++---------- libdimension/threads.c | 48 +++++++++++++++++++++--- libdimension/threads.h | 64 +++++++++++++++++++------------- 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 +dnl Copyright (C) 2009-2013 Tavian Barnes 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 + #include + ], + [ + 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 * + * Copyright (C) 2010-2013 Tavian Barnes * * * * 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 * + * Copyright (C) 2010-2013 Tavian Barnes * * * * 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. -- cgit v1.2.3