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. --- libdimension/future.c | 89 +++++++++++++++++++++++++++++++++++++++------------ 1 file changed, 69 insertions(+), 20 deletions(-) (limited to 'libdimension/future.c') 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); } -- cgit v1.2.3