[ create a new paste ] login | about

Link: http://codepad.org/s63ffXRH    [ raw code | fork ]

C++, pasted on Jun 30:
#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


Create a new paste based on this one


Comments: