I have an algorithm that is painfully slow if I use 'pure' mutexes. That's because most of the critical sections are short and far shorter than the work to sleep a thread. However, it is also slower to allow the occasional long critical section cause long spins in other thread(s). This class is intended to provide a balance. The balance here is compile-time fixed. What would be interesting is an adaptive strategy for tuning the spin time.
#include <atomic>
#include <condition_variable>
#include <mutex>
///<summary>A hybrid lock making a number of spin attempts
///and then entering a wait before retrying.</summary>
///<remarks>Implemented to be interface swappable
///with <code>std::mutex</code>.</remarks>
template<unsigned SPIN_LIMIT>
class hybrid_lock {
public:
typedef unsigned spin_count_type;
typedef void *native_handle_type;
inline void lock() {
spin_count_type limit(0);
for (;;) {
if (!this->mFlag.test_and_set(std::memory_order::memory_order_acquire)) {
return;
}
if (limit >= SPIN_LIMIT) {
break;
}
++limit;
}
std::unique_lock<std::mutex> guard(this->mMutex);
this->mCondition.wait(guard, [this]() {
return !this>mFlag.test_and_set(
std::memory_order::memory_order_acquire
);
});
}
inline bool try_lock() {
return !this->mFlag.test_and_set(
std::memory_order::memory_order_acquire
);
}
inline void unlock() {
this->mFlag.clear(std::memory_order::memory_order_release);
this->mCondition.notify_one();
}
///<summary>Returns the address of the flag backing the lock.</summary>
///<remarks>The return value may not be compatible with
///<code>std::mutex</code>.</remarks>
///<returns>The address of the flag
///backing the spin-lock.</returns>
inline native_handle_type native_handle() {
return static_cast<native_handle_type>(&this->mFlag);
}
inline hybrid_lock(void) {
mFlag.clear();
}
private:
std::atomic_flag mFlag;//The actual lock object.
std::mutex mMutex;//Used to synchronize sleeping threads.
std::condition_variable mCondition;
hybrid_lock(const hybrid_lock&) = delete;
hybrid_lock& operator=(const hybrid_lock&) = delete;
};