Skip to content

Commit

Permalink
add yamc::fair::{timed_mutex, recursive_timed_mutex}
Browse files Browse the repository at this point in the history
  • Loading branch information
yohhoy committed Jan 23, 2017
1 parent f5f978d commit aee4958
Show file tree
Hide file tree
Showing 6 changed files with 158 additions and 19 deletions.
4 changes: 3 additions & 1 deletion README.md
Expand Up @@ -22,6 +22,8 @@ Note: [`std::mutex`'s default constructor][mutex_ctor] is constexpr, but `yamc::
- `yamc::checked::recursive_timed_mutex`: requirements debugging, recursive, support timeout
- `yamc::fair::mutex`: fairness, non-recursive
- `yamc::fair::recursive_mutex`: fairness, recursive
- `yamc::fair::timed_mutex`: fairness, non-recursive, support timeout
- `yamc::fair::recursive_timed_mutex`: fairness, recursive, support timeout
- `yamc::alternate::recursive_mutex`: recursive

C++11/14/17 Standard Library define variable mutex types:
Expand All @@ -43,7 +45,7 @@ C++11/14/17 Standard Library define variable mutex types:


# Tweaks
## Busy waiting for spinlock mutex
## Busy waiting in spinlock mutex
The spinlock mutexes use an exponential backoff algorithm in busy waiting to acquire lock as default.
These backoff algorithm of spinlock `mutex`-es are implemented with policy-based template class `basic_mutex<BackoffPolicy>`.
You can tweak the algorithm by specifying BackoffPolicy when you instantiate spinlock mutex type, or define the following macros to change default behavior of all spinlock mutex types.
Expand Down
157 changes: 141 additions & 16 deletions include/fair_mutex.hpp
Expand Up @@ -26,6 +26,8 @@
#ifndef YAMC_FAIR_MUTEX_HPP_
#define YAMC_FAIR_MUTEX_HPP_

#include <cassert>
#include <chrono>
#include <condition_variable>
#include <mutex>
#include <thread>
Expand All @@ -38,19 +40,15 @@ namespace yamc {
*/
namespace fair {

class mutex {
namespace detail {

class mutex_base {
protected:
std::size_t next_ = 0;
std::size_t curr_ = 0;
std::condition_variable cv_;
std::mutex mtx_;

public:
mutex() = default;
~mutex() = default;

mutex(const mutex&) = delete;
mutex& operator=(const mutex&) = delete;

void lock()
{
std::unique_lock<decltype(mtx_)> lk(mtx_);
Expand Down Expand Up @@ -78,21 +76,15 @@ class mutex {
};


class recursive_mutex {
class recursive_mutex_base {
protected:
std::size_t next_ = 0;
std::size_t curr_ = 0;
std::size_t ncount_ = 0;
std::thread::id owner_;
std::condition_variable cv_;
std::mutex mtx_;

public:
recursive_mutex() = default;
~recursive_mutex() = default;

recursive_mutex(const recursive_mutex&) = delete;
recursive_mutex& operator=(const recursive_mutex&) = delete;

void lock()
{
const auto tid = std::this_thread::get_id();
Expand Down Expand Up @@ -142,6 +134,139 @@ class recursive_mutex {
}
};

} // namespace detail


class mutex : private detail::mutex_base {
using base = detail::mutex_base;

public:
mutex() = default;
~mutex() = default;

mutex(const mutex&) = delete;
mutex& operator=(const mutex&) = delete;

using base::lock;
using base::try_lock;
using base::unlock;
};


class timed_mutex : private detail::mutex_base {
using base = detail::mutex_base;

template <typename Clock, typename Duration>
bool do_try_lockwait(const std::chrono::time_point<Clock, Duration>& tp)
{
std::unique_lock<decltype(mtx_)> lk(mtx_);
std::size_t request = next_;
while (request != curr_) {
if (cv_.wait_until(lk, tp) == std::cv_status::timeout) {
if (request == curr_) // re-check predicate
break;
return false;
}
}
++next_;
return true;
}

public:
timed_mutex() = default;
~timed_mutex() = default;

timed_mutex(const timed_mutex&) = delete;
timed_mutex& operator=(const timed_mutex&) = delete;

using base::lock;
using base::try_lock;
using base::unlock;

template <typename Rep, typename Period>
bool try_lock_for(const std::chrono::duration<Rep, Period>& duration)
{
const auto tp = std::chrono::system_clock::now() + duration;
return do_try_lockwait(tp);
}

template <typename Clock, typename Duration>
bool try_lock_until(const std::chrono::time_point<Clock, Duration>& tp)
{
return do_try_lockwait(tp);
}
};


class recursive_mutex : private detail::recursive_mutex_base {
using base = detail::recursive_mutex_base;

public:
recursive_mutex() = default;
~recursive_mutex() = default;

recursive_mutex(const recursive_mutex&) = delete;
recursive_mutex& operator=(const recursive_mutex&) = delete;

using base::lock;
using base::try_lock;
using base::unlock;
};


class recursive_timed_mutex : private detail::recursive_mutex_base {
using base = detail::recursive_mutex_base;

template <typename Clock, typename Duration>
bool do_try_lockwait(const std::chrono::time_point<Clock, Duration>& tp)
{
const auto tid = std::this_thread::get_id();
std::unique_lock<decltype(mtx_)> lk(mtx_);
if (owner_ == tid) {
assert(0 < ncount_);
++ncount_;
return true;
}
std::size_t request = next_;
while (request != curr_) {
if (cv_.wait_until(lk, tp) == std::cv_status::timeout) {
if (request == curr_) // re-check predicate
break;
return false;
}
}
++next_;
assert(ncount_ == 0 && owner_ == std::thread::id());
ncount_ = 1;
owner_ = tid;
return true;
}

public:
recursive_timed_mutex() = default;
~recursive_timed_mutex() = default;

recursive_timed_mutex(const recursive_timed_mutex&) = delete;
recursive_timed_mutex& operator=(const recursive_timed_mutex&) = delete;

using base::lock;
using base::try_lock;
using base::unlock;

template <typename Rep, typename Period>
bool try_lock_for(const std::chrono::duration<Rep, Period>& duration)
{
const auto tp = std::chrono::system_clock::now() + duration;
return do_try_lockwait(tp);
}

template <typename Clock, typename Duration>
bool try_lock_until(const std::chrono::time_point<Clock, Duration>& tp)
{
return do_try_lockwait(tp);
}
};

} // namespace fair
} // namespace yamc

Expand Down
6 changes: 5 additions & 1 deletion tests/basic_test.cpp
Expand Up @@ -89,7 +89,9 @@ TYPED_TEST(NormalMutexTest, TryLockFail)

using RecursiveMutexTypes = ::testing::Types<
yamc::checked::recursive_mutex,
yamc::checked::recursive_timed_mutex,
yamc::fair::recursive_mutex,
yamc::fair::recursive_timed_mutex,
yamc::alternate::recursive_mutex
>;

Expand Down Expand Up @@ -182,7 +184,9 @@ TYPED_TEST(RecursiveMutexTest, TryLockFail)

using TimedMutexTypes = ::testing::Types<
yamc::checked::timed_mutex,
yamc::checked::recursive_timed_mutex
yamc::checked::recursive_timed_mutex,
yamc::fair::timed_mutex,
yamc::fair::recursive_timed_mutex
>;

template <typename Mutex>
Expand Down
4 changes: 4 additions & 0 deletions tests/compile_test.cpp
Expand Up @@ -78,8 +78,12 @@ int main()
test_requirements<yamc::checked::recursive_mutex>();
test_requirements_timed<yamc::checked::timed_mutex>();
test_requirements_timed<yamc::checked::recursive_timed_mutex>();

test_requirements<yamc::fair::mutex>();
test_requirements<yamc::fair::recursive_mutex>();
test_requirements_timed<yamc::fair::timed_mutex>();
test_requirements_timed<yamc::fair::recursive_timed_mutex>();

test_requirements<yamc::alternate::recursive_mutex>();
return 0;
}
2 changes: 2 additions & 0 deletions tests/dump_sizeof.cpp
Expand Up @@ -33,6 +33,8 @@ int main()
DUMP(yamc::checked::recursive_timed_mutex);
DUMP(yamc::fair::mutex);
DUMP(yamc::fair::recursive_mutex);
DUMP(yamc::fair::timed_mutex);
DUMP(yamc::fair::recursive_timed_mutex);
DUMP(yamc::alternate::recursive_mutex);
return 0;
}
4 changes: 3 additions & 1 deletion tests/fairness_test.cpp
Expand Up @@ -28,7 +28,9 @@ std::mutex g_guard;

using FairMutexTypes = ::testing::Types<
yamc::fair::mutex,
yamc::fair::recursive_mutex
yamc::fair::recursive_mutex,
yamc::fair::timed_mutex,
yamc::fair::recursive_timed_mutex
>;

template <typename Mutex>
Expand Down

0 comments on commit aee4958

Please sign in to comment.