mingw.shared_mutex.h 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504
  1. /// \file mingw.shared_mutex.h
  2. /// \brief Standard-compliant shared_mutex for MinGW
  3. ///
  4. /// (c) 2017 by Nathaniel J. McClatchey, Athens OH, United States
  5. /// \author Nathaniel J. McClatchey
  6. ///
  7. /// \copyright Simplified (2-clause) BSD License.
  8. ///
  9. /// \note This file may become part of the mingw-w64 runtime package. If/when
  10. /// this happens, the appropriate license will be added, i.e. this code will
  11. /// become dual-licensed, and the current BSD 2-clause license will stay.
  12. /// \note Target Windows version is determined by WINVER, which is determined in
  13. /// <windows.h> from _WIN32_WINNT, which can itself be set by the user.
  14. // Notes on the namespaces:
  15. // - The implementation can be accessed directly in the namespace
  16. // mingw_stdthread.
  17. // - Objects will be brought into namespace std by a using directive. This
  18. // will cause objects declared in std (such as MinGW's implementation) to
  19. // hide this implementation's definitions.
  20. // - To avoid poluting the namespace with implementation details, all objects
  21. // to be pushed into std will be placed in mingw_stdthread::visible.
  22. // The end result is that if MinGW supplies an object, it is automatically
  23. // used. If MinGW does not supply an object, this implementation's version will
  24. // instead be used.
  25. #ifndef MINGW_SHARED_MUTEX_H_
  26. #define MINGW_SHARED_MUTEX_H_
  27. #if !defined(__cplusplus) || (__cplusplus < 201103L)
  28. #error A C++11 compiler is required!
  29. #endif
  30. #include <cassert>
  31. // For descriptive errors.
  32. #include <system_error>
  33. // Implementing a shared_mutex without OS support will require atomic read-
  34. // modify-write capacity.
  35. #include <atomic>
  36. // For timing in shared_lock and shared_timed_mutex.
  37. #include <chrono>
  38. #include <limits>
  39. // Use MinGW's shared_lock class template, if it's available. Requires C++14.
  40. // If unavailable (eg. because this library is being used in C++11), then an
  41. // implementation of shared_lock is provided by this header.
  42. #if (__cplusplus >= 201402L)
  43. #include <shared_mutex>
  44. #endif
  45. // For defer_lock_t, adopt_lock_t, and try_to_lock_t
  46. #include "mingw.mutex.h"
  47. // For this_thread::yield.
  48. //#include "mingw.thread.h"
  49. // Might be able to use native Slim Reader-Writer (SRW) locks.
  50. #ifdef _WIN32
  51. #include <sdkddkver.h> // Detect Windows version.
  52. #if (defined(__MINGW32__) && !defined(__MINGW64_VERSION_MAJOR))
  53. #pragma message "The Windows API that MinGW-w32 provides is not fully compatible\
  54. with Microsoft's API. We'll try to work around this, but we can make no\
  55. guarantees. This problem does not exist in MinGW-w64."
  56. #include <windows.h> // No further granularity can be expected.
  57. #else
  58. #include <synchapi.h>
  59. #endif
  60. #endif
  61. namespace mingw_stdthread
  62. {
  63. // Define a portable atomics-based shared_mutex
  64. namespace portable
  65. {
  66. class shared_mutex
  67. {
  68. typedef uint_fast16_t counter_type;
  69. std::atomic<counter_type> mCounter {0};
  70. static constexpr counter_type kWriteBit = 1 << (std::numeric_limits<counter_type>::digits - 1);
  71. #if STDMUTEX_RECURSION_CHECKS
  72. // Runtime checker for verifying owner threads. Note: Exclusive mode only.
  73. _OwnerThread mOwnerThread {};
  74. #endif
  75. public:
  76. typedef shared_mutex * native_handle_type;
  77. shared_mutex () = default;
  78. // No form of copying or moving should be allowed.
  79. shared_mutex (const shared_mutex&) = delete;
  80. shared_mutex & operator= (const shared_mutex&) = delete;
  81. ~shared_mutex ()
  82. {
  83. // Terminate if someone tries to destroy an owned mutex.
  84. assert(mCounter.load(std::memory_order_relaxed) == 0);
  85. }
  86. void lock_shared (void)
  87. {
  88. counter_type expected = mCounter.load(std::memory_order_relaxed);
  89. do
  90. {
  91. // Delay if writing or if too many readers are attempting to read.
  92. if (expected >= kWriteBit - 1)
  93. {
  94. using namespace std;
  95. expected = mCounter.load(std::memory_order_relaxed);
  96. continue;
  97. }
  98. if (mCounter.compare_exchange_weak(expected,
  99. static_cast<counter_type>(expected + 1),
  100. std::memory_order_acquire,
  101. std::memory_order_relaxed))
  102. break;
  103. }
  104. while (true);
  105. }
  106. bool try_lock_shared (void)
  107. {
  108. counter_type expected = mCounter.load(std::memory_order_relaxed) & static_cast<counter_type>(~kWriteBit);
  109. if (expected + 1 == kWriteBit)
  110. return false;
  111. else
  112. return mCounter.compare_exchange_strong( expected,
  113. static_cast<counter_type>(expected + 1),
  114. std::memory_order_acquire,
  115. std::memory_order_relaxed);
  116. }
  117. void unlock_shared (void)
  118. {
  119. using namespace std;
  120. #ifndef NDEBUG
  121. if (!(mCounter.fetch_sub(1, memory_order_release) & static_cast<counter_type>(~kWriteBit)))
  122. __builtin_trap();
  123. #else
  124. mCounter.fetch_sub(1, memory_order_release);
  125. #endif
  126. }
  127. // Behavior is undefined if a lock was previously acquired.
  128. void lock (void)
  129. {
  130. #if STDMUTEX_RECURSION_CHECKS
  131. DWORD self = mOwnerThread.checkOwnerBeforeLock();
  132. #endif
  133. using namespace std;
  134. // Might be able to use relaxed memory order...
  135. // Wait for the write-lock to be unlocked, then claim the write slot.
  136. counter_type current;
  137. while ((current = mCounter.fetch_or(kWriteBit, std::memory_order_acquire)) & kWriteBit);
  138. //this_thread::yield();
  139. // Wait for readers to finish up.
  140. while (current != kWriteBit)
  141. {
  142. //this_thread::yield();
  143. current = mCounter.load(std::memory_order_acquire);
  144. }
  145. #if STDMUTEX_RECURSION_CHECKS
  146. mOwnerThread.setOwnerAfterLock(self);
  147. #endif
  148. }
  149. bool try_lock (void)
  150. {
  151. #if STDMUTEX_RECURSION_CHECKS
  152. DWORD self = mOwnerThread.checkOwnerBeforeLock();
  153. #endif
  154. counter_type expected = 0;
  155. bool ret = mCounter.compare_exchange_strong(expected, kWriteBit,
  156. std::memory_order_acquire,
  157. std::memory_order_relaxed);
  158. #if STDMUTEX_RECURSION_CHECKS
  159. if (ret)
  160. mOwnerThread.setOwnerAfterLock(self);
  161. #endif
  162. return ret;
  163. }
  164. void unlock (void)
  165. {
  166. #if STDMUTEX_RECURSION_CHECKS
  167. mOwnerThread.checkSetOwnerBeforeUnlock();
  168. #endif
  169. using namespace std;
  170. #ifndef NDEBUG
  171. if (mCounter.load(memory_order_relaxed) != kWriteBit)
  172. __builtin_trap();
  173. #endif
  174. mCounter.store(0, memory_order_release);
  175. }
  176. native_handle_type native_handle (void)
  177. {
  178. return this;
  179. }
  180. };
  181. } // Namespace portable
  182. // The native shared_mutex implementation primarily uses features of Windows
  183. // Vista, but the features used for try_lock and try_lock_shared were not
  184. // introduced until Windows 7. To allow limited use while compiling for Vista,
  185. // I define the class without try_* functions in that case.
  186. // Only fully-featured implementations will be placed into namespace std.
  187. #if defined(_WIN32) && (WINVER >= _WIN32_WINNT_VISTA)
  188. namespace vista
  189. {
  190. class condition_variable_any;
  191. }
  192. namespace windows7
  193. {
  194. // We already #include "mingw.mutex.h". May as well reduce redundancy.
  195. class shared_mutex : windows7::mutex
  196. {
  197. // Allow condition_variable_any (and only condition_variable_any) to treat a
  198. // shared_mutex as its base class.
  199. friend class vista::condition_variable_any;
  200. public:
  201. using windows7::mutex::native_handle_type;
  202. using windows7::mutex::lock;
  203. using windows7::mutex::unlock;
  204. using windows7::mutex::native_handle;
  205. void lock_shared (void)
  206. {
  207. AcquireSRWLockShared(native_handle());
  208. }
  209. void unlock_shared (void)
  210. {
  211. ReleaseSRWLockShared(native_handle());
  212. }
  213. // TryAcquireSRW functions are a Windows 7 feature.
  214. #if (WINVER >= _WIN32_WINNT_WIN7)
  215. bool try_lock_shared (void)
  216. {
  217. return TryAcquireSRWLockShared(native_handle()) != 0;
  218. }
  219. using windows7::mutex::try_lock;
  220. #endif
  221. };
  222. } // Namespace windows7
  223. #endif // Compiling for Vista
  224. #if (defined(_WIN32) && (WINVER >= _WIN32_WINNT_WIN7))
  225. using windows7::shared_mutex;
  226. #else
  227. using portable::shared_mutex;
  228. #endif
  229. class shared_timed_mutex : shared_mutex
  230. {
  231. typedef shared_mutex Base;
  232. public:
  233. using Base::lock;
  234. using Base::try_lock;
  235. using Base::unlock;
  236. using Base::lock_shared;
  237. using Base::try_lock_shared;
  238. using Base::unlock_shared;
  239. template< class Clock, class Duration >
  240. bool try_lock_until ( const std::chrono::time_point<Clock,Duration>& cutoff )
  241. {
  242. do
  243. {
  244. if (try_lock())
  245. return true;
  246. }
  247. while (std::chrono::steady_clock::now() < cutoff);
  248. return false;
  249. }
  250. template< class Rep, class Period >
  251. bool try_lock_for (const std::chrono::duration<Rep,Period>& rel_time)
  252. {
  253. return try_lock_until(std::chrono::steady_clock::now() + rel_time);
  254. }
  255. template< class Clock, class Duration >
  256. bool try_lock_shared_until ( const std::chrono::time_point<Clock,Duration>& cutoff )
  257. {
  258. do
  259. {
  260. if (try_lock_shared())
  261. return true;
  262. }
  263. while (std::chrono::steady_clock::now() < cutoff);
  264. return false;
  265. }
  266. template< class Rep, class Period >
  267. bool try_lock_shared_for (const std::chrono::duration<Rep,Period>& rel_time)
  268. {
  269. return try_lock_shared_until(std::chrono::steady_clock::now() + rel_time);
  270. }
  271. };
  272. #if __cplusplus >= 201402L
  273. using std::shared_lock;
  274. #else
  275. // If not supplied by shared_mutex (eg. because C++14 is not supported), I
  276. // supply the various helper classes that the header should have defined.
  277. template<class Mutex>
  278. class shared_lock
  279. {
  280. Mutex * mMutex;
  281. bool mOwns;
  282. // Reduce code redundancy
  283. void verify_lockable (void)
  284. {
  285. using namespace std;
  286. if (mMutex == nullptr)
  287. __builtin_trap();
  288. if (mOwns)
  289. __builtin_trap();
  290. }
  291. public:
  292. typedef Mutex mutex_type;
  293. shared_lock (void) noexcept
  294. : mMutex(nullptr), mOwns(false)
  295. {
  296. }
  297. shared_lock (shared_lock<Mutex> && other) noexcept
  298. : mMutex(other.mutex_), mOwns(other.owns_)
  299. {
  300. other.mMutex = nullptr;
  301. other.mOwns = false;
  302. }
  303. explicit shared_lock (mutex_type & m)
  304. : mMutex(&m), mOwns(true)
  305. {
  306. mMutex->lock_shared();
  307. }
  308. shared_lock (mutex_type & m, defer_lock_t) noexcept
  309. : mMutex(&m), mOwns(false)
  310. {
  311. }
  312. shared_lock (mutex_type & m, adopt_lock_t)
  313. : mMutex(&m), mOwns(true)
  314. {
  315. }
  316. shared_lock (mutex_type & m, try_to_lock_t)
  317. : mMutex(&m), mOwns(m.try_lock_shared())
  318. {
  319. }
  320. template< class Rep, class Period >
  321. shared_lock( mutex_type& m, const std::chrono::duration<Rep,Period>& timeout_duration )
  322. : mMutex(&m), mOwns(m.try_lock_shared_for(timeout_duration))
  323. {
  324. }
  325. template< class Clock, class Duration >
  326. shared_lock( mutex_type& m, const std::chrono::time_point<Clock,Duration>& timeout_time )
  327. : mMutex(&m), mOwns(m.try_lock_shared_until(timeout_time))
  328. {
  329. }
  330. shared_lock& operator= (shared_lock<Mutex> && other) noexcept
  331. {
  332. if (&other != this)
  333. {
  334. if (mOwns)
  335. mMutex->unlock_shared();
  336. mMutex = other.mMutex;
  337. mOwns = other.mOwns;
  338. other.mMutex = nullptr;
  339. other.mOwns = false;
  340. }
  341. return *this;
  342. }
  343. ~shared_lock (void)
  344. {
  345. if (mOwns)
  346. mMutex->unlock_shared();
  347. }
  348. shared_lock (const shared_lock<Mutex> &) = delete;
  349. shared_lock& operator= (const shared_lock<Mutex> &) = delete;
  350. // Shared locking
  351. void lock (void)
  352. {
  353. verify_lockable();
  354. mMutex->lock_shared();
  355. mOwns = true;
  356. }
  357. bool try_lock (void)
  358. {
  359. verify_lockable();
  360. mOwns = mMutex->try_lock_shared();
  361. return mOwns;
  362. }
  363. template< class Clock, class Duration >
  364. bool try_lock_until( const std::chrono::time_point<Clock,Duration>& cutoff )
  365. {
  366. verify_lockable();
  367. do
  368. {
  369. mOwns = mMutex->try_lock_shared();
  370. if (mOwns)
  371. return mOwns;
  372. }
  373. while (std::chrono::steady_clock::now() < cutoff);
  374. return false;
  375. }
  376. template< class Rep, class Period >
  377. bool try_lock_for (const std::chrono::duration<Rep,Period>& rel_time)
  378. {
  379. return try_lock_until(std::chrono::steady_clock::now() + rel_time);
  380. }
  381. void unlock (void)
  382. {
  383. using namespace std;
  384. if (!mOwns)
  385. __builtin_trap();
  386. mMutex->unlock_shared();
  387. mOwns = false;
  388. }
  389. // Modifiers
  390. void swap (shared_lock<Mutex> & other) noexcept
  391. {
  392. using namespace std;
  393. swap(mMutex, other.mMutex);
  394. swap(mOwns, other.mOwns);
  395. }
  396. mutex_type * release (void) noexcept
  397. {
  398. mutex_type * ptr = mMutex;
  399. mMutex = nullptr;
  400. mOwns = false;
  401. return ptr;
  402. }
  403. // Observers
  404. mutex_type * mutex (void) const noexcept
  405. {
  406. return mMutex;
  407. }
  408. bool owns_lock (void) const noexcept
  409. {
  410. return mOwns;
  411. }
  412. explicit operator bool () const noexcept
  413. {
  414. return owns_lock();
  415. }
  416. };
  417. template< class Mutex >
  418. void swap( shared_lock<Mutex>& lhs, shared_lock<Mutex>& rhs ) noexcept
  419. {
  420. lhs.swap(rhs);
  421. }
  422. #endif // C++11
  423. } // Namespace mingw_stdthread
  424. namespace std
  425. {
  426. // Because of quirks of the compiler, the common "using namespace std;"
  427. // directive would flatten the namespaces and introduce ambiguity where there
  428. // was none. Direct specification (std::), however, would be unaffected.
  429. // Take the safe option, and include only in the presence of MinGW's win32
  430. // implementation.
  431. #if (__cplusplus < 201703L) || (defined(__MINGW32__ ) && !defined(_GLIBCXX_HAS_GTHREADS) && !defined(__clang__))
  432. using mingw_stdthread::shared_mutex;
  433. #endif
  434. #if (__cplusplus < 201402L) || (defined(__MINGW32__ ) && !defined(_GLIBCXX_HAS_GTHREADS) && !defined(__clang__))
  435. using mingw_stdthread::shared_timed_mutex;
  436. using mingw_stdthread::shared_lock;
  437. #elif !defined(MINGW_STDTHREAD_REDUNDANCY_WARNING) // Skip repetition
  438. #define MINGW_STDTHREAD_REDUNDANCY_WARNING
  439. #pragma message "This version of MinGW seems to include a win32 port of\
  440. pthreads, and probably already has C++ std threading classes implemented,\
  441. based on pthreads. These classes, found in namespace std, are not overridden\
  442. by the mingw-std-thread library. If you would still like to use this\
  443. implementation (as it is more lightweight), use the classes provided in\
  444. namespace mingw_stdthread."
  445. #endif
  446. } // Namespace std
  447. #endif // MINGW_SHARED_MUTEX_H_