From ea2b4fb4935627e4dea48f193eeb0019155a3abe Mon Sep 17 00:00:00 2001 From: Boris Kolpackov Date: Tue, 10 May 2022 09:48:08 +0200 Subject: Use our own implementation of C++14 threads on MinGW --- libbutl/mingw-condition_variable.hxx | 267 +++++++++++++++++++++++++++++++++++ 1 file changed, 267 insertions(+) create mode 100644 libbutl/mingw-condition_variable.hxx (limited to 'libbutl/mingw-condition_variable.hxx') diff --git a/libbutl/mingw-condition_variable.hxx b/libbutl/mingw-condition_variable.hxx new file mode 100644 index 0000000..ce94941 --- /dev/null +++ b/libbutl/mingw-condition_variable.hxx @@ -0,0 +1,267 @@ +/** +* std::condition_variable implementation for MinGW-w64 +* +* Copyright (c) 2013-2016 by Mega Limited, Auckland, New Zealand +* Copyright (c) 2022 the build2 authors +* +* Licensed under the 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. +*/ + +#ifndef LIBBUTL_MINGW_CONDITION_VARIABLE_HXX +#define LIBBUTL_MINGW_CONDITION_VARIABLE_HXX + +#if !defined(__cplusplus) || (__cplusplus < 201402L) +# error C++14 compiler required +#endif + +#if !defined(_WIN32_WINNT) || _WIN32_WINNT < 0x0601 +# error _WIN32_WINNT should be 0x0601 (Windows 7) or greater +#endif + +#include // Use std::cv_status, if available. + +#include +#include +#include + +#include + +#include +#include + +namespace mingw_stdthread +{ +#if defined(__MINGW32__ ) && !defined(_GLIBCXX_HAS_GTHREADS) + enum class cv_status { no_timeout, timeout }; +#else + using std::cv_status; +#endif + + // Native condition variable-based implementation. + // + 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; + + bool wait_unique (mutex * pmutex, DWORD time) + { + 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); + return success; + } + bool wait_impl (unique_lock & lock, DWORD time) + { + mutex * pmutex = lock.release(); + bool success = wait_unique(pmutex, time); + lock = unique_lock(*pmutex, adopt_lock); + return success; + } +public: + using native_handle_type = PCONDITION_VARIABLE; + native_handle_type native_handle () + { + return &cvariable_; + } + + condition_variable () = default; + ~condition_variable () = default; + + condition_variable (const condition_variable &) = delete; + condition_variable & operator= (const condition_variable &) = delete; + + void notify_one () noexcept + { + WakeConditionVariable(&cvariable_); + } + + void notify_all () noexcept + { + WakeAllConditionVariable(&cvariable_); + } + + void wait (unique_lock & lock) + { + wait_impl(lock, kInfinite); + } + + template + void wait (unique_lock & lock, Predicate pred) + { + while (!pred()) + wait(lock); + } + + template + cv_status wait_for(unique_lock& lock, + const std::chrono::duration& rel_time) + { + using namespace std::chrono; + auto timeout = duration_cast(rel_time).count(); + DWORD waittime = (timeout < kInfinite) ? ((timeout < 0) ? 0 : static_cast(timeout)) : (kInfinite - 1); + bool result = wait_impl(lock, waittime) || (timeout >= kInfinite); + return result ? cv_status::no_timeout : cv_status::timeout; + } + + template + bool wait_for(unique_lock& lock, + const std::chrono::duration& rel_time, + Predicate pred) + { + return wait_until(lock, + std::chrono::steady_clock::now() + rel_time, + std::move(pred)); + } + template + cv_status wait_until (unique_lock& lock, + const std::chrono::time_point& abs_time) + { + return wait_for(lock, abs_time - Clock::now()); + } + template + bool wait_until (unique_lock& lock, + const std::chrono::time_point& 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; + + condition_variable internal_cv_ {}; + mutex internal_mutex_ {}; + + template + bool wait_impl (L & lock, DWORD time) + { + unique_lock 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 & lock, DWORD time) + { + return internal_cv_.wait_impl(lock, time); + } + bool wait_impl (unique_lock & lock, DWORD time) + { + shared_mutex * pmutex = lock.release(); + bool success = internal_cv_.wait_unique(pmutex, time); + lock = unique_lock(*pmutex, adopt_lock); + return success; + } + bool wait_impl (shared_lock & lock, DWORD time) + { + shared_mutex * pmutex = lock.release(); + BOOL success = SleepConditionVariableSRW(native_handle(), + pmutex->native_handle(), time, + CONDITION_VARIABLE_LOCKMODE_SHARED); + lock = shared_lock(*pmutex, adopt_lock); + return success; + } + public: + using native_handle_type = typename condition_variable::native_handle_type; + + native_handle_type native_handle () + { + return internal_cv_.native_handle(); + } + + void notify_one () noexcept + { + internal_cv_.notify_one(); + } + + void notify_all () noexcept + { + internal_cv_.notify_all(); + } + + condition_variable_any () = default; + ~condition_variable_any () = default; + + template + void wait (L & lock) + { + wait_impl(lock, kInfinite); + } + + template + void wait (L & lock, Predicate pred) + { + while (!pred()) + wait(lock); + } + + template + cv_status wait_for(L& lock, const std::chrono::duration& period) + { + using namespace std::chrono; + auto timeout = duration_cast(period).count(); + DWORD waittime = (timeout < kInfinite) ? ((timeout < 0) ? 0 : static_cast(timeout)) : (kInfinite - 1); + bool result = wait_impl(lock, waittime) || (timeout >= kInfinite); + return result ? cv_status::no_timeout : cv_status::timeout; + } + + template + bool wait_for(L& lock, const std::chrono::duration& period, + Predicate pred) + { + return wait_until(lock, std::chrono::steady_clock::now() + period, + std::move(pred)); + } + template + cv_status wait_until (L& lock, + const std::chrono::time_point& abs_time) + { + return wait_for(lock, abs_time - Clock::now()); + } + template + bool wait_until (L& lock, + const std::chrono::time_point& abs_time, + Predicate pred) + { + while (!pred()) + { + if (wait_until(lock, abs_time) == cv_status::timeout) + { + return pred(); + } + } + return true; + } + }; +} + +#endif // LIBBUTL_MINGW_CONDITION_VARIABLE_HXX -- cgit v1.1