libshit

Just some random shit
git clone https://git.neptards.moe/neptards/libshit.git
Log | Files | Refs | Submodules | README | LICENSE

shared_mutex (4906B)


      1 // -*- c++ -*-
      2 
      3 #include <condition_variable>
      4 #include <mutex>
      5 #include <system_error>
      6 
      7 #define MUTEX_ASSERT(x) //assert(x)
      8 //#include <cassert>
      9 
     10 namespace std
     11 {
     12 
     13   // https://stackoverflow.com/questions/8635963/read-write-lock-implementation-using-mutex-only
     14   class shared_mutex
     15   {
     16   public:
     17     shared_mutex() = default;
     18     shared_mutex(const shared_mutex&) = delete;
     19     void operator=(const shared_mutex&) = delete;
     20 
     21     void lock()
     22     {
     23       std::unique_lock l{mtx};
     24       ++wait_wr;
     25       while (no != 0)
     26       {
     27         if (no == HAS_WR) // recursive locking
     28           throw std::system_error(std::make_error_code(
     29                                     std::errc::resource_deadlock_would_occur));
     30         wr.wait(l);
     31       }
     32       --wait_wr;
     33       no = HAS_WR;
     34     }
     35 
     36     bool try_lock()
     37     {
     38       std::unique_lock l{mtx, std::try_to_lock};
     39       if (!l || no != 0) return false;
     40       no = HAS_WR;
     41       return true;
     42     }
     43 
     44     void unlock() // noexcept
     45     {
     46       std::unique_lock l{mtx};
     47       MUTEX_ASSERT(no == HAS_WR);
     48       no = 0;
     49       if (wait_wr) wr.notify_one();
     50       else if (wait_rd) rd.notify_all();
     51     }
     52 
     53     void lock_shared()
     54     {
     55       std::unique_lock l{mtx};
     56       ++wait_rd;
     57       rd.wait(l, [this]() { return no != HAS_WR; });
     58       --wait_rd;
     59       ++no;
     60     }
     61 
     62     bool try_lock_shared()
     63     {
     64       std::unique_lock l{mtx, std::try_to_lock};
     65       if (!l || no == HAS_WR) return false;
     66       ++no;
     67       return true;
     68     }
     69 
     70     void unlock_shared() // noexcept
     71     {
     72       std::unique_lock l{mtx};
     73       MUTEX_ASSERT(no);
     74       MUTEX_ASSERT(no != HAS_WR);
     75       --no;
     76       if (no == 0 && wait_wr) wr.notify_one();
     77     }
     78 
     79   private:
     80     mutex mtx;
     81     condition_variable rd, wr;
     82     unsigned no = 0, wait_rd = 0, wait_wr = 0;
     83     static inline constexpr const unsigned HAS_WR = -1;
     84   };
     85 
     86   template <typename Mutex>
     87   class shared_lock
     88   {
     89   public:
     90     using mutex_type = Mutex;
     91     shared_lock() noexcept = default;
     92     shared_lock(shared_lock&& o) noexcept : m{o.m}, own{o.own}
     93     {
     94       o.m = nullptr;
     95       o.own = false;
     96     }
     97     explicit shared_lock(Mutex& m) : m{&m}, own{true} { m.lock_shared(); }
     98     shared_lock(Mutex& m, std::defer_lock_t) noexcept : m{&m}, own{false} {}
     99     shared_lock(Mutex& m, std::try_to_lock_t) : m{&m}, own{m.try_lock_shared()} {}
    100     shared_lock(Mutex& m, std::adopt_lock_t) : m{&m}, own{true} {}
    101 
    102     template <typename Rep, typename Period>
    103     shared_lock(Mutex& m, const std::chrono::duration<Rep, Period>& timeout)
    104       : m{&m}, own{true}
    105     { m.try_lock_shared_for(timeout); }
    106     template <typename Clock, typename Duration>
    107     shared_lock(Mutex& m, const std::chrono::time_point<Clock, Duration>& time)
    108       : m{&m}, own{true}
    109     { m.try_lock_shared_until(time); }
    110 
    111     ~shared_lock() noexcept { if (m && own) m->unlock_shared(); }
    112 
    113     shared_lock& operator=(shared_lock&& o) noexcept
    114     {
    115       if (m && own) m->unlock_shared();
    116       m = o.m; own = o.own;
    117       o.m = nullptr; o.own = false;
    118     }
    119 
    120     void lock()
    121     {
    122       CheckLockable();
    123       m->lock_shared();
    124       own = true;
    125     }
    126 
    127     bool try_lock()
    128     {
    129       CheckLockable();
    130       return own = m->try_lock_shared();
    131     }
    132 
    133     template <typename Rep, typename Period>
    134     bool try_lock_for(const std::chrono::duration<Rep, Period>& timeout)
    135     {
    136       CheckLockable();
    137       return own = m->try_lcok_shared_for(timeout);
    138     }
    139 
    140     template <typename Clock, typename Duration>
    141     bool try_lock_for(const std::chrono::time_point<Clock, Duration>& time)
    142     {
    143       CheckLockable();
    144       return own = m->try_lcok_shared_until(time);
    145     }
    146 
    147     void unlock()
    148     {
    149       // todo: what the hell am I supposed to throw when !own?
    150       if (!m || !own)
    151         throw std::system_error(std::make_error_code(
    152                                   std::errc::operation_not_permitted));
    153       m->unlock_shared();
    154     }
    155 
    156     // cppreference says it's a template, wtf? it shouldn't be
    157     // https://timsong-cpp.github.io/cppwp/n4659/thread.lock.shared
    158     void swap(shared_lock& o) noexcept
    159     {
    160       swap(m, o.m);
    161       swap(own, o.own);
    162     }
    163 
    164     Mutex* release() noexcept
    165     {
    166       auto res = m;
    167       m = nullptr;
    168       own = false;
    169       return res;
    170     }
    171 
    172     Mutex& mutex() const noexcept { return m; }
    173     bool owns_lock() const noexcept { return own; }
    174     explicit operator bool() const noexcept { return own; }
    175 
    176   private:
    177     void CheckLockable()
    178     {
    179       if (!m)
    180         throw std::system_error(std::make_error_code(
    181                                   std::errc::operation_not_permitted));
    182       if (own)
    183         throw std::system_error(std::make_error_code(
    184                                   std::errc::resource_deadlock_would_occur));
    185     }
    186 
    187     Mutex* m = nullptr;
    188     bool own = false;
    189   };
    190 
    191   template <typename Mutex>
    192   void swap(shared_lock<Mutex>& a, shared_lock<Mutex>& b) noexcept
    193   { a.swap(b); }
    194 
    195 }
    196 
    197 #undef MUTEX_ASSERT