rwguard.h 6.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241
  1. #ifndef RWGUARD_H
  2. #define RWGUARD_H
  3. #include <shared_mutex>
  4. #include <mutex>
  5. #include <map>
  6. #include <condition_variable>
  7. #include "types.h"
  8. namespace binom {
  9. typedef ui64 f_virtual_index;
  10. struct RWSyncMap {
  11. class RWGuard;
  12. private:
  13. class RWGuardAutoDelete;
  14. std::map<f_virtual_index, std::weak_ptr<RWGuardAutoDelete>> mtx_map;
  15. std::mutex general_mtx;
  16. //! RWGuard autodestroyer (Removes RWGuard-element from RWSyncMap)
  17. class RWGuardAutoDelete {
  18. RWSyncMap* map;
  19. std::shared_mutex mtx;
  20. f_virtual_index v_index;
  21. friend class RWGuard;
  22. public:
  23. RWGuardAutoDelete(RWSyncMap* map, f_virtual_index v_index)
  24. : map(map), mtx(), v_index(v_index) {}
  25. RWGuardAutoDelete(const RWGuardAutoDelete&) = delete;
  26. ~RWGuardAutoDelete() {
  27. std::scoped_lock lock(map->general_mtx);
  28. auto it = map->mtx_map.find(v_index);
  29. if(it != map->mtx_map.cend())
  30. if(auto shr_ptr = it->second.lock(); !shr_ptr)
  31. map->mtx_map.erase(it);
  32. }
  33. };
  34. public:
  35. enum class LockType : ui8 {
  36. shared_lock,
  37. unique_lock,
  38. unlocked
  39. };
  40. //! Read/Write guard (shared mutex for BinOM nodes)
  41. class RWGuard {
  42. std::shared_ptr<RWGuardAutoDelete> shr_ptr;
  43. LockType lock_type = LockType::unlocked;
  44. ui64 lock_count = 0;
  45. RWGuard(f_virtual_index v_index, RWSyncMap* map) : shr_ptr(std::make_shared<RWGuardAutoDelete>(map, v_index)) {
  46. map->mtx_map.emplace(v_index, shr_ptr);
  47. }
  48. RWGuard(std::weak_ptr<RWGuardAutoDelete>& weak, f_virtual_index v_index, RWSyncMap* map) : shr_ptr(weak.lock()) {
  49. if(!shr_ptr) {
  50. new(this) RWGuard(v_index, map);
  51. weak = shr_ptr;
  52. }
  53. }
  54. void forceUnlock() {
  55. if(!shr_ptr) return;
  56. lock_count = 0;
  57. switch (lock_type) {
  58. case LockType::shared_lock:
  59. shr_ptr->mtx.unlock_shared();
  60. lock_type = LockType::unlocked;
  61. return;
  62. case LockType::unique_lock:
  63. shr_ptr->mtx.unlock();
  64. lock_type = LockType::unlocked;
  65. return;
  66. case LockType::unlocked: return;
  67. }
  68. }
  69. friend struct RWSyncMap;
  70. public:
  71. RWGuard() = default;
  72. RWGuard(RWGuard&& other)
  73. : shr_ptr(std::move(other.shr_ptr)), lock_type(other.lock_type), lock_count(other.lock_count) {
  74. other.shr_ptr.reset();
  75. }
  76. RWGuard(RWGuard&) = delete;
  77. ~RWGuard() {forceUnlock();}
  78. f_virtual_index getLockedIndex() {return shr_ptr->v_index;}
  79. void clear() {
  80. forceUnlock();
  81. shr_ptr.reset();
  82. }
  83. RWGuard& operator=(RWGuard&& other) {
  84. this->~RWGuard();
  85. return *new(this) RWGuard(std::move(other));
  86. }
  87. LockType getLockType() {return lock_type;}
  88. void lock() {
  89. if(!shr_ptr) return;
  90. if(lock_type == LockType::unique_lock) {
  91. ++lock_count;
  92. return;
  93. }
  94. forceUnlock();
  95. shr_ptr->mtx.lock();
  96. lock_type = LockType::unique_lock;
  97. }
  98. void lockShared() {
  99. if(!shr_ptr) return;
  100. if(lock_type != LockType::unlocked) {
  101. ++lock_count;
  102. return;
  103. }
  104. shr_ptr->mtx.lock_shared();
  105. lock_type = LockType::shared_lock;
  106. }
  107. void unlock() {
  108. if(!shr_ptr) return;
  109. if(lock_count) {
  110. --lock_count;
  111. return;
  112. }
  113. forceUnlock();
  114. }
  115. // bool tryLock() {
  116. // if(!shr_ptr) return;
  117. // unlock();
  118. // if(shr_ptr->mtx.try_lock()) {
  119. // lock_type = LockType::unique_lock;
  120. // return true;
  121. // }
  122. // return false;
  123. // }
  124. // bool tryLockShared() {
  125. // if(!shr_ptr) return;
  126. // unlock();
  127. // if(shr_ptr->mtx.try_lock_shared()) {
  128. // lock_type = LockType::shared_lock;
  129. // return true;
  130. // }
  131. // return false;
  132. // }
  133. };
  134. //! RAII RWGuard locker
  135. class ScopedRWGuard {
  136. RWSyncMap::RWGuard* rwg = nullptr;
  137. LockType lock_type = LockType::unlocked;
  138. public:
  139. ScopedRWGuard(RWGuard& rwg,
  140. LockType lock_type = LockType::unlocked)
  141. : rwg(&rwg), lock_type(lock_type) {
  142. switch (lock_type) {
  143. case binom::RWSyncMap::LockType::shared_lock:
  144. rwg.lockShared();
  145. return;
  146. case binom::RWSyncMap::LockType::unique_lock:
  147. rwg.lock();
  148. return;
  149. case binom::RWSyncMap::LockType::unlocked:return;
  150. }
  151. }
  152. ScopedRWGuard(ScopedRWGuard& other,
  153. LockType lock_type = LockType::unlocked)
  154. : rwg(other.rwg), lock_type(lock_type) {
  155. if(rwg)
  156. switch (lock_type) {
  157. case binom::RWSyncMap::LockType::shared_lock:
  158. rwg->lockShared();
  159. return;
  160. case binom::RWSyncMap::LockType::unique_lock:
  161. rwg->lock();
  162. return;
  163. case binom::RWSyncMap::LockType::unlocked:return;
  164. }
  165. }
  166. ScopedRWGuard(ScopedRWGuard&& other,
  167. LockType lock_type = LockType::unlocked)
  168. : rwg(other.rwg), lock_type(lock_type) {
  169. if(rwg && lock_type != other.lock_type) {
  170. rwg->unlock();
  171. switch (lock_type) {
  172. case binom::RWSyncMap::LockType::shared_lock:
  173. rwg->lockShared();
  174. return;
  175. case binom::RWSyncMap::LockType::unique_lock:
  176. rwg->lock();
  177. return;
  178. case binom::RWSyncMap::LockType::unlocked:return;
  179. }
  180. }
  181. other.rwg = nullptr;
  182. }
  183. ~ScopedRWGuard() {
  184. if(rwg && lock_type != LockType::unlocked)
  185. rwg->unlock();
  186. }
  187. inline LockType getScopeLockType() {return lock_type;}
  188. inline LockType getGuardLockType() {if(rwg)return rwg->getLockType(); return LockType::unlocked;}
  189. inline f_virtual_index getLockIndex() {if(rwg)return rwg->getLockedIndex(); return 0xFFFFFFFFFFFFFFFF_ui64;}
  190. inline RWGuard& getRWGuard() {return *rwg;}
  191. };
  192. RWSyncMap() = default;
  193. ~RWSyncMap() {}
  194. RWGuard get(f_virtual_index node_index) {
  195. std::scoped_lock lock(general_mtx);
  196. if(auto it = mtx_map.find(node_index); it != mtx_map.cend()) {
  197. RWGuard guard(it->second, node_index, this);
  198. return guard;
  199. } else {
  200. RWGuard guard(node_index, this);
  201. return guard;
  202. }
  203. }
  204. };
  205. }
  206. #endif // RWGUARD_H