mirror of
				https://github.com/zenorogue/hyperrogue.git
				synced 2025-10-30 13:32:59 +00:00 
			
		
		
		
	fixed the thread locking
This commit is contained in:
		| @@ -320,12 +320,13 @@ struct fpattern { | |||||||
| struct discovery { | struct discovery { | ||||||
|   fpattern experiment; |   fpattern experiment; | ||||||
|   std::shared_ptr<std::thread> discoverer; |   std::shared_ptr<std::thread> discoverer; | ||||||
|   std::recursive_mutex lock, slock; |   std::mutex lock; | ||||||
|  |   std::condition_variable cv; | ||||||
|   bool is_suspended; |   bool is_suspended; | ||||||
|   bool stop_it; |   bool stop_it; | ||||||
|    |    | ||||||
|   map<unsigned, tuple<int, int, matrix, matrix, matrix, int> > hashes_found; |   map<unsigned, tuple<int, int, matrix, matrix, matrix, int> > hashes_found; | ||||||
|   discovery() : experiment(0) { is_suspended = false; stop_it = false; experiment.dis = this; } |   discovery() : experiment(0) { is_suspended = false; stop_it = false; experiment.dis = this; experiment.Prime = experiment.Field = experiment.wsquare = 0; } | ||||||
|    |    | ||||||
|   void activate(); |   void activate(); | ||||||
|   void suspend(); |   void suspend(); | ||||||
| @@ -1258,27 +1259,28 @@ void discovery::activate() { | |||||||
|         if(stop_it) break; |         if(stop_it) break; | ||||||
|         } |         } | ||||||
|       }); |       }); | ||||||
|     slock.lock(); |  | ||||||
|     } |     } | ||||||
|   if(is_suspended) { |   if(is_suspended) { | ||||||
|     is_suspended = false; |     if(1) { | ||||||
|     lock.unlock(); |       std::unique_lock<std::mutex> lk(lock); | ||||||
|     slock.unlock(); |       is_suspended = false; | ||||||
|  |       } | ||||||
|  |     cv.notify_one(); | ||||||
|     } |     } | ||||||
|   } |   } | ||||||
|  |  | ||||||
| void discovery::discovered() { | void discovery::discovered() { | ||||||
|   slock.unlock(); |   std::unique_lock<std::mutex> lk(lock); | ||||||
|   lock.lock(); |  | ||||||
|   auto& e = experiment; |   auto& e = experiment; | ||||||
|   hashes_found[e.compute_hash()] = make_tuple(e.Prime, e.wsquare, e.R, e.P, e.X, isize(e.matrices) / e.local_group); |   hashes_found[e.compute_hash()] = make_tuple(e.Prime, e.wsquare, e.R, e.P, e.X, isize(e.matrices) / e.local_group); | ||||||
|   lock.unlock(); |  | ||||||
|   slock.lock(); |  | ||||||
|   } |   } | ||||||
|  |  | ||||||
| void discovery::suspend() { is_suspended = true; lock.lock(); slock.lock(); } | void discovery::suspend() { is_suspended = true; } | ||||||
|  |  | ||||||
| void discovery::check_suspend() { slock.unlock(); lock.lock(); lock.unlock(); slock.lock();  } | void discovery::check_suspend() {  | ||||||
|  |   std::unique_lock<std::mutex> lk(lock); | ||||||
|  |   if(is_suspended) cv.wait(lk, [this] { return !is_suspended; }); | ||||||
|  |   } | ||||||
|  |  | ||||||
| void discovery::schedule_destruction() { stop_it = true; } | void discovery::schedule_destruction() { stop_it = true; } | ||||||
| discovery::~discovery() { schedule_destruction(); if(discoverer) discoverer->join(); } | discovery::~discovery() { schedule_destruction(); if(discoverer) discoverer->join(); } | ||||||
|   | |||||||
							
								
								
									
										33
									
								
								geom-exp.cpp
									
									
									
									
									
								
							
							
						
						
									
										33
									
								
								geom-exp.cpp
									
									
									
									
									
								
							| @@ -437,23 +437,24 @@ EX void showQuotientConfig3() { | |||||||
|      |      | ||||||
|   dialog::addBreak(100); |   dialog::addBreak(100); | ||||||
|  |  | ||||||
|   if(!ds.is_suspended) ds.lock.lock(); |   if(1) { | ||||||
|   auto&l = ds.hashes_found; |     std::unique_lock<std::mutex> lk(ds.lock); | ||||||
|   for(auto& v: l) { |     auto&l = ds.hashes_found; | ||||||
|     char x = 'a'; |     for(auto& v: l) { | ||||||
|     string s = XLAT("#%1, cells: %2", itsh(v.first), its(get<5>(v.second))); |       char x = 'a'; | ||||||
|     dialog::addItem(s, x++); |       string s = XLAT("#%1, cells: %2", itsh(v.first), its(get<5>(v.second))); | ||||||
|     dialog::add_action([&v] { |       dialog::addItem(s, x++); | ||||||
|       stop_game(); |       dialog::add_action([&v] { | ||||||
|       int tmp; |         stop_game(); | ||||||
|       tie(currfp.Prime, currfp.wsquare, currfp.R, currfp.P, currfp.X, tmp) = v.second; |         int tmp; | ||||||
|       currfp.Field = currfp.wsquare ? currfp.Prime * currfp.Prime : currfp.Prime; |         tie(currfp.Prime, currfp.wsquare, currfp.R, currfp.P, currfp.X, tmp) = v.second; | ||||||
|       currfp.generate_all3(); |         currfp.Field = currfp.wsquare ? currfp.Prime * currfp.Prime : currfp.Prime; | ||||||
|       currfp.analyze(); |         currfp.generate_all3(); | ||||||
|       start_game(); |         currfp.analyze(); | ||||||
|       }); |         start_game(); | ||||||
|  |         }); | ||||||
|  |       } | ||||||
|     } |     } | ||||||
|   if(!ds.is_suspended) ds.lock.unlock(); |  | ||||||
|      |      | ||||||
|   dialog::addBreak(100); |   dialog::addBreak(100); | ||||||
|    |    | ||||||
|   | |||||||
							
								
								
									
										564
									
								
								mingw.condition_variable.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										564
									
								
								mingw.condition_variable.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,564 @@ | |||||||
|  | /** | ||||||
|  | * @file condition_variable.h | ||||||
|  | * @brief std::condition_variable implementation for MinGW | ||||||
|  | * | ||||||
|  | * (c) 2013-2016 by Mega Limited, Auckland, New Zealand | ||||||
|  | * @author Alexander Vassilev | ||||||
|  | * | ||||||
|  | * @copyright Simplified (2-clause) BSD License. | ||||||
|  | * You should have received a copy of the license along with this | ||||||
|  | * program. | ||||||
|  | * | ||||||
|  | * This code is distributed in the hope that it will be useful, | ||||||
|  | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||||
|  | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. | ||||||
|  | * @note | ||||||
|  | * This file may become part of the mingw-w64 runtime package. If/when this happens, | ||||||
|  | * the appropriate license will be added, i.e. this code will become dual-licensed, | ||||||
|  | * and the current BSD 2-clause license will stay. | ||||||
|  | */ | ||||||
|  |  | ||||||
|  | #ifndef MINGW_CONDITIONAL_VARIABLE_H | ||||||
|  | #define MINGW_CONDITIONAL_VARIABLE_H | ||||||
|  |  | ||||||
|  | #if !defined(__cplusplus) || (__cplusplus < 201103L) | ||||||
|  | #error A C++11 compiler is required! | ||||||
|  | #endif | ||||||
|  | //  Use the standard classes for std::, if available. | ||||||
|  | #include <condition_variable> | ||||||
|  |  | ||||||
|  | #include <cassert> | ||||||
|  | #include <chrono> | ||||||
|  | #include <system_error> | ||||||
|  |  | ||||||
|  | #include <sdkddkver.h>  //  Detect Windows version. | ||||||
|  | #if (WINVER < _WIN32_WINNT_VISTA) | ||||||
|  | #include <atomic> | ||||||
|  | #endif | ||||||
|  | #if (defined(__MINGW32__) && !defined(__MINGW64_VERSION_MAJOR)) | ||||||
|  | #pragma message "The Windows API that MinGW-w32 provides is not fully compatible\ | ||||||
|  |  with Microsoft's API. We'll try to work around this, but we can make no\ | ||||||
|  |  guarantees. This problem does not exist in MinGW-w64." | ||||||
|  | #include <windows.h>    //  No further granularity can be expected. | ||||||
|  | #else | ||||||
|  | #if (WINVER < _WIN32_WINNT_VISTA) | ||||||
|  | #include <windef.h> | ||||||
|  | #include <winbase.h>  //  For CreateSemaphore | ||||||
|  | #include <handleapi.h> | ||||||
|  | #endif | ||||||
|  | #include <synchapi.h> | ||||||
|  | #endif | ||||||
|  |  | ||||||
|  | #include "mingw.mutex.h" | ||||||
|  | #include "mingw.shared_mutex.h" | ||||||
|  |  | ||||||
|  | #if !defined(_WIN32_WINNT) || (_WIN32_WINNT < 0x0501) | ||||||
|  | #error To use the MinGW-std-threads library, you will need to define the macro _WIN32_WINNT to be 0x0501 (Windows XP) or higher. | ||||||
|  | #endif | ||||||
|  |  | ||||||
|  | namespace mingw_stdthread | ||||||
|  | { | ||||||
|  | #if defined(__MINGW32__ ) && !defined(_GLIBCXX_HAS_GTHREADS) | ||||||
|  | enum class cv_status { no_timeout, timeout }; | ||||||
|  | #else | ||||||
|  | using std::cv_status; | ||||||
|  | #endif | ||||||
|  | namespace xp | ||||||
|  | { | ||||||
|  | //    Include the XP-compatible condition_variable classes only if actually | ||||||
|  | //  compiling for XP. The XP-compatible classes are slower than the newer | ||||||
|  | //  versions, and depend on features not compatible with Windows Phone 8. | ||||||
|  | #if (WINVER < _WIN32_WINNT_VISTA) | ||||||
|  | class condition_variable_any | ||||||
|  | { | ||||||
|  |     recursive_mutex mMutex {}; | ||||||
|  |     std::atomic<int> mNumWaiters {0}; | ||||||
|  |     HANDLE mSemaphore; | ||||||
|  |     HANDLE mWakeEvent {}; | ||||||
|  | public: | ||||||
|  |     using native_handle_type = HANDLE; | ||||||
|  |     native_handle_type native_handle() | ||||||
|  |     { | ||||||
|  |         return mSemaphore; | ||||||
|  |     } | ||||||
|  |     condition_variable_any(const condition_variable_any&) = delete; | ||||||
|  |     condition_variable_any& operator=(const condition_variable_any&) = delete; | ||||||
|  |     condition_variable_any() | ||||||
|  |         :   mSemaphore(CreateSemaphoreA(NULL, 0, 0xFFFF, NULL)) | ||||||
|  |     { | ||||||
|  |         if (mSemaphore == NULL) | ||||||
|  |             throw std::system_error(GetLastError(), std::generic_category()); | ||||||
|  |         mWakeEvent = CreateEvent(NULL, FALSE, FALSE, NULL); | ||||||
|  |         if (mWakeEvent == NULL) | ||||||
|  |         { | ||||||
|  |             CloseHandle(mSemaphore); | ||||||
|  |             throw std::system_error(GetLastError(), std::generic_category()); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |     ~condition_variable_any() | ||||||
|  |     { | ||||||
|  |         CloseHandle(mWakeEvent); | ||||||
|  |         CloseHandle(mSemaphore); | ||||||
|  |     } | ||||||
|  | private: | ||||||
|  |     template <class M> | ||||||
|  |     bool wait_impl(M& lock, DWORD timeout) | ||||||
|  |     { | ||||||
|  |         { | ||||||
|  |             lock_guard<recursive_mutex> guard(mMutex); | ||||||
|  |             mNumWaiters++; | ||||||
|  |         } | ||||||
|  |         lock.unlock(); | ||||||
|  |         DWORD ret = WaitForSingleObject(mSemaphore, timeout); | ||||||
|  |  | ||||||
|  |         mNumWaiters--; | ||||||
|  |         SetEvent(mWakeEvent); | ||||||
|  |         lock.lock(); | ||||||
|  |         if (ret == WAIT_OBJECT_0) | ||||||
|  |             return true; | ||||||
|  |         else if (ret == WAIT_TIMEOUT) | ||||||
|  |             return false; | ||||||
|  | //2 possible cases: | ||||||
|  | //1)The point in notify_all() where we determine the count to | ||||||
|  | //increment the semaphore with has not been reached yet: | ||||||
|  | //we just need to decrement mNumWaiters, but setting the event does not hurt | ||||||
|  | // | ||||||
|  | //2)Semaphore has just been released with mNumWaiters just before | ||||||
|  | //we decremented it. This means that the semaphore count | ||||||
|  | //after all waiters finish won't be 0 - because not all waiters | ||||||
|  | //woke up by acquiring the semaphore - we woke up by a timeout. | ||||||
|  | //The notify_all() must handle this gracefully | ||||||
|  | // | ||||||
|  |         else | ||||||
|  |         { | ||||||
|  |             using namespace std; | ||||||
|  |             throw system_error(make_error_code(errc(0)/*errc::owner_dead*/)); // no protocol_error in my mingw | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | public: | ||||||
|  |     template <class M> | ||||||
|  |     void wait(M& lock) | ||||||
|  |     { | ||||||
|  |         wait_impl(lock, INFINITE); | ||||||
|  |     } | ||||||
|  |     template <class M, class Predicate> | ||||||
|  |     void wait(M& lock, Predicate pred) | ||||||
|  |     { | ||||||
|  |         while(!pred()) | ||||||
|  |         { | ||||||
|  |             wait(lock); | ||||||
|  |         }; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     void notify_all() noexcept | ||||||
|  |     { | ||||||
|  |         lock_guard<recursive_mutex> lock(mMutex); //block any further wait requests until all current waiters are unblocked | ||||||
|  |         if (mNumWaiters.load() <= 0) | ||||||
|  |             return; | ||||||
|  |  | ||||||
|  |         ReleaseSemaphore(mSemaphore, mNumWaiters, NULL); | ||||||
|  |         while(mNumWaiters > 0) | ||||||
|  |         { | ||||||
|  |             auto ret = WaitForSingleObject(mWakeEvent, 1000); | ||||||
|  |             if (ret == WAIT_FAILED || ret == WAIT_ABANDONED) | ||||||
|  |                 std::terminate(); | ||||||
|  |         } | ||||||
|  |         assert(mNumWaiters == 0); | ||||||
|  | //in case some of the waiters timed out just after we released the | ||||||
|  | //semaphore by mNumWaiters, it won't be zero now, because not all waiters | ||||||
|  | //woke up by acquiring the semaphore. So we must zero the semaphore before | ||||||
|  | //we accept waiters for the next event | ||||||
|  | //See _wait_impl for details | ||||||
|  |         while(WaitForSingleObject(mSemaphore, 0) == WAIT_OBJECT_0); | ||||||
|  |     } | ||||||
|  |     void notify_one() noexcept | ||||||
|  |     { | ||||||
|  |         lock_guard<recursive_mutex> lock(mMutex); | ||||||
|  |         int targetWaiters = mNumWaiters.load() - 1; | ||||||
|  |         if (targetWaiters <= -1) | ||||||
|  |             return; | ||||||
|  |         ReleaseSemaphore(mSemaphore, 1, NULL); | ||||||
|  |         while(mNumWaiters > targetWaiters) | ||||||
|  |         { | ||||||
|  |             auto ret = WaitForSingleObject(mWakeEvent, 1000); | ||||||
|  |             if (ret == WAIT_FAILED || ret == WAIT_ABANDONED) | ||||||
|  |                 std::terminate(); | ||||||
|  |         } | ||||||
|  |         assert(mNumWaiters == targetWaiters); | ||||||
|  |     } | ||||||
|  |     template <class M, class Rep, class Period> | ||||||
|  |     cv_status wait_for(M& lock, | ||||||
|  |                        const std::chrono::duration<Rep, Period>& rel_time) | ||||||
|  |     { | ||||||
|  |         using namespace std::chrono; | ||||||
|  |         auto timeout = duration_cast<milliseconds>(rel_time).count(); | ||||||
|  |         DWORD waittime = (timeout < INFINITE) ? ((timeout < 0) ? 0 : static_cast<DWORD>(timeout)) : (INFINITE - 1); | ||||||
|  |         bool ret = wait_impl(lock, waittime) || (timeout >= INFINITE); | ||||||
|  |         return ret?cv_status::no_timeout:cv_status::timeout; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     template <class M, class Rep, class Period, class Predicate> | ||||||
|  |     bool wait_for(M& lock, | ||||||
|  |                   const std::chrono::duration<Rep, Period>& rel_time, Predicate pred) | ||||||
|  |     { | ||||||
|  |         return wait_until(lock, std::chrono::steady_clock::now()+rel_time, pred); | ||||||
|  |     } | ||||||
|  |     template <class M, class Clock, class Duration> | ||||||
|  |     cv_status wait_until (M& lock, | ||||||
|  |                           const std::chrono::time_point<Clock,Duration>& abs_time) | ||||||
|  |     { | ||||||
|  |         return wait_for(lock, abs_time - Clock::now()); | ||||||
|  |     } | ||||||
|  |     template <class M, class Clock, class Duration, class Predicate> | ||||||
|  |     bool wait_until (M& lock, | ||||||
|  |                      const std::chrono::time_point<Clock, Duration>& abs_time, | ||||||
|  |                      Predicate pred) | ||||||
|  |     { | ||||||
|  |         while (!pred()) | ||||||
|  |         { | ||||||
|  |             if (wait_until(lock, abs_time) == cv_status::timeout) | ||||||
|  |             { | ||||||
|  |                 return pred(); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |         return true; | ||||||
|  |     } | ||||||
|  | }; | ||||||
|  | class condition_variable: condition_variable_any | ||||||
|  | { | ||||||
|  |     using base = condition_variable_any; | ||||||
|  | public: | ||||||
|  |     using base::native_handle_type; | ||||||
|  |     using base::native_handle; | ||||||
|  |     using base::base; | ||||||
|  |     using base::notify_all; | ||||||
|  |     using base::notify_one; | ||||||
|  |     void wait(unique_lock<mutex> &lock) | ||||||
|  |     { | ||||||
|  |         base::wait(lock); | ||||||
|  |     } | ||||||
|  |     template <class Predicate> | ||||||
|  |     void wait(unique_lock<mutex>& lock, Predicate pred) | ||||||
|  |     { | ||||||
|  |         base::wait(lock, pred); | ||||||
|  |     } | ||||||
|  |     template <class Rep, class Period> | ||||||
|  |     cv_status wait_for(unique_lock<mutex>& lock, const std::chrono::duration<Rep, Period>& rel_time) | ||||||
|  |     { | ||||||
|  |         return base::wait_for(lock, rel_time); | ||||||
|  |     } | ||||||
|  |     template <class Rep, class Period, class Predicate> | ||||||
|  |     bool wait_for(unique_lock<mutex>& lock, const std::chrono::duration<Rep, Period>& rel_time, Predicate pred) | ||||||
|  |     { | ||||||
|  |         return base::wait_for(lock, rel_time, pred); | ||||||
|  |     } | ||||||
|  |     template <class Clock, class Duration> | ||||||
|  |     cv_status wait_until (unique_lock<mutex>& lock, const std::chrono::time_point<Clock,Duration>& abs_time) | ||||||
|  |     { | ||||||
|  |         return base::wait_until(lock, abs_time); | ||||||
|  |     } | ||||||
|  |     template <class Clock, class Duration, class Predicate> | ||||||
|  |     bool wait_until (unique_lock<mutex>& lock, const std::chrono::time_point<Clock, Duration>& abs_time, Predicate pred) | ||||||
|  |     { | ||||||
|  |         return base::wait_until(lock, abs_time, pred); | ||||||
|  |     } | ||||||
|  | }; | ||||||
|  | #endif  //  Compiling for XP | ||||||
|  | } //  Namespace mingw_stdthread::xp | ||||||
|  |  | ||||||
|  | #if (WINVER >= _WIN32_WINNT_VISTA) | ||||||
|  | namespace vista | ||||||
|  | { | ||||||
|  | //  If compiling for Vista or higher, use the native condition variable. | ||||||
|  | class condition_variable | ||||||
|  | { | ||||||
|  |     static constexpr DWORD kInfinite = 0xffffffffl; | ||||||
|  | #pragma GCC diagnostic push | ||||||
|  | #pragma GCC diagnostic ignored "-Wzero-as-null-pointer-constant" | ||||||
|  |     CONDITION_VARIABLE cvariable_ = CONDITION_VARIABLE_INIT; | ||||||
|  | #pragma GCC diagnostic pop | ||||||
|  |  | ||||||
|  |     friend class condition_variable_any; | ||||||
|  |  | ||||||
|  | #if STDMUTEX_RECURSION_CHECKS | ||||||
|  |     template<typename MTX> | ||||||
|  |     inline static void before_wait (MTX * pmutex) | ||||||
|  |     { | ||||||
|  |         pmutex->mOwnerThread.checkSetOwnerBeforeUnlock(); | ||||||
|  |     } | ||||||
|  |     template<typename MTX> | ||||||
|  |     inline static void after_wait (MTX * pmutex) | ||||||
|  |     { | ||||||
|  |         pmutex->mOwnerThread.setOwnerAfterLock(GetCurrentThreadId()); | ||||||
|  |     } | ||||||
|  | #else | ||||||
|  |     inline static void before_wait (void *) { } | ||||||
|  |     inline static void after_wait (void *) { } | ||||||
|  | #endif | ||||||
|  |  | ||||||
|  |     bool wait_impl (unique_lock<xp::mutex> & lock, DWORD time) | ||||||
|  |     { | ||||||
|  |         using mutex_handle_type = typename xp::mutex::native_handle_type; | ||||||
|  |         static_assert(std::is_same<mutex_handle_type, PCRITICAL_SECTION>::value, | ||||||
|  |                       "Native Win32 condition variable requires std::mutex to \ | ||||||
|  | use native Win32 critical section objects."); | ||||||
|  |         xp::mutex * pmutex = lock.release(); | ||||||
|  |         before_wait(pmutex); | ||||||
|  |         BOOL success = SleepConditionVariableCS(&cvariable_, | ||||||
|  |                                                 pmutex->native_handle(), | ||||||
|  |                                                 time); | ||||||
|  |         after_wait(pmutex); | ||||||
|  |         lock = unique_lock<xp::mutex>(*pmutex, adopt_lock); | ||||||
|  |         return success; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     bool wait_unique (windows7::mutex * pmutex, DWORD time) | ||||||
|  |     { | ||||||
|  |         before_wait(pmutex); | ||||||
|  |         BOOL success = SleepConditionVariableSRW( native_handle(), | ||||||
|  |                                                   pmutex->native_handle(), | ||||||
|  |                                                   time, | ||||||
|  | //    CONDITION_VARIABLE_LOCKMODE_SHARED has a value not specified by | ||||||
|  | //  Microsoft's Dev Center, but is known to be (convertible to) a ULONG. To | ||||||
|  | //  ensure that the value passed to this function is not equal to Microsoft's | ||||||
|  | //  constant, we can either use a static_assert, or simply generate an | ||||||
|  | //  appropriate value. | ||||||
|  |                                            !CONDITION_VARIABLE_LOCKMODE_SHARED); | ||||||
|  |         after_wait(pmutex); | ||||||
|  |         return success; | ||||||
|  |     } | ||||||
|  |     bool wait_impl (unique_lock<windows7::mutex> & lock, DWORD time) | ||||||
|  |     { | ||||||
|  |         windows7::mutex * pmutex = lock.release(); | ||||||
|  |         bool success = wait_unique(pmutex, time); | ||||||
|  |         lock = unique_lock<windows7::mutex>(*pmutex, adopt_lock); | ||||||
|  |         return success; | ||||||
|  |     } | ||||||
|  | public: | ||||||
|  |     using native_handle_type = PCONDITION_VARIABLE; | ||||||
|  |     native_handle_type native_handle (void) | ||||||
|  |     { | ||||||
|  |         return &cvariable_; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     condition_variable (void) = default; | ||||||
|  |     ~condition_variable (void) = default; | ||||||
|  |  | ||||||
|  |     condition_variable (const condition_variable &) = delete; | ||||||
|  |     condition_variable & operator= (const condition_variable &) = delete; | ||||||
|  |  | ||||||
|  |     void notify_one (void) noexcept | ||||||
|  |     { | ||||||
|  |         WakeConditionVariable(&cvariable_); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     void notify_all (void) noexcept | ||||||
|  |     { | ||||||
|  |         WakeAllConditionVariable(&cvariable_); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     void wait (unique_lock<mutex> & lock) | ||||||
|  |     { | ||||||
|  |         wait_impl(lock, kInfinite); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     template<class Predicate> | ||||||
|  |     void wait (unique_lock<mutex> & lock, Predicate pred) | ||||||
|  |     { | ||||||
|  |         while (!pred()) | ||||||
|  |             wait(lock); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     template <class Rep, class Period> | ||||||
|  |     cv_status wait_for(unique_lock<mutex>& lock, | ||||||
|  |                        const std::chrono::duration<Rep, Period>& rel_time) | ||||||
|  |     { | ||||||
|  |         using namespace std::chrono; | ||||||
|  |         auto timeout = duration_cast<milliseconds>(rel_time).count(); | ||||||
|  |         DWORD waittime = (timeout < kInfinite) ? ((timeout < 0) ? 0 : static_cast<DWORD>(timeout)) : (kInfinite - 1); | ||||||
|  |         bool result = wait_impl(lock, waittime) || (timeout >= kInfinite); | ||||||
|  |         return result ? cv_status::no_timeout : cv_status::timeout; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     template <class Rep, class Period, class Predicate> | ||||||
|  |     bool wait_for(unique_lock<mutex>& lock, | ||||||
|  |                   const std::chrono::duration<Rep, Period>& rel_time, | ||||||
|  |                   Predicate pred) | ||||||
|  |     { | ||||||
|  |         return wait_until(lock, | ||||||
|  |                           std::chrono::steady_clock::now() + rel_time, | ||||||
|  |                           std::move(pred)); | ||||||
|  |     } | ||||||
|  |     template <class Clock, class Duration> | ||||||
|  |     cv_status wait_until (unique_lock<mutex>& lock, | ||||||
|  |                           const std::chrono::time_point<Clock,Duration>& abs_time) | ||||||
|  |     { | ||||||
|  |         return wait_for(lock, abs_time - Clock::now()); | ||||||
|  |     } | ||||||
|  |     template <class Clock, class Duration, class Predicate> | ||||||
|  |     bool wait_until  (unique_lock<mutex>& lock, | ||||||
|  |                       const std::chrono::time_point<Clock, Duration>& abs_time, | ||||||
|  |                       Predicate pred) | ||||||
|  |     { | ||||||
|  |         while (!pred()) | ||||||
|  |         { | ||||||
|  |             if (wait_until(lock, abs_time) == cv_status::timeout) | ||||||
|  |             { | ||||||
|  |                 return pred(); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |         return true; | ||||||
|  |     } | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | class condition_variable_any | ||||||
|  | { | ||||||
|  |     static constexpr DWORD kInfinite = 0xffffffffl; | ||||||
|  |     using native_shared_mutex = windows7::shared_mutex; | ||||||
|  |  | ||||||
|  |     condition_variable internal_cv_ {}; | ||||||
|  | //    When available, the SRW-based mutexes should be faster than the | ||||||
|  | //  CriticalSection-based mutexes. Only try_lock will be unavailable in Vista, | ||||||
|  | //  and try_lock is not used by condition_variable_any. | ||||||
|  |     windows7::mutex internal_mutex_ {}; | ||||||
|  |  | ||||||
|  |     template<class L> | ||||||
|  |     bool wait_impl (L & lock, DWORD time) | ||||||
|  |     { | ||||||
|  |         unique_lock<decltype(internal_mutex_)> internal_lock(internal_mutex_); | ||||||
|  |         lock.unlock(); | ||||||
|  |         bool success = internal_cv_.wait_impl(internal_lock, time); | ||||||
|  |         lock.lock(); | ||||||
|  |         return success; | ||||||
|  |     } | ||||||
|  | //    If the lock happens to be called on a native Windows mutex, skip any extra | ||||||
|  | //  contention. | ||||||
|  |     inline bool wait_impl (unique_lock<mutex> & lock, DWORD time) | ||||||
|  |     { | ||||||
|  |         return internal_cv_.wait_impl(lock, time); | ||||||
|  |     } | ||||||
|  | //    Some shared_mutex functionality is available even in Vista, but it's not | ||||||
|  | //  until Windows 7 that a full implementation is natively possible. The class | ||||||
|  | //  itself is defined, with missing features, at the Vista feature level. | ||||||
|  |     bool wait_impl (unique_lock<native_shared_mutex> & lock, DWORD time) | ||||||
|  |     { | ||||||
|  |         native_shared_mutex * pmutex = lock.release(); | ||||||
|  |         bool success = internal_cv_.wait_unique(pmutex, time); | ||||||
|  |         lock = unique_lock<native_shared_mutex>(*pmutex, adopt_lock); | ||||||
|  |         return success; | ||||||
|  |     } | ||||||
|  |     bool wait_impl (shared_lock<native_shared_mutex> & lock, DWORD time) | ||||||
|  |     { | ||||||
|  |         native_shared_mutex * pmutex = lock.release(); | ||||||
|  |         BOOL success = SleepConditionVariableSRW(native_handle(), | ||||||
|  |                        pmutex->native_handle(), time, | ||||||
|  |                        CONDITION_VARIABLE_LOCKMODE_SHARED); | ||||||
|  |         lock = shared_lock<native_shared_mutex>(*pmutex, adopt_lock); | ||||||
|  |         return success; | ||||||
|  |     } | ||||||
|  | public: | ||||||
|  |     using native_handle_type = typename condition_variable::native_handle_type; | ||||||
|  |  | ||||||
|  |     native_handle_type native_handle (void) | ||||||
|  |     { | ||||||
|  |         return internal_cv_.native_handle(); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     void notify_one (void) noexcept | ||||||
|  |     { | ||||||
|  |         internal_cv_.notify_one(); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     void notify_all (void) noexcept | ||||||
|  |     { | ||||||
|  |         internal_cv_.notify_all(); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     condition_variable_any (void) = default; | ||||||
|  |     ~condition_variable_any (void) = default; | ||||||
|  |  | ||||||
|  |     template<class L> | ||||||
|  |     void wait (L & lock) | ||||||
|  |     { | ||||||
|  |         wait_impl(lock, kInfinite); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     template<class L, class Predicate> | ||||||
|  |     void wait (L & lock, Predicate pred) | ||||||
|  |     { | ||||||
|  |         while (!pred()) | ||||||
|  |             wait(lock); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     template <class L, class Rep, class Period> | ||||||
|  |     cv_status wait_for(L& lock, const std::chrono::duration<Rep,Period>& period) | ||||||
|  |     { | ||||||
|  |         using namespace std::chrono; | ||||||
|  |         auto timeout = duration_cast<milliseconds>(period).count(); | ||||||
|  |         DWORD waittime = (timeout < kInfinite) ? ((timeout < 0) ? 0 : static_cast<DWORD>(timeout)) : (kInfinite - 1); | ||||||
|  |         bool result = wait_impl(lock, waittime) || (timeout >= kInfinite); | ||||||
|  |         return result ? cv_status::no_timeout : cv_status::timeout; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     template <class L, class Rep, class Period, class Predicate> | ||||||
|  |     bool wait_for(L& lock, const std::chrono::duration<Rep, Period>& period, | ||||||
|  |                   Predicate pred) | ||||||
|  |     { | ||||||
|  |         return wait_until(lock, std::chrono::steady_clock::now() + period, | ||||||
|  |                           std::move(pred)); | ||||||
|  |     } | ||||||
|  |     template <class L, class Clock, class Duration> | ||||||
|  |     cv_status wait_until (L& lock, | ||||||
|  |                           const std::chrono::time_point<Clock,Duration>& abs_time) | ||||||
|  |     { | ||||||
|  |         return wait_for(lock, abs_time - Clock::now()); | ||||||
|  |     } | ||||||
|  |     template <class L, class Clock, class Duration, class Predicate> | ||||||
|  |     bool wait_until  (L& lock, | ||||||
|  |                       const std::chrono::time_point<Clock, Duration>& abs_time, | ||||||
|  |                       Predicate pred) | ||||||
|  |     { | ||||||
|  |         while (!pred()) | ||||||
|  |         { | ||||||
|  |             if (wait_until(lock, abs_time) == cv_status::timeout) | ||||||
|  |             { | ||||||
|  |                 return pred(); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |         return true; | ||||||
|  |     } | ||||||
|  | }; | ||||||
|  | } //  Namespace vista | ||||||
|  | #endif | ||||||
|  | #if WINVER < 0x0600 | ||||||
|  | using xp::condition_variable; | ||||||
|  | using xp::condition_variable_any; | ||||||
|  | #else | ||||||
|  | using vista::condition_variable; | ||||||
|  | using vista::condition_variable_any; | ||||||
|  | #endif | ||||||
|  | } //  Namespace mingw_stdthread | ||||||
|  |  | ||||||
|  | //  Push objects into std, but only if they are not already there. | ||||||
|  | namespace std | ||||||
|  | { | ||||||
|  | //    Because of quirks of the compiler, the common "using namespace std;" | ||||||
|  | //  directive would flatten the namespaces and introduce ambiguity where there | ||||||
|  | //  was none. Direct specification (std::), however, would be unaffected. | ||||||
|  | //    Take the safe option, and include only in the presence of MinGW's win32 | ||||||
|  | //  implementation. | ||||||
|  | #if defined(__MINGW32__ ) && !defined(_GLIBCXX_HAS_GTHREADS) | ||||||
|  | using mingw_stdthread::cv_status; | ||||||
|  | using mingw_stdthread::condition_variable; | ||||||
|  | using mingw_stdthread::condition_variable_any; | ||||||
|  | #elif !defined(MINGW_STDTHREAD_REDUNDANCY_WARNING)  //  Skip repetition | ||||||
|  | #define MINGW_STDTHREAD_REDUNDANCY_WARNING | ||||||
|  | #pragma message "This version of MinGW seems to include a win32 port of\ | ||||||
|  |  pthreads, and probably already has C++11 std threading classes implemented,\ | ||||||
|  |  based on pthreads. These classes, found in namespace std, are not overridden\ | ||||||
|  |  by the mingw-std-thread library. If you would still like to use this\ | ||||||
|  |  implementation (as it is more lightweight), use the classes provided in\ | ||||||
|  |  namespace mingw_stdthread." | ||||||
|  | #endif | ||||||
|  | } | ||||||
|  | #endif // MINGW_CONDITIONAL_VARIABLE_H | ||||||
							
								
								
									
										503
									
								
								mingw.shared_mutex.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										503
									
								
								mingw.shared_mutex.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,503 @@ | |||||||
|  | /// \file mingw.shared_mutex.h | ||||||
|  | /// \brief Standard-compliant shared_mutex for MinGW | ||||||
|  | /// | ||||||
|  | /// (c) 2017 by Nathaniel J. McClatchey, Athens OH, United States | ||||||
|  | /// \author Nathaniel J. McClatchey | ||||||
|  | /// | ||||||
|  | /// \copyright Simplified (2-clause) BSD License. | ||||||
|  | /// | ||||||
|  | /// \note This file may become part of the mingw-w64 runtime package. If/when | ||||||
|  | /// this happens, the appropriate license will be added, i.e. this code will | ||||||
|  | /// become dual-licensed, and the current BSD 2-clause license will stay. | ||||||
|  | /// \note Target Windows version is determined by WINVER, which is determined in | ||||||
|  | /// <windows.h> from _WIN32_WINNT, which can itself be set by the user. | ||||||
|  |  | ||||||
|  | //  Notes on the namespaces: | ||||||
|  | //  - The implementation can be accessed directly in the namespace | ||||||
|  | //    mingw_stdthread. | ||||||
|  | //  - Objects will be brought into namespace std by a using directive. This | ||||||
|  | //    will cause objects declared in std (such as MinGW's implementation) to | ||||||
|  | //    hide this implementation's definitions. | ||||||
|  | //  - To avoid poluting the namespace with implementation details, all objects | ||||||
|  | //    to be pushed into std will be placed in mingw_stdthread::visible. | ||||||
|  | //  The end result is that if MinGW supplies an object, it is automatically | ||||||
|  | //  used. If MinGW does not supply an object, this implementation's version will | ||||||
|  | //  instead be used. | ||||||
|  |  | ||||||
|  | #ifndef MINGW_SHARED_MUTEX_H_ | ||||||
|  | #define MINGW_SHARED_MUTEX_H_ | ||||||
|  |  | ||||||
|  | #if !defined(__cplusplus) || (__cplusplus < 201103L) | ||||||
|  | #error A C++11 compiler is required! | ||||||
|  | #endif | ||||||
|  |  | ||||||
|  | #include <cassert> | ||||||
|  | //  For descriptive errors. | ||||||
|  | #include <system_error> | ||||||
|  | //    Implementing a shared_mutex without OS support will require atomic read- | ||||||
|  | //  modify-write capacity. | ||||||
|  | #include <atomic> | ||||||
|  | //  For timing in shared_lock and shared_timed_mutex. | ||||||
|  | #include <chrono> | ||||||
|  | #include <limits> | ||||||
|  |  | ||||||
|  | //    Use MinGW's shared_lock class template, if it's available. Requires C++14. | ||||||
|  | //  If unavailable (eg. because this library is being used in C++11), then an | ||||||
|  | //  implementation of shared_lock is provided by this header. | ||||||
|  | #if (__cplusplus >= 201402L) | ||||||
|  | #include <shared_mutex> | ||||||
|  | #endif | ||||||
|  |  | ||||||
|  | //  For defer_lock_t, adopt_lock_t, and try_to_lock_t | ||||||
|  | #include "mingw.mutex.h" | ||||||
|  | //  For this_thread::yield. | ||||||
|  | //#include "mingw.thread.h" | ||||||
|  |  | ||||||
|  | //  Might be able to use native Slim Reader-Writer (SRW) locks. | ||||||
|  | #ifdef _WIN32 | ||||||
|  | #include <sdkddkver.h>  //  Detect Windows version. | ||||||
|  | #if (defined(__MINGW32__) && !defined(__MINGW64_VERSION_MAJOR)) | ||||||
|  | #pragma message "The Windows API that MinGW-w32 provides is not fully compatible\ | ||||||
|  |  with Microsoft's API. We'll try to work around this, but we can make no\ | ||||||
|  |  guarantees. This problem does not exist in MinGW-w64." | ||||||
|  | #include <windows.h>    //  No further granularity can be expected. | ||||||
|  | #else | ||||||
|  | #include <synchapi.h> | ||||||
|  | #endif | ||||||
|  | #endif | ||||||
|  |  | ||||||
|  | namespace mingw_stdthread | ||||||
|  | { | ||||||
|  | //  Define a portable atomics-based shared_mutex | ||||||
|  | namespace portable | ||||||
|  | { | ||||||
|  | class shared_mutex | ||||||
|  | { | ||||||
|  |     typedef uint_fast16_t counter_type; | ||||||
|  |     std::atomic<counter_type> mCounter {0}; | ||||||
|  |     static constexpr counter_type kWriteBit = 1 << (std::numeric_limits<counter_type>::digits - 1); | ||||||
|  |  | ||||||
|  | #if STDMUTEX_RECURSION_CHECKS | ||||||
|  | //  Runtime checker for verifying owner threads. Note: Exclusive mode only. | ||||||
|  |     _OwnerThread mOwnerThread {}; | ||||||
|  | #endif | ||||||
|  | public: | ||||||
|  |     typedef shared_mutex * native_handle_type; | ||||||
|  |  | ||||||
|  |     shared_mutex () = default; | ||||||
|  |  | ||||||
|  | //  No form of copying or moving should be allowed. | ||||||
|  |     shared_mutex (const shared_mutex&) = delete; | ||||||
|  |     shared_mutex & operator= (const shared_mutex&) = delete; | ||||||
|  |  | ||||||
|  |     ~shared_mutex () | ||||||
|  |     { | ||||||
|  | //  Terminate if someone tries to destroy an owned mutex. | ||||||
|  |         assert(mCounter.load(std::memory_order_relaxed) == 0); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     void lock_shared (void) | ||||||
|  |     { | ||||||
|  |         counter_type expected = mCounter.load(std::memory_order_relaxed); | ||||||
|  |         do | ||||||
|  |         { | ||||||
|  | //  Delay if writing or if too many readers are attempting to read. | ||||||
|  |             if (expected >= kWriteBit - 1) | ||||||
|  |             { | ||||||
|  |                 using namespace std; | ||||||
|  |                 expected = mCounter.load(std::memory_order_relaxed); | ||||||
|  |                 continue; | ||||||
|  |             } | ||||||
|  |             if (mCounter.compare_exchange_weak(expected, | ||||||
|  |                                                static_cast<counter_type>(expected + 1), | ||||||
|  |                                                std::memory_order_acquire, | ||||||
|  |                                                std::memory_order_relaxed)) | ||||||
|  |                 break; | ||||||
|  |         } | ||||||
|  |         while (true); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     bool try_lock_shared (void) | ||||||
|  |     { | ||||||
|  |         counter_type expected = mCounter.load(std::memory_order_relaxed) & static_cast<counter_type>(~kWriteBit); | ||||||
|  |         if (expected + 1 == kWriteBit) | ||||||
|  |             return false; | ||||||
|  |         else | ||||||
|  |             return mCounter.compare_exchange_strong( expected, | ||||||
|  |                                                     static_cast<counter_type>(expected + 1), | ||||||
|  |                                                     std::memory_order_acquire, | ||||||
|  |                                                     std::memory_order_relaxed); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     void unlock_shared (void) | ||||||
|  |     { | ||||||
|  |         using namespace std; | ||||||
|  | #ifndef NDEBUG | ||||||
|  |         if (!(mCounter.fetch_sub(1, memory_order_release) & static_cast<counter_type>(~kWriteBit))) | ||||||
|  |             throw system_error(make_error_code(errc::operation_not_permitted)); | ||||||
|  | #else | ||||||
|  |         mCounter.fetch_sub(1, memory_order_release); | ||||||
|  | #endif | ||||||
|  |     } | ||||||
|  |  | ||||||
|  | //  Behavior is undefined if a lock was previously acquired. | ||||||
|  |     void lock (void) | ||||||
|  |     { | ||||||
|  | #if STDMUTEX_RECURSION_CHECKS | ||||||
|  |         DWORD self = mOwnerThread.checkOwnerBeforeLock(); | ||||||
|  | #endif | ||||||
|  |         using namespace std; | ||||||
|  | //  Might be able to use relaxed memory order... | ||||||
|  | //  Wait for the write-lock to be unlocked, then claim the write slot. | ||||||
|  |         counter_type current; | ||||||
|  |         while ((current = mCounter.fetch_or(kWriteBit, std::memory_order_acquire)) & kWriteBit); | ||||||
|  |             //this_thread::yield(); | ||||||
|  | //  Wait for readers to finish up. | ||||||
|  |         while (current != kWriteBit) | ||||||
|  |         { | ||||||
|  |             //this_thread::yield(); | ||||||
|  |             current = mCounter.load(std::memory_order_acquire); | ||||||
|  |         } | ||||||
|  | #if STDMUTEX_RECURSION_CHECKS | ||||||
|  |         mOwnerThread.setOwnerAfterLock(self); | ||||||
|  | #endif | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     bool try_lock (void) | ||||||
|  |     { | ||||||
|  | #if STDMUTEX_RECURSION_CHECKS | ||||||
|  |         DWORD self = mOwnerThread.checkOwnerBeforeLock(); | ||||||
|  | #endif | ||||||
|  |         counter_type expected = 0; | ||||||
|  |         bool ret = mCounter.compare_exchange_strong(expected, kWriteBit, | ||||||
|  |                                                     std::memory_order_acquire, | ||||||
|  |                                                     std::memory_order_relaxed); | ||||||
|  | #if STDMUTEX_RECURSION_CHECKS | ||||||
|  |         if (ret) | ||||||
|  |             mOwnerThread.setOwnerAfterLock(self); | ||||||
|  | #endif | ||||||
|  |         return ret; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     void unlock (void) | ||||||
|  |     { | ||||||
|  | #if STDMUTEX_RECURSION_CHECKS | ||||||
|  |         mOwnerThread.checkSetOwnerBeforeUnlock(); | ||||||
|  | #endif | ||||||
|  |         using namespace std; | ||||||
|  | #ifndef NDEBUG | ||||||
|  |         if (mCounter.load(memory_order_relaxed) != kWriteBit) | ||||||
|  |             throw system_error(make_error_code(errc::operation_not_permitted)); | ||||||
|  | #endif | ||||||
|  |         mCounter.store(0, memory_order_release); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     native_handle_type native_handle (void) | ||||||
|  |     { | ||||||
|  |         return this; | ||||||
|  |     } | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | } //  Namespace portable | ||||||
|  |  | ||||||
|  | //    The native shared_mutex implementation primarily uses features of Windows | ||||||
|  | //  Vista, but the features used for try_lock and try_lock_shared were not | ||||||
|  | //  introduced until Windows 7. To allow limited use while compiling for Vista, | ||||||
|  | //  I define the class without try_* functions in that case. | ||||||
|  | //    Only fully-featured implementations will be placed into namespace std. | ||||||
|  | #if defined(_WIN32) && (WINVER >= _WIN32_WINNT_VISTA) | ||||||
|  | namespace vista | ||||||
|  | { | ||||||
|  | class condition_variable_any; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | namespace windows7 | ||||||
|  | { | ||||||
|  | //  We already #include "mingw.mutex.h". May as well reduce redundancy. | ||||||
|  | class shared_mutex : windows7::mutex | ||||||
|  | { | ||||||
|  | //    Allow condition_variable_any (and only condition_variable_any) to treat a | ||||||
|  | //  shared_mutex as its base class. | ||||||
|  |     friend class vista::condition_variable_any; | ||||||
|  | public: | ||||||
|  |     using windows7::mutex::native_handle_type; | ||||||
|  |     using windows7::mutex::lock; | ||||||
|  |     using windows7::mutex::unlock; | ||||||
|  |     using windows7::mutex::native_handle; | ||||||
|  |  | ||||||
|  |     void lock_shared (void) | ||||||
|  |     { | ||||||
|  |         AcquireSRWLockShared(native_handle()); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     void unlock_shared (void) | ||||||
|  |     { | ||||||
|  |         ReleaseSRWLockShared(native_handle()); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  | //  TryAcquireSRW functions are a Windows 7 feature. | ||||||
|  | #if (WINVER >= _WIN32_WINNT_WIN7) | ||||||
|  |     bool try_lock_shared (void) | ||||||
|  |     { | ||||||
|  |         return TryAcquireSRWLockShared(native_handle()) != 0; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     using windows7::mutex::try_lock; | ||||||
|  | #endif | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | } //  Namespace windows7 | ||||||
|  | #endif  //  Compiling for Vista | ||||||
|  | #if (defined(_WIN32) && (WINVER >= _WIN32_WINNT_WIN7)) | ||||||
|  | using windows7::shared_mutex; | ||||||
|  | #else | ||||||
|  | using portable::shared_mutex; | ||||||
|  | #endif | ||||||
|  |  | ||||||
|  | class shared_timed_mutex : shared_mutex | ||||||
|  | { | ||||||
|  |     typedef shared_mutex Base; | ||||||
|  | public: | ||||||
|  |     using Base::lock; | ||||||
|  |     using Base::try_lock; | ||||||
|  |     using Base::unlock; | ||||||
|  |     using Base::lock_shared; | ||||||
|  |     using Base::try_lock_shared; | ||||||
|  |     using Base::unlock_shared; | ||||||
|  |  | ||||||
|  |     template< class Clock, class Duration > | ||||||
|  |     bool try_lock_until ( const std::chrono::time_point<Clock,Duration>& cutoff ) | ||||||
|  |     { | ||||||
|  |         do | ||||||
|  |         { | ||||||
|  |             if (try_lock()) | ||||||
|  |                 return true; | ||||||
|  |         } | ||||||
|  |         while (std::chrono::steady_clock::now() < cutoff); | ||||||
|  |         return false; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     template< class Rep, class Period > | ||||||
|  |     bool try_lock_for (const std::chrono::duration<Rep,Period>& rel_time) | ||||||
|  |     { | ||||||
|  |         return try_lock_until(std::chrono::steady_clock::now() + rel_time); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     template< class Clock, class Duration > | ||||||
|  |     bool try_lock_shared_until ( const std::chrono::time_point<Clock,Duration>& cutoff ) | ||||||
|  |     { | ||||||
|  |         do | ||||||
|  |         { | ||||||
|  |             if (try_lock_shared()) | ||||||
|  |                 return true; | ||||||
|  |         } | ||||||
|  |         while (std::chrono::steady_clock::now() < cutoff); | ||||||
|  |         return false; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     template< class Rep, class Period > | ||||||
|  |     bool try_lock_shared_for (const std::chrono::duration<Rep,Period>& rel_time) | ||||||
|  |     { | ||||||
|  |         return try_lock_shared_until(std::chrono::steady_clock::now() + rel_time); | ||||||
|  |     } | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | #if __cplusplus >= 201402L | ||||||
|  | using std::shared_lock; | ||||||
|  | #else | ||||||
|  | //    If not supplied by shared_mutex (eg. because C++14 is not supported), I | ||||||
|  | //  supply the various helper classes that the header should have defined. | ||||||
|  | template<class Mutex> | ||||||
|  | class shared_lock | ||||||
|  | { | ||||||
|  |     Mutex * mMutex; | ||||||
|  |     bool mOwns; | ||||||
|  | //  Reduce code redundancy | ||||||
|  |     void verify_lockable (void) | ||||||
|  |     { | ||||||
|  |         using namespace std; | ||||||
|  |         if (mMutex == nullptr) | ||||||
|  |             throw system_error(make_error_code(errc::operation_not_permitted)); | ||||||
|  |         if (mOwns) | ||||||
|  |             throw system_error(make_error_code(errc::resource_deadlock_would_occur)); | ||||||
|  |     } | ||||||
|  | public: | ||||||
|  |     typedef Mutex mutex_type; | ||||||
|  |  | ||||||
|  |     shared_lock (void) noexcept | ||||||
|  |         : mMutex(nullptr), mOwns(false) | ||||||
|  |     { | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     shared_lock (shared_lock<Mutex> && other) noexcept | ||||||
|  |         : mMutex(other.mutex_), mOwns(other.owns_) | ||||||
|  |     { | ||||||
|  |         other.mMutex = nullptr; | ||||||
|  |         other.mOwns = false; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     explicit shared_lock (mutex_type & m) | ||||||
|  |         : mMutex(&m), mOwns(true) | ||||||
|  |     { | ||||||
|  |         mMutex->lock_shared(); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     shared_lock (mutex_type & m, defer_lock_t) noexcept | ||||||
|  |         : mMutex(&m), mOwns(false) | ||||||
|  |     { | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     shared_lock (mutex_type & m, adopt_lock_t) | ||||||
|  |         : mMutex(&m), mOwns(true) | ||||||
|  |     { | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     shared_lock (mutex_type & m, try_to_lock_t) | ||||||
|  |         : mMutex(&m), mOwns(m.try_lock_shared()) | ||||||
|  |     { | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     template< class Rep, class Period > | ||||||
|  |     shared_lock( mutex_type& m, const std::chrono::duration<Rep,Period>& timeout_duration ) | ||||||
|  |         : mMutex(&m), mOwns(m.try_lock_shared_for(timeout_duration)) | ||||||
|  |     { | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     template< class Clock, class Duration > | ||||||
|  |     shared_lock( mutex_type& m, const std::chrono::time_point<Clock,Duration>& timeout_time ) | ||||||
|  |         : mMutex(&m), mOwns(m.try_lock_shared_until(timeout_time)) | ||||||
|  |     { | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     shared_lock& operator= (shared_lock<Mutex> && other) noexcept | ||||||
|  |     { | ||||||
|  |         if (&other != this) | ||||||
|  |         { | ||||||
|  |             if (mOwns) | ||||||
|  |                 mMutex->unlock_shared(); | ||||||
|  |             mMutex = other.mMutex; | ||||||
|  |             mOwns = other.mOwns; | ||||||
|  |             other.mMutex = nullptr; | ||||||
|  |             other.mOwns = false; | ||||||
|  |         } | ||||||
|  |         return *this; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |  | ||||||
|  |     ~shared_lock (void) | ||||||
|  |     { | ||||||
|  |         if (mOwns) | ||||||
|  |             mMutex->unlock_shared(); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     shared_lock (const shared_lock<Mutex> &) = delete; | ||||||
|  |     shared_lock& operator= (const shared_lock<Mutex> &) = delete; | ||||||
|  |  | ||||||
|  | //  Shared locking | ||||||
|  |     void lock (void) | ||||||
|  |     { | ||||||
|  |         verify_lockable(); | ||||||
|  |         mMutex->lock_shared(); | ||||||
|  |         mOwns = true; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     bool try_lock (void) | ||||||
|  |     { | ||||||
|  |         verify_lockable(); | ||||||
|  |         mOwns = mMutex->try_lock_shared(); | ||||||
|  |         return mOwns; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     template< class Clock, class Duration > | ||||||
|  |     bool try_lock_until( const std::chrono::time_point<Clock,Duration>& cutoff ) | ||||||
|  |     { | ||||||
|  |         verify_lockable(); | ||||||
|  |         do | ||||||
|  |         { | ||||||
|  |             mOwns = mMutex->try_lock_shared(); | ||||||
|  |             if (mOwns) | ||||||
|  |                 return mOwns; | ||||||
|  |         } | ||||||
|  |         while (std::chrono::steady_clock::now() < cutoff); | ||||||
|  |         return false; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     template< class Rep, class Period > | ||||||
|  |     bool try_lock_for (const std::chrono::duration<Rep,Period>& rel_time) | ||||||
|  |     { | ||||||
|  |         return try_lock_until(std::chrono::steady_clock::now() + rel_time); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     void unlock (void) | ||||||
|  |     { | ||||||
|  |         using namespace std; | ||||||
|  |         if (!mOwns) | ||||||
|  |             throw system_error(make_error_code(errc::operation_not_permitted)); | ||||||
|  |         mMutex->unlock_shared(); | ||||||
|  |         mOwns = false; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  | //  Modifiers | ||||||
|  |     void swap (shared_lock<Mutex> & other) noexcept | ||||||
|  |     { | ||||||
|  |         using namespace std; | ||||||
|  |         swap(mMutex, other.mMutex); | ||||||
|  |         swap(mOwns, other.mOwns); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     mutex_type * release (void) noexcept | ||||||
|  |     { | ||||||
|  |         mutex_type * ptr = mMutex; | ||||||
|  |         mMutex = nullptr; | ||||||
|  |         mOwns = false; | ||||||
|  |         return ptr; | ||||||
|  |     } | ||||||
|  | //  Observers | ||||||
|  |     mutex_type * mutex (void) const noexcept | ||||||
|  |     { | ||||||
|  |         return mMutex; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     bool owns_lock (void) const noexcept | ||||||
|  |     { | ||||||
|  |         return mOwns; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     explicit operator bool () const noexcept | ||||||
|  |     { | ||||||
|  |         return owns_lock(); | ||||||
|  |     } | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | template< class Mutex > | ||||||
|  | void swap( shared_lock<Mutex>& lhs, shared_lock<Mutex>& rhs ) noexcept | ||||||
|  | { | ||||||
|  |     lhs.swap(rhs); | ||||||
|  | } | ||||||
|  | #endif  //  C++11 | ||||||
|  | } //  Namespace mingw_stdthread | ||||||
|  |  | ||||||
|  | namespace std | ||||||
|  | { | ||||||
|  | //    Because of quirks of the compiler, the common "using namespace std;" | ||||||
|  | //  directive would flatten the namespaces and introduce ambiguity where there | ||||||
|  | //  was none. Direct specification (std::), however, would be unaffected. | ||||||
|  | //    Take the safe option, and include only in the presence of MinGW's win32 | ||||||
|  | //  implementation. | ||||||
|  | #if (__cplusplus < 201703L) || (defined(__MINGW32__ ) && !defined(_GLIBCXX_HAS_GTHREADS)) | ||||||
|  | using mingw_stdthread::shared_mutex; | ||||||
|  | #endif | ||||||
|  | #if (__cplusplus < 201402L) || (defined(__MINGW32__ ) && !defined(_GLIBCXX_HAS_GTHREADS)) | ||||||
|  | using mingw_stdthread::shared_timed_mutex; | ||||||
|  | using mingw_stdthread::shared_lock; | ||||||
|  | #elif !defined(MINGW_STDTHREAD_REDUNDANCY_WARNING)  //  Skip repetition | ||||||
|  | #define MINGW_STDTHREAD_REDUNDANCY_WARNING | ||||||
|  | #pragma message "This version of MinGW seems to include a win32 port of\ | ||||||
|  |  pthreads, and probably already has C++ std threading classes implemented,\ | ||||||
|  |  based on pthreads. These classes, found in namespace std, are not overridden\ | ||||||
|  |  by the mingw-std-thread library. If you would still like to use this\ | ||||||
|  |  implementation (as it is more lightweight), use the classes provided in\ | ||||||
|  |  namespace mingw_stdthread." | ||||||
|  | #endif | ||||||
|  | } //  Namespace std | ||||||
|  | #endif // MINGW_SHARED_MUTEX_H_ | ||||||
| @@ -432,9 +432,11 @@ extern "C" { | |||||||
| #if WINDOWS | #if WINDOWS | ||||||
| #include "mingw.thread.h" | #include "mingw.thread.h" | ||||||
| #include "mingw.mutex.h" | #include "mingw.mutex.h" | ||||||
|  | #include "mingw.condition_variable.h" | ||||||
| #else | #else | ||||||
| #include <thread> | #include <thread> | ||||||
| #include <mutex> | #include <mutex> | ||||||
|  | #include <condition_variable> | ||||||
| #endif | #endif | ||||||
| #endif | #endif | ||||||
|  |  | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user
	 Zeno Rogue
					Zeno Rogue