#pragma once
#define __STDC_LIMIT_MACROS
#include <iostream>
#include <array>
#include <mutex>
#include <memory>
#include <functional>
#include <algorithm>
#include <thread>
#include <condition_variable>
namespace syncope {
class pool_guard_base {};
typedef const pool_guard_base& pool_guard;
// default hash function
template<typename T>
inline size_t lock_hash(T const& obj, size_t lock_count) {
return (reinterpret_cast<size_t>(&obj) / sizeof(T)) % lock_count;
}
class write_starvation_lock
{
public:
write_starvation_lock::write_starvation_lock() : locks_(0), waiting_readers_(0)
{}
write_starvation_lock(const write_starvation_lock&) = delete;
write_starvation_lock& operator =(const write_starvation_lock&) = delete;
void write_starvation_lock::rdlock()
{
std::unique_lock<std::mutex> lock(mtx_);
++waiting_readers_;
while(locks_ < 0) read_condition_.wait(lock);
--waiting_readers_;
++locks_;
}
void write_starvation_lock::lock()
{
std::unique_lock<std::mutex> lock(mtx_);
while(locks_ != 0) write_condition_.wait(lock);
locks_ = -1;
}
void write_starvation_lock::unlock()
{
std::unique_lock<std::mutex> lock(mtx_);
// reader unlock
if(locks_ > 0) {
--locks_;
if(locks_ == 0)
write_condition_.notify_one();
}
// writer unlock
else {
locks_ = 0;
if(waiting_readers_ != 0)
read_condition_.notify_all();
else
write_condition_.notify_one();
}
}
private:
std::mutex mtx_;
std::condition_variable read_condition_;
std::condition_variable write_condition_;
int locks_;
int waiting_readers_;
};
inline void lock_for_reading(std::mutex& mtx) { mtx.lock(); }
inline void lock_for_reading(write_starvation_lock& mtx) { mtx.rdlock(); }
template<typename pool_type, bool read, class T>
class pool_guard_one : public pool_guard_base {
pool_type& pool_;
const size_t hash_;
mutable bool owns_lock_;
void lock() const {
if(read) pool_.rdlock(hash_);
else pool_.lock(hash_);
owns_lock_ = true;
}
void unlock() const {
pool_.unlock(hash_);
owns_lock_ = false;
}
public:
pool_guard_one(pool_type& pool, T const& obj)
: pool_(pool)
, owns_lock_(false)
{
using namespace syncope;
hash_ = lock_hash(obj, pool_type::lock_count);
lock();
}
pool_guard_one(pool_guard_one const&) = delete;
pool_guard_one& operator = (pool_guard_one const&) = delete;
pool_guard_one(pool_guard_one&& other)
: pool_(other.pool_)
, hash_(other.hash_)
, owns_lock_(other.owns_lock_)
{
other.owns_lock_ = false;
}
pool_guard_one& operator = (pool_guard_one&& other) {
value_ = other.value_;
other.owns_lock_ = false;
assert(&pool_ == &other.pool_);
}
~pool_guard_one() {
if(owns_lock_) {
unlock();
}
}
};
template<typename pool_type, bool read, typename... T>
class pool_guard_many : public pool_guard_base {
enum {
H = sizeof...(T) // hashes array size (can be greater than sizeof...(T))
};
pool_type& pool_;
typedef std::array<size_t, H> hash_array;
hash_array hashes_;
size_t hash_count_;
template<size_t I>
struct fill_hashes
{
void operator () (std::array<size_t, H>& hashes, std::tuple<T const&...> const& items) {
using namespace syncope;
hashes[I] = lock_hash(std::get<I>(items), pool_type::lock_count);
fill_hashes<I+1>()(hashes, items);
}
};
template<>
struct fill_hashes<H>{
void operator() (std::array<size_t, H>& hashes, std::tuple<T const&...> const& items) {}
};
void lock() {
for(size_t i = 0; i < hash_count_; ++i) {
if(read) pool_.rdlock(hashes_[i]);
else pool_.lock(hashes_[i]);
}
}
void unlock() {
while(hash_count_) {
pool_.unlock(hashes_[--hash_count_]);
}
}
public:
pool_guard_many(pool_type& pool, T const&... objects)
: pool_(pool)
{
auto all = std::tie(objects...);
fill_hashes<0> fill_all;
fill_all(hashes_, all);
std::sort(hashes_.begin(), hashes_.end());
hash_array::iterator it = std::unique(hashes_.begin(), hashes_.end());
hash_count_ = std::distance(hashes_.begin(), it);
lock();
}
~pool_guard_many() {
if(hash_count_) {
unlock();
}
}
pool_guard_many(pool_guard_many const&) = delete;
pool_guard_many& operator = (pool_guard_many const&) = delete;
pool_guard_many(pool_guard_many&& other)
: pool_(other.pool_)
, hash_count_(other.hash_count_)
{
std::swap(hashes_, other.hashes_);
other.hash_count_ = 0;
}
pool_guard_many& operator = (pool_guard_many&& other) {
assert(&other.pool_ == &pool_);
std::swap(hashes_, other.hashes_);
hash_count_ = other.hash_count_;
other.hash_count_ = 0;
}
};
template<typename t_lock, size_t count>
class lock_pool {
typedef lock_pool<t_lock, count> pool_type;
mutable std::array<t_lock, count> locks_;
const char* name_;
public:
static const size_t lock_count = count;
public:
lock_pool(const char* name) : name_(name) {}
lock_pool(lock_pool const&) = delete;
lock_pool& operator = (lock_pool const&) = delete;
void lock(size_t hash) const {
std::cout << name_ << " lock " << hash << std::endl;
locks_[hash].lock();
}
void rdlock(size_t hash) const {
std::cout << name_ << " read lock " << hash << std::endl;
using namespace syncope;
lock_for_reading(locks_[hash]);
}
void unlock(size_t hash) const {
std::cout << name_ << " unlock " << hash << std::endl;
locks_[hash].unlock();
}
template<typename T>
pool_guard_one<pool_type, false, T> synchronize(T const& arg) {
return std::move(pool_guard_one<pool_type, false, T>(*this, arg));
}
template<typename... T>
pool_guard_many<pool_type, false, T...> synchronize(T const&... args) {
return std::move(pool_guard_many<pool_type, false, T...>(*this, args...));
}
template<typename T>
pool_guard_one<pool_type, true, T> synchronize_read(T const& arg) {
return std::move(pool_guard_one<pool_type, true, T>(*this, arg));
}
template<typename... T>
pool_guard_many<pool_type, true, T...> synchronize_read(T const&... args) {
return std::move(pool_guard_many<pool_type, true, T...>(*this, args...));
}
};
} // namespace syncope