all repos — mgba @ 8c68d867e61f6227dad735390444ff8e41510f4f

mGBA Game Boy Advance Emulator

src/third-party/discord-rpc/include/mingw-std-threads/condition_variable (view raw)

  1/**
  2* @file condition_variable.h
  3* @brief std::condition_variable implementation for MinGW
  4*
  5* (c) 2013-2016 by Mega Limited, Auckland, New Zealand
  6* @author Alexander Vassilev
  7*
  8* @copyright Simplified (2-clause) BSD License.
  9* You should have received a copy of the license along with this
 10* program.
 11*
 12* This code is distributed in the hope that it will be useful,
 13* but WITHOUT ANY WARRANTY; without even the implied warranty of
 14* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 15* @note
 16* This file may become part of the mingw-w64 runtime package. If/when this happens,
 17* the appropriate license will be added, i.e. this code will become dual-licensed,
 18* and the current BSD 2-clause license will stay.
 19*/
 20
 21#ifndef MINGW_CONDITIONAL_VARIABLE_H
 22#define MINGW_CONDITIONAL_VARIABLE_H
 23
 24#if !defined(__cplusplus) || (__cplusplus < 201103L)
 25#error A C++11 compiler is required!
 26#endif
 27//  Use the standard classes for std::, if available.
 28#include_next <condition_variable>
 29
 30#include <cassert>
 31#include <chrono>
 32#include <system_error>
 33#include <windows.h>
 34
 35#if (WINVER < _WIN32_WINNT_VISTA)
 36#include <atomic>
 37#endif
 38
 39#include <mutex>
 40#include <shared_mutex>
 41
 42#if !defined(_WIN32_WINNT) || (_WIN32_WINNT < 0x0501)
 43#error To use the MinGW-std-threads library, you will need to define the macro _WIN32_WINNT to be 0x0501 (Windows XP) or higher.
 44#endif
 45
 46namespace mingw_stdthread
 47{
 48#if defined(__MINGW32__ ) && !defined(_GLIBCXX_HAS_GTHREADS)
 49enum class cv_status { no_timeout, timeout };
 50#else
 51using std::cv_status;
 52#endif
 53namespace xp
 54{
 55//    Include the XP-compatible condition_variable classes only if actually
 56//  compiling for XP. The XP-compatible classes are slower than the newer
 57//  versions, and depend on features not compatible with Windows Phone 8.
 58#if (WINVER < _WIN32_WINNT_VISTA)
 59class condition_variable_any
 60{
 61    recursive_mutex mMutex {};
 62    std::atomic<int> mNumWaiters {0};
 63    HANDLE mSemaphore;
 64    HANDLE mWakeEvent {};
 65public:
 66    using native_handle_type = HANDLE;
 67    native_handle_type native_handle()
 68    {
 69        return mSemaphore;
 70    }
 71    condition_variable_any(const condition_variable_any&) = delete;
 72    condition_variable_any& operator=(const condition_variable_any&) = delete;
 73    condition_variable_any()
 74        :   mSemaphore(CreateSemaphore(NULL, 0, 0xFFFF, NULL))
 75    {
 76        if (mSemaphore == NULL)
 77            throw std::system_error(GetLastError(), std::generic_category());
 78        mWakeEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
 79        if (mWakeEvent == NULL)
 80        {
 81            CloseHandle(mSemaphore);
 82            throw std::system_error(GetLastError(), std::generic_category());
 83        }
 84    }
 85    ~condition_variable_any()
 86    {
 87        CloseHandle(mWakeEvent);
 88        CloseHandle(mSemaphore);
 89    }
 90private:
 91    template <class M>
 92    bool wait_impl(M& lock, DWORD timeout)
 93    {
 94        {
 95            lock_guard<recursive_mutex> guard(mMutex);
 96            mNumWaiters++;
 97        }
 98        lock.unlock();
 99        DWORD ret = WaitForSingleObject(mSemaphore, timeout);
100
101        mNumWaiters--;
102        SetEvent(mWakeEvent);
103        lock.lock();
104        if (ret == WAIT_OBJECT_0)
105            return true;
106        else if (ret == WAIT_TIMEOUT)
107            return false;
108//2 possible cases:
109//1)The point in notify_all() where we determine the count to
110//increment the semaphore with has not been reached yet:
111//we just need to decrement mNumWaiters, but setting the event does not hurt
112//
113//2)Semaphore has just been released with mNumWaiters just before
114//we decremented it. This means that the semaphore count
115//after all waiters finish won't be 0 - because not all waiters
116//woke up by acquiring the semaphore - we woke up by a timeout.
117//The notify_all() must handle this gracefully
118//
119        else
120        {
121            using namespace std;
122            throw system_error(make_error_code(errc::protocol_error));
123        }
124    }
125public:
126    template <class M>
127    void wait(M& lock)
128    {
129        wait_impl(lock, INFINITE);
130    }
131    template <class M, class Predicate>
132    void wait(M& lock, Predicate pred)
133    {
134        while(!pred())
135        {
136            wait(lock);
137        };
138    }
139
140    void notify_all() noexcept
141    {
142        lock_guard<recursive_mutex> lock(mMutex); //block any further wait requests until all current waiters are unblocked
143        if (mNumWaiters.load() <= 0)
144            return;
145
146        ReleaseSemaphore(mSemaphore, mNumWaiters, NULL);
147        while(mNumWaiters > 0)
148        {
149            auto ret = WaitForSingleObject(mWakeEvent, 1000);
150            if (ret == WAIT_FAILED || ret == WAIT_ABANDONED)
151                std::terminate();
152        }
153        assert(mNumWaiters == 0);
154//in case some of the waiters timed out just after we released the
155//semaphore by mNumWaiters, it won't be zero now, because not all waiters
156//woke up by acquiring the semaphore. So we must zero the semaphore before
157//we accept waiters for the next event
158//See _wait_impl for details
159        while(WaitForSingleObject(mSemaphore, 0) == WAIT_OBJECT_0);
160    }
161    void notify_one() noexcept
162    {
163        lock_guard<recursive_mutex> lock(mMutex);
164        int targetWaiters = mNumWaiters.load() - 1;
165        if (targetWaiters <= -1)
166            return;
167        ReleaseSemaphore(mSemaphore, 1, NULL);
168        while(mNumWaiters > targetWaiters)
169        {
170            auto ret = WaitForSingleObject(mWakeEvent, 1000);
171            if (ret == WAIT_FAILED || ret == WAIT_ABANDONED)
172                std::terminate();
173        }
174        assert(mNumWaiters == targetWaiters);
175    }
176    template <class M, class Rep, class Period>
177    cv_status wait_for(M& lock,
178                       const std::chrono::duration<Rep, Period>& rel_time)
179    {
180        using namespace std::chrono;
181        auto timeout = duration_cast<milliseconds>(rel_time).count();
182        DWORD waittime = (timeout < INFINITE) ? ((timeout < 0) ? 0 : static_cast<DWORD>(timeout)) : (INFINITE - 1);
183        bool ret = wait_impl(lock, waittime) || (timeout >= INFINITE);
184        return ret?cv_status::no_timeout:cv_status::timeout;
185    }
186
187    template <class M, class Rep, class Period, class Predicate>
188    bool wait_for(M& lock,
189                  const std::chrono::duration<Rep, Period>& rel_time, Predicate pred)
190    {
191        return wait_until(lock, std::chrono::steady_clock::now()+rel_time, pred);
192    }
193    template <class M, class Clock, class Duration>
194    cv_status wait_until (M& lock,
195                          const std::chrono::time_point<Clock,Duration>& abs_time)
196    {
197        return wait_for(lock, abs_time - Clock::now());
198    }
199    template <class M, class Clock, class Duration, class Predicate>
200    bool wait_until (M& lock,
201                     const std::chrono::time_point<Clock, Duration>& abs_time,
202                     Predicate pred)
203    {
204        while (!pred())
205        {
206            if (wait_until(lock, abs_time) == cv_status::timeout)
207            {
208                return pred();
209            }
210        }
211        return true;
212    }
213};
214class condition_variable: condition_variable_any
215{
216    using base = condition_variable_any;
217public:
218    using base::native_handle_type;
219    using base::native_handle;
220    using base::base;
221    using base::notify_all;
222    using base::notify_one;
223    void wait(unique_lock<mutex> &lock)
224    {
225        base::wait(lock);
226    }
227    template <class Predicate>
228    void wait(unique_lock<mutex>& lock, Predicate pred)
229    {
230        base::wait(lock, pred);
231    }
232    template <class Rep, class Period>
233    cv_status wait_for(unique_lock<mutex>& lock, const std::chrono::duration<Rep, Period>& rel_time)
234    {
235        return base::wait_for(lock, rel_time);
236    }
237    template <class Rep, class Period, class Predicate>
238    bool wait_for(unique_lock<mutex>& lock, const std::chrono::duration<Rep, Period>& rel_time, Predicate pred)
239    {
240        return base::wait_for(lock, rel_time, pred);
241    }
242    template <class Clock, class Duration>
243    cv_status wait_until (unique_lock<mutex>& lock, const std::chrono::time_point<Clock,Duration>& abs_time)
244    {
245        return base::wait_until(lock, abs_time);
246    }
247    template <class Clock, class Duration, class Predicate>
248    bool wait_until (unique_lock<mutex>& lock, const std::chrono::time_point<Clock, Duration>& abs_time, Predicate pred)
249    {
250        return base::wait_until(lock, abs_time, pred);
251    }
252};
253#endif  //  Compiling for XP
254} //  Namespace mingw_stdthread::xp
255
256#if (WINVER >= _WIN32_WINNT_VISTA)
257namespace vista
258{
259//  If compiling for Vista or higher, use the native condition variable.
260class condition_variable
261{
262#pragma GCC diagnostic push
263#pragma GCC diagnostic ignored "-Wzero-as-null-pointer-constant"
264    CONDITION_VARIABLE cvariable_ = CONDITION_VARIABLE_INIT;
265#pragma GCC diagnostic pop
266
267    friend class condition_variable_any;
268
269#if STDMUTEX_RECURSION_CHECKS
270    template<typename MTX>
271    inline static void before_wait (MTX * pmutex)
272    {
273        pmutex->mOwnerThread.checkSetOwnerBeforeUnlock();
274    }
275    template<typename MTX>
276    inline static void after_wait (MTX * pmutex)
277    {
278        pmutex->mOwnerThread.setOwnerAfterLock(GetCurrentThreadId());
279    }
280#else
281    inline static void before_wait (void *) { }
282    inline static void after_wait (void *) { }
283#endif
284
285    bool wait_impl (unique_lock<xp::mutex> & lock, DWORD time)
286    {
287        using mutex_handle_type = typename xp::mutex::native_handle_type;
288        static_assert(std::is_same<mutex_handle_type, PCRITICAL_SECTION>::value,
289                      "Native Win32 condition variable requires std::mutex to \
290use native Win32 critical section objects.");
291        xp::mutex * pmutex = lock.release();
292        before_wait(pmutex);
293        BOOL success = SleepConditionVariableCS(&cvariable_,
294                                                pmutex->native_handle(),
295                                                time);
296        after_wait(pmutex);
297        lock = unique_lock<xp::mutex>(*pmutex, adopt_lock);
298        return success;
299    }
300
301    bool wait_unique (windows7::mutex * pmutex, DWORD time)
302    {
303        before_wait(pmutex);
304        BOOL success = SleepConditionVariableSRW( native_handle(),
305                                                  pmutex->native_handle(),
306                                                  time,
307//    CONDITION_VARIABLE_LOCKMODE_SHARED has a value not specified by
308//  Microsoft's Dev Center, but is known to be (convertible to) a ULONG. To
309//  ensure that the value passed to this function is not equal to Microsoft's
310//  constant, we can either use a static_assert, or simply generate an
311//  appropriate value.
312                                           !CONDITION_VARIABLE_LOCKMODE_SHARED);
313        after_wait(pmutex);
314        return success;
315    }
316    bool wait_impl (unique_lock<windows7::mutex> & lock, DWORD time)
317    {
318        windows7::mutex * pmutex = lock.release();
319        bool success = wait_unique(pmutex, time);
320        lock = unique_lock<windows7::mutex>(*pmutex, adopt_lock);
321        return success;
322    }
323public:
324    using native_handle_type = PCONDITION_VARIABLE;
325    native_handle_type native_handle (void)
326    {
327        return &cvariable_;
328    }
329
330    condition_variable (void) = default;
331    ~condition_variable (void) = default;
332
333    condition_variable (const condition_variable &) = delete;
334    condition_variable & operator= (const condition_variable &) = delete;
335
336    void notify_one (void) noexcept
337    {
338        WakeConditionVariable(&cvariable_);
339    }
340
341    void notify_all (void) noexcept
342    {
343        WakeAllConditionVariable(&cvariable_);
344    }
345
346    void wait (unique_lock<mutex> & lock)
347    {
348        wait_impl(lock, INFINITE);
349    }
350
351    template<class Predicate>
352    void wait (unique_lock<mutex> & lock, Predicate pred)
353    {
354        while (!pred())
355            wait(lock);
356    }
357
358    template <class Rep, class Period>
359    cv_status wait_for(unique_lock<mutex>& lock,
360                       const std::chrono::duration<Rep, Period>& rel_time)
361    {
362        using namespace std::chrono;
363        auto timeout = duration_cast<milliseconds>(rel_time).count();
364        DWORD waittime = (timeout < INFINITE) ? ((timeout < 0) ? 0 : static_cast<DWORD>(timeout)) : (INFINITE - 1);
365        bool result = wait_impl(lock, waittime) || (timeout >= INFINITE);
366        return result ? cv_status::no_timeout : cv_status::timeout;
367    }
368
369    template <class Rep, class Period, class Predicate>
370    bool wait_for(unique_lock<mutex>& lock,
371                  const std::chrono::duration<Rep, Period>& rel_time,
372                  Predicate pred)
373    {
374        return wait_until(lock,
375                          std::chrono::steady_clock::now() + rel_time,
376                          std::move(pred));
377    }
378    template <class Clock, class Duration>
379    cv_status wait_until (unique_lock<mutex>& lock,
380                          const std::chrono::time_point<Clock,Duration>& abs_time)
381    {
382        return wait_for(lock, abs_time - Clock::now());
383    }
384    template <class Clock, class Duration, class Predicate>
385    bool wait_until  (unique_lock<mutex>& lock,
386                      const std::chrono::time_point<Clock, Duration>& abs_time,
387                      Predicate pred)
388    {
389        while (!pred())
390        {
391            if (wait_until(lock, abs_time) == cv_status::timeout)
392            {
393                return pred();
394            }
395        }
396        return true;
397    }
398};
399
400class condition_variable_any
401{
402    using native_shared_mutex = windows7::shared_mutex;
403
404    condition_variable internal_cv_ {};
405//    When available, the SRW-based mutexes should be faster than the
406//  CriticalSection-based mutexes. Only try_lock will be unavailable in Vista,
407//  and try_lock is not used by condition_variable_any.
408    windows7::mutex internal_mutex_ {};
409
410    template<class L>
411    bool wait_impl (L & lock, DWORD time)
412    {
413        unique_lock<decltype(internal_mutex_)> internal_lock(internal_mutex_);
414        lock.unlock();
415        bool success = internal_cv_.wait_impl(internal_lock, time);
416        lock.lock();
417        return success;
418    }
419//    If the lock happens to be called on a native Windows mutex, skip any extra
420//  contention.
421    inline bool wait_impl (unique_lock<mutex> & lock, DWORD time)
422    {
423        return internal_cv_.wait_impl(lock, time);
424    }
425//    Some shared_mutex functionality is available even in Vista, but it's not
426//  until Windows 7 that a full implementation is natively possible. The class
427//  itself is defined, with missing features, at the Vista feature level.
428    bool wait_impl (unique_lock<native_shared_mutex> & lock, DWORD time)
429    {
430        native_shared_mutex * pmutex = lock.release();
431        bool success = internal_cv_.wait_unique(pmutex, time);
432        lock = unique_lock<native_shared_mutex>(*pmutex, adopt_lock);
433        return success;
434    }
435    bool wait_impl (shared_lock<native_shared_mutex> & lock, DWORD time)
436    {
437        native_shared_mutex * pmutex = lock.release();
438        BOOL success = SleepConditionVariableSRW(native_handle(),
439                       pmutex->native_handle(), time,
440                       CONDITION_VARIABLE_LOCKMODE_SHARED);
441        lock = shared_lock<native_shared_mutex>(*pmutex, adopt_lock);
442        return success;
443    }
444public:
445    using native_handle_type = typename condition_variable::native_handle_type;
446
447    native_handle_type native_handle (void)
448    {
449        return internal_cv_.native_handle();
450    }
451
452    void notify_one (void) noexcept
453    {
454        internal_cv_.notify_one();
455    }
456
457    void notify_all (void) noexcept
458    {
459        internal_cv_.notify_all();
460    }
461
462    condition_variable_any (void) = default;
463    ~condition_variable_any (void) = default;
464
465    template<class L>
466    void wait (L & lock)
467    {
468        wait_impl(lock, INFINITE);
469    }
470
471    template<class L, class Predicate>
472    void wait (L & lock, Predicate pred)
473    {
474        while (!pred())
475            wait(lock);
476    }
477
478    template <class L, class Rep, class Period>
479    cv_status wait_for(L& lock, const std::chrono::duration<Rep,Period>& period)
480    {
481        using namespace std::chrono;
482        auto timeout = duration_cast<milliseconds>(period).count();
483        DWORD waittime = (timeout < INFINITE) ? ((timeout < 0) ? 0 : static_cast<DWORD>(timeout)) : (INFINITE - 1);
484        bool result = wait_impl(lock, waittime) || (timeout >= INFINITE);
485        return result ? cv_status::no_timeout : cv_status::timeout;
486    }
487
488    template <class L, class Rep, class Period, class Predicate>
489    bool wait_for(L& lock, const std::chrono::duration<Rep, Period>& period,
490                  Predicate pred)
491    {
492        return wait_until(lock, std::chrono::steady_clock::now() + period,
493                          std::move(pred));
494    }
495    template <class L, class Clock, class Duration>
496    cv_status wait_until (L& lock,
497                          const std::chrono::time_point<Clock,Duration>& abs_time)
498    {
499        return wait_for(lock, abs_time - Clock::now());
500    }
501    template <class L, class Clock, class Duration, class Predicate>
502    bool wait_until  (L& lock,
503                      const std::chrono::time_point<Clock, Duration>& abs_time,
504                      Predicate pred)
505    {
506        while (!pred())
507        {
508            if (wait_until(lock, abs_time) == cv_status::timeout)
509            {
510                return pred();
511            }
512        }
513        return true;
514    }
515};
516} //  Namespace vista
517#endif
518#if WINVER < 0x0600
519using xp::condition_variable;
520using xp::condition_variable_any;
521#else
522using vista::condition_variable;
523using vista::condition_variable_any;
524#endif
525} //  Namespace mingw_stdthread
526
527//  Push objects into std, but only if they are not already there.
528namespace std
529{
530//    Because of quirks of the compiler, the common "using namespace std;"
531//  directive would flatten the namespaces and introduce ambiguity where there
532//  was none. Direct specification (std::), however, would be unaffected.
533//    Take the safe option, and include only in the presence of MinGW's win32
534//  implementation.
535#if defined(__MINGW32__ ) && !defined(_GLIBCXX_HAS_GTHREADS)
536using mingw_stdthread::cv_status;
537using mingw_stdthread::condition_variable;
538using mingw_stdthread::condition_variable_any;
539#elif !defined(MINGW_STDTHREAD_REDUNDANCY_WARNING)  //  Skip repetition
540#define MINGW_STDTHREAD_REDUNDANCY_WARNING
541#pragma message "This version of MinGW seems to include a win32 port of\
542 pthreads, and probably already has C++11 std threading classes implemented,\
543 based on pthreads. These classes, found in namespace std, are not overridden\
544 by the mingw-std-thread library. If you would still like to use this\
545 implementation (as it is more lightweight), use the classes provided in\
546 namespace mingw_stdthread."
547#endif
548}
549#endif // MINGW_CONDITIONAL_VARIABLE_H