From fe4850c0d0e8eed3fe851007c667206684aab0fc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pedro=20J=2E=20Est=C3=A9banez?= Date: Fri, 17 Nov 2023 20:44:38 +0100 Subject: [PATCH] Use mingw-std-threads in MinGW builds --- COPYRIGHT.txt | 5 + core/math/aabb.cpp | 5 + core/object/object.h | 2 +- core/os/condition_variable.h | 11 +- core/os/mutex.cpp | 8 +- core/os/mutex.h | 27 +- core/os/os.cpp | 10 +- core/os/rw_lock.h | 9 +- core/os/semaphore.h | 16 +- core/os/thread.cpp | 6 +- core/os/thread.h | 9 +- editor/dependency_editor.h | 4 + editor/plugins/script_editor_plugin.h | 4 + editor/plugins/shader_editor_plugin.h | 4 + editor/plugins/visual_shader_editor_plugin.h | 4 + modules/gdscript/gdscript_tokenizer.h | 6 + .../editor/editor_scene_importer_blend.cpp | 5 + scene/3d/camera_3d.h | 5 + scene/debugger/scene_debugger.cpp | 5 + scene/resources/camera_attributes.cpp | 5 + servers/rendering/renderer_scene_render.cpp | 5 + thirdparty/README.md | 18 + thirdparty/mingw-std-threads/LICENSE | 24 + .../mingw.condition_variable.h | 564 ++++++++++++++++++ thirdparty/mingw-std-threads/mingw.invoke.h | 109 ++++ thirdparty/mingw-std-threads/mingw.mutex.h | 500 ++++++++++++++++ .../mingw-std-threads/mingw.shared_mutex.h | 503 ++++++++++++++++ thirdparty/mingw-std-threads/mingw.thread.h | 360 +++++++++++ thirdparty/mingw-std-threads/no_except.patch | 137 +++++ 29 files changed, 2342 insertions(+), 28 deletions(-) create mode 100644 thirdparty/mingw-std-threads/LICENSE create mode 100644 thirdparty/mingw-std-threads/mingw.condition_variable.h create mode 100644 thirdparty/mingw-std-threads/mingw.invoke.h create mode 100644 thirdparty/mingw-std-threads/mingw.mutex.h create mode 100644 thirdparty/mingw-std-threads/mingw.shared_mutex.h create mode 100644 thirdparty/mingw-std-threads/mingw.thread.h create mode 100644 thirdparty/mingw-std-threads/no_except.patch diff --git a/COPYRIGHT.txt b/COPYRIGHT.txt index fe046863fcd..03415b4c39a 100644 --- a/COPYRIGHT.txt +++ b/COPYRIGHT.txt @@ -328,6 +328,11 @@ Comment: meshoptimizer Copyright: 2016-2022, Arseny Kapoulkine License: Expat +Files: ./thirdparty/mingw-std-threads/ +Comment: mingw-std-threads +Copyright: 2016, Mega Limited +License: BSD-2-clause + Files: ./thirdparty/minimp3/ Comment: MiniMP3 Copyright: lieff diff --git a/core/math/aabb.cpp b/core/math/aabb.cpp index 1071df09792..e1d49dacc07 100644 --- a/core/math/aabb.cpp +++ b/core/math/aabb.cpp @@ -117,6 +117,11 @@ AABB AABB::intersection(const AABB &p_aabb) const { return AABB(min, max - min); } +#ifdef MINGW_ENABLED +#undef near +#undef far +#endif + bool AABB::intersects_ray(const Vector3 &p_from, const Vector3 &p_dir, Vector3 *r_clip, Vector3 *r_normal) const { #ifdef MATH_CHECKS if (unlikely(size.x < 0 || size.y < 0 || size.z < 0)) { diff --git a/core/object/object.h b/core/object/object.h index 7b53fcaa416..fdd1c0b2676 100644 --- a/core/object/object.h +++ b/core/object/object.h @@ -656,7 +656,7 @@ private: friend class RefCounted; bool type_is_reference = false; - std::mutex _instance_binding_mutex; + BinaryMutex _instance_binding_mutex; struct InstanceBinding { void *binding = nullptr; void *token = nullptr; diff --git a/core/os/condition_variable.h b/core/os/condition_variable.h index 6037ff327dd..6a6996019d3 100644 --- a/core/os/condition_variable.h +++ b/core/os/condition_variable.h @@ -31,7 +31,14 @@ #ifndef CONDITION_VARIABLE_H #define CONDITION_VARIABLE_H +#ifdef MINGW_ENABLED +#define MINGW_STDTHREAD_REDUNDANCY_WARNING +#include "thirdparty/mingw-std-threads/mingw.condition_variable.h" +#define THREADING_NAMESPACE mingw_stdthread +#else #include +#define THREADING_NAMESPACE std +#endif // An object one or multiple threads can wait on a be notified by some other. // Normally, you want to use a semaphore for such scenarios, but when the @@ -40,12 +47,12 @@ // own mutex to tie the wait-notify to some other behavior, you need to use this. class ConditionVariable { - mutable std::condition_variable condition; + mutable THREADING_NAMESPACE::condition_variable condition; public: template _ALWAYS_INLINE_ void wait(const MutexLock &p_lock) const { - condition.wait(const_cast &>(p_lock.lock)); + condition.wait(const_cast &>(p_lock.lock)); } _ALWAYS_INLINE_ void notify_one() const { diff --git a/core/os/mutex.cpp b/core/os/mutex.cpp index 7dbb60590b6..5d4e457c5fa 100644 --- a/core/os/mutex.cpp +++ b/core/os/mutex.cpp @@ -40,7 +40,7 @@ void _global_unlock() { _global_mutex.unlock(); } -template class MutexImpl; -template class MutexImpl; -template class MutexLock>; -template class MutexLock>; +template class MutexImpl; +template class MutexImpl; +template class MutexLock>; +template class MutexLock>; diff --git a/core/os/mutex.h b/core/os/mutex.h index cee0f8af749..03af48ca7c2 100644 --- a/core/os/mutex.h +++ b/core/os/mutex.h @@ -34,7 +34,14 @@ #include "core/error/error_macros.h" #include "core/typedefs.h" +#ifdef MINGW_ENABLED +#define MINGW_STDTHREAD_REDUNDANCY_WARNING +#include "thirdparty/mingw-std-threads/mingw.mutex.h" +#define THREADING_NAMESPACE mingw_stdthread +#else #include +#define THREADING_NAMESPACE std +#endif template class MutexLock; @@ -73,9 +80,9 @@ template class SafeBinaryMutex { friend class MutexLock; - using StdMutexType = std::mutex; + using StdMutexType = THREADING_NAMESPACE::mutex; - mutable std::mutex mutex; + mutable THREADING_NAMESPACE::mutex mutex; static thread_local uint32_t count; public: @@ -115,7 +122,7 @@ template class MutexLock { friend class ConditionVariable; - std::unique_lock lock; + THREADING_NAMESPACE::unique_lock lock; public: _ALWAYS_INLINE_ explicit MutexLock(const MutexT &p_mutex) : @@ -128,7 +135,7 @@ template class MutexLock> { friend class ConditionVariable; - std::unique_lock lock; + THREADING_NAMESPACE::unique_lock lock; public: _ALWAYS_INLINE_ explicit MutexLock(const SafeBinaryMutex &p_mutex) : @@ -140,12 +147,12 @@ public: }; }; -using Mutex = MutexImpl; // Recursive, for general use -using BinaryMutex = MutexImpl; // Non-recursive, handle with care +using Mutex = MutexImpl; // Recursive, for general use +using BinaryMutex = MutexImpl; // Non-recursive, handle with care -extern template class MutexImpl; -extern template class MutexImpl; -extern template class MutexLock>; -extern template class MutexLock>; +extern template class MutexImpl; +extern template class MutexImpl; +extern template class MutexLock>; +extern template class MutexLock>; #endif // MUTEX_H diff --git a/core/os/os.cpp b/core/os/os.cpp index c7390f14ff4..f5d55ca107d 100644 --- a/core/os/os.cpp +++ b/core/os/os.cpp @@ -39,7 +39,15 @@ #include "core/version_generated.gen.h" #include + +#ifdef MINGW_ENABLED +#define MINGW_STDTHREAD_REDUNDANCY_WARNING +#include "thirdparty/mingw-std-threads/mingw.thread.h" +#define THREADING_NAMESPACE mingw_stdthread +#else #include +#define THREADING_NAMESPACE std +#endif OS *OS::singleton = nullptr; uint64_t OS::target_ticks = 0; @@ -359,7 +367,7 @@ String OS::get_unique_id() const { } int OS::get_processor_count() const { - return std::thread::hardware_concurrency(); + return THREADING_NAMESPACE::thread::hardware_concurrency(); } String OS::get_processor_name() const { diff --git a/core/os/rw_lock.h b/core/os/rw_lock.h index a232fcb1cec..e5bb6aec2be 100644 --- a/core/os/rw_lock.h +++ b/core/os/rw_lock.h @@ -33,10 +33,17 @@ #include "core/typedefs.h" +#ifdef MINGW_ENABLED +#define MINGW_STDTHREAD_REDUNDANCY_WARNING +#include "thirdparty/mingw-std-threads/mingw.shared_mutex.h" +#define THREADING_NAMESPACE mingw_stdthread +#else #include +#define THREADING_NAMESPACE std +#endif class RWLock { - mutable std::shared_timed_mutex mutex; + mutable THREADING_NAMESPACE::shared_timed_mutex mutex; public: // Lock the RWLock, block if locked by someone else. diff --git a/core/os/semaphore.h b/core/os/semaphore.h index 66dfb3ee021..8bb1529bbd4 100644 --- a/core/os/semaphore.h +++ b/core/os/semaphore.h @@ -37,13 +37,21 @@ #include "core/error/error_macros.h" #endif +#ifdef MINGW_ENABLED +#define MINGW_STDTHREAD_REDUNDANCY_WARNING +#include "thirdparty/mingw-std-threads/mingw.condition_variable.h" +#include "thirdparty/mingw-std-threads/mingw.mutex.h" +#define THREADING_NAMESPACE mingw_stdthread +#else #include #include +#define THREADING_NAMESPACE std +#endif class Semaphore { private: - mutable std::mutex mutex; - mutable std::condition_variable condition; + mutable THREADING_NAMESPACE::mutex mutex; + mutable THREADING_NAMESPACE::condition_variable condition; mutable uint32_t count = 0; // Initialized as locked. #ifdef DEBUG_ENABLED mutable uint32_t awaiters = 0; @@ -57,7 +65,7 @@ public: } _ALWAYS_INLINE_ void wait() const { - std::unique_lock lock(mutex); + THREADING_NAMESPACE::unique_lock lock(mutex); #ifdef DEBUG_ENABLED ++awaiters; #endif @@ -116,7 +124,7 @@ public: "A Semaphore object is being destroyed while one or more threads are still waiting on it.\n" "Please call post() on it as necessary to prevent such a situation and so ensure correct cleanup."); // And now, the hacky countermeasure (i.e., leak the condition variable). - new (&condition) std::condition_variable(); + new (&condition) THREADING_NAMESPACE::condition_variable(); } } #endif diff --git a/core/os/thread.cpp b/core/os/thread.cpp index 03e2c5409d9..2ba90ba42cd 100644 --- a/core/os/thread.cpp +++ b/core/os/thread.cpp @@ -69,8 +69,7 @@ void Thread::callback(ID p_caller_id, const Settings &p_settings, Callback p_cal Thread::ID Thread::start(Thread::Callback p_callback, void *p_user, const Settings &p_settings) { ERR_FAIL_COND_V_MSG(id != UNASSIGNED_ID, UNASSIGNED_ID, "A Thread object has been re-started without wait_to_finish() having been called on it."); id = id_counter.increment(); - std::thread new_thread(&Thread::callback, id, p_settings, p_callback, p_user); - thread.swap(new_thread); + thread = THREADING_NAMESPACE::thread(&Thread::callback, id, p_settings, p_callback, p_user); return id; } @@ -82,8 +81,7 @@ void Thread::wait_to_finish() { ERR_FAIL_COND_MSG(id == UNASSIGNED_ID, "Attempt of waiting to finish on a thread that was never started."); ERR_FAIL_COND_MSG(id == get_caller_id(), "Threads can't wait to finish on themselves, another thread must wait."); thread.join(); - std::thread empty_thread; - thread.swap(empty_thread); + thread = THREADING_NAMESPACE::thread(); id = UNASSIGNED_ID; } diff --git a/core/os/thread.h b/core/os/thread.h index 3e307adfffe..cc954678f92 100644 --- a/core/os/thread.h +++ b/core/os/thread.h @@ -42,7 +42,14 @@ #include "core/templates/safe_refcount.h" #include "core/typedefs.h" +#ifdef MINGW_ENABLED +#define MINGW_STDTHREAD_REDUNDANCY_WARNING +#include "thirdparty/mingw-std-threads/mingw.thread.h" +#define THREADING_NAMESPACE mingw_stdthread +#else #include +#define THREADING_NAMESPACE std +#endif class String; @@ -82,7 +89,7 @@ private: ID id = UNASSIGNED_ID; static SafeNumeric id_counter; static thread_local ID caller_id; - std::thread thread; + THREADING_NAMESPACE::thread thread; static void callback(ID p_caller_id, const Settings &p_settings, Thread::Callback p_callback, void *p_userdata); diff --git a/editor/dependency_editor.h b/editor/dependency_editor.h index 0979606716b..d43b12f1d25 100644 --- a/editor/dependency_editor.h +++ b/editor/dependency_editor.h @@ -68,6 +68,10 @@ public: DependencyEditor(); }; +#ifdef MINGW_ENABLED +#undef FILE_OPEN +#endif + class DependencyEditorOwners : public AcceptDialog { GDCLASS(DependencyEditorOwners, AcceptDialog); diff --git a/editor/plugins/script_editor_plugin.h b/editor/plugins/script_editor_plugin.h index f6c2eb727b4..4a814ea1bcc 100644 --- a/editor/plugins/script_editor_plugin.h +++ b/editor/plugins/script_editor_plugin.h @@ -204,6 +204,10 @@ class EditorScriptCodeCompletionCache; class FindInFilesDialog; class FindInFilesPanel; +#ifdef MINGW_ENABLED +#undef FILE_OPEN +#endif + class ScriptEditor : public PanelContainer { GDCLASS(ScriptEditor, PanelContainer); diff --git a/editor/plugins/shader_editor_plugin.h b/editor/plugins/shader_editor_plugin.h index 039afd61fec..073b0ee1927 100644 --- a/editor/plugins/shader_editor_plugin.h +++ b/editor/plugins/shader_editor_plugin.h @@ -42,6 +42,10 @@ class TextShaderEditor; class VisualShaderEditor; class WindowWrapper; +#ifdef MINGW_ENABLED +#undef FILE_OPEN +#endif + class ShaderEditorPlugin : public EditorPlugin { GDCLASS(ShaderEditorPlugin, EditorPlugin); diff --git a/editor/plugins/visual_shader_editor_plugin.h b/editor/plugins/visual_shader_editor_plugin.h index 07a73b4e51d..c4ee654b253 100644 --- a/editor/plugins/visual_shader_editor_plugin.h +++ b/editor/plugins/visual_shader_editor_plugin.h @@ -260,6 +260,10 @@ class VisualShaderEditor : public VBoxContainer { COLLAPSE_ALL }; +#ifdef MINGW_ENABLED +#undef DELETE +#endif + enum NodeMenuOptions { ADD, SEPARATOR, // ignore diff --git a/modules/gdscript/gdscript_tokenizer.h b/modules/gdscript/gdscript_tokenizer.h index 6dd8a986526..a64aaf6820e 100644 --- a/modules/gdscript/gdscript_tokenizer.h +++ b/modules/gdscript/gdscript_tokenizer.h @@ -37,6 +37,12 @@ #include "core/templates/vector.h" #include "core/variant/variant.h" +#ifdef MINGW_ENABLED +#undef CONST +#undef IN +#undef VOID +#endif + class GDScriptTokenizer { public: enum CursorPlace { diff --git a/modules/gltf/editor/editor_scene_importer_blend.cpp b/modules/gltf/editor/editor_scene_importer_blend.cpp index 2fad475e922..9587604e566 100644 --- a/modules/gltf/editor/editor_scene_importer_blend.cpp +++ b/modules/gltf/editor/editor_scene_importer_blend.cpp @@ -45,6 +45,11 @@ #include "main/main.h" #include "scene/gui/line_edit.h" +#ifdef MINGW_ENABLED +#define near +#define far +#endif + #ifdef WINDOWS_ENABLED #include #endif diff --git a/scene/3d/camera_3d.h b/scene/3d/camera_3d.h index aa302ded4a3..8de607806ea 100644 --- a/scene/3d/camera_3d.h +++ b/scene/3d/camera_3d.h @@ -36,6 +36,11 @@ #include "scene/resources/camera_attributes.h" #include "scene/resources/environment.h" +#ifdef MINGW_ENABLED +#undef near +#undef far +#endif + class Camera3D : public Node3D { GDCLASS(Camera3D, Node3D); diff --git a/scene/debugger/scene_debugger.cpp b/scene/debugger/scene_debugger.cpp index 79cd1056dde..5603b2dbe45 100644 --- a/scene/debugger/scene_debugger.cpp +++ b/scene/debugger/scene_debugger.cpp @@ -72,6 +72,11 @@ void SceneDebugger::deinitialize() { } } +#ifdef MINGW_ENABLED +#undef near +#undef far +#endif + #ifdef DEBUG_ENABLED Error SceneDebugger::parse_message(void *p_user, const String &p_msg, const Array &p_args, bool &r_captured) { SceneTree *scene_tree = SceneTree::get_singleton(); diff --git a/scene/resources/camera_attributes.cpp b/scene/resources/camera_attributes.cpp index 7c46729af3b..323241200ca 100644 --- a/scene/resources/camera_attributes.cpp +++ b/scene/resources/camera_attributes.cpp @@ -373,6 +373,11 @@ real_t CameraAttributesPhysical::get_fov() const { return frustum_fov; } +#ifdef MINGW_ENABLED +#undef near +#undef far +#endif + void CameraAttributesPhysical::_update_frustum() { //https://en.wikipedia.org/wiki/Circle_of_confusion#Circle_of_confusion_diameter_limit_based_on_d/1500 Vector2i sensor_size = Vector2i(36, 24); // Matches high-end DSLR, could be made variable if there is demand. diff --git a/servers/rendering/renderer_scene_render.cpp b/servers/rendering/renderer_scene_render.cpp index a389e3e7675..cd89bb314b9 100644 --- a/servers/rendering/renderer_scene_render.cpp +++ b/servers/rendering/renderer_scene_render.cpp @@ -47,6 +47,11 @@ void RendererSceneRender::CameraData::set_camera(const Transform3D p_transform, taa_jitter = p_taa_jitter; } +#ifdef MINGW_ENABLED +#undef near +#undef far +#endif + void RendererSceneRender::CameraData::set_multiview_camera(uint32_t p_view_count, const Transform3D *p_transforms, const Projection *p_projections, bool p_is_orthogonal, bool p_vaspect) { ERR_FAIL_COND_MSG(p_view_count != 2, "Incorrect view count for stereoscopic view"); diff --git a/thirdparty/README.md b/thirdparty/README.md index 4fcd85b36e5..0baa5f85bc0 100644 --- a/thirdparty/README.md +++ b/thirdparty/README.md @@ -515,6 +515,24 @@ error metrics instead of a combination of distance and attribute errors. Patches for both changes can be found in the `patches` directory. +## mingw-std-threads + +- Upstream: https://github.com/meganz/mingw-std-threads +- Version: git (c931bac289dd431f1dd30fc4a5d1a7be36668073, 2023) +- License: BSD-2-clause + +Files extracted from upstream repository: + +- `LICENSE` +- `mingw.condition_variable.h` +- `mingw.invoke.h` +- `mingw.mutex.h` +- `mingw.shared_mutex.h` +- `mingw.thread.h` + +Once copied, apply `no_except.patch` (needed because Godot is built without exceptions). + + ## minimp3 - Upstream: https://github.com/lieff/minimp3 diff --git a/thirdparty/mingw-std-threads/LICENSE b/thirdparty/mingw-std-threads/LICENSE new file mode 100644 index 00000000000..ac525cf2030 --- /dev/null +++ b/thirdparty/mingw-std-threads/LICENSE @@ -0,0 +1,24 @@ +Copyright (c) 2016, Mega Limited +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +* Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + +* Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + diff --git a/thirdparty/mingw-std-threads/mingw.condition_variable.h b/thirdparty/mingw-std-threads/mingw.condition_variable.h new file mode 100644 index 00000000000..f9e248c1541 --- /dev/null +++ b/thirdparty/mingw-std-threads/mingw.condition_variable.h @@ -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 + +#include +#include +#include + +#include // Detect Windows version. +#if (WINVER < _WIN32_WINNT_VISTA) +#include +#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 // No further granularity can be expected. +#else +#if (WINVER < _WIN32_WINNT_VISTA) +#include +#include // For CreateSemaphore +#include +#endif +#include +#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 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) + __builtin_trap(); + mWakeEvent = CreateEvent(NULL, FALSE, FALSE, NULL); + if (mWakeEvent == NULL) + { + CloseHandle(mSemaphore); + __builtin_trap(); + } + } + ~condition_variable_any() + { + CloseHandle(mWakeEvent); + CloseHandle(mSemaphore); + } +private: + template + bool wait_impl(M& lock, DWORD timeout) + { + { + lock_guard 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; + __builtin_trap(); + } + } +public: + template + void wait(M& lock) + { + wait_impl(lock, INFINITE); + } + template + void wait(M& lock, Predicate pred) + { + while(!pred()) + { + wait(lock); + }; + } + + void notify_all() noexcept + { + lock_guard 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 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 + cv_status wait_for(M& lock, + const std::chrono::duration& rel_time) + { + using namespace std::chrono; + auto timeout = duration_cast(rel_time).count(); + DWORD waittime = (timeout < INFINITE) ? ((timeout < 0) ? 0 : static_cast(timeout)) : (INFINITE - 1); + bool ret = wait_impl(lock, waittime) || (timeout >= INFINITE); + return ret?cv_status::no_timeout:cv_status::timeout; + } + + template + bool wait_for(M& lock, + const std::chrono::duration& rel_time, Predicate pred) + { + return wait_until(lock, std::chrono::steady_clock::now()+rel_time, pred); + } + template + cv_status wait_until (M& lock, + const std::chrono::time_point& abs_time) + { + return wait_for(lock, abs_time - Clock::now()); + } + template + bool wait_until (M& 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: 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 &lock) + { + base::wait(lock); + } + template + void wait(unique_lock& lock, Predicate pred) + { + base::wait(lock, pred); + } + template + cv_status wait_for(unique_lock& lock, const std::chrono::duration& rel_time) + { + return base::wait_for(lock, rel_time); + } + template + bool wait_for(unique_lock& lock, const std::chrono::duration& rel_time, Predicate pred) + { + return base::wait_for(lock, rel_time, pred); + } + template + cv_status wait_until (unique_lock& lock, const std::chrono::time_point& abs_time) + { + return base::wait_until(lock, abs_time); + } + template + bool wait_until (unique_lock& lock, const std::chrono::time_point& 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 + inline static void before_wait (MTX * pmutex) + { + pmutex->mOwnerThread.checkSetOwnerBeforeUnlock(); + } + template + 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 & lock, DWORD time) + { + using mutex_handle_type = typename xp::mutex::native_handle_type; + static_assert(std::is_same::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(*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 & lock, DWORD time) + { + windows7::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 (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 & 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; + 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 + 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); + } +// 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 & lock, DWORD time) + { + native_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) + { + native_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 (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 + 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; + } +}; +} // 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 diff --git a/thirdparty/mingw-std-threads/mingw.invoke.h b/thirdparty/mingw-std-threads/mingw.invoke.h new file mode 100644 index 00000000000..d5c9dd38bb1 --- /dev/null +++ b/thirdparty/mingw-std-threads/mingw.invoke.h @@ -0,0 +1,109 @@ +/// \file mingw.invoke.h +/// \brief Lightweight `invoke` implementation, for C++11 and C++14. +/// +/// (c) 2018-2019 by Nathaniel J. McClatchey, San Jose, CA, United States +/// \author Nathaniel J. McClatchey, PhD +/// +/// \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. + +#ifndef MINGW_INVOKE_H_ +#define MINGW_INVOKE_H_ + +#include // For std::result_of, etc. +#include // For std::forward +#include // For std::reference_wrapper + +namespace mingw_stdthread +{ +namespace detail +{ +// For compatibility, implement std::invoke for C++11 and C++14 +#if __cplusplus < 201703L + template + struct Invoker + { + template + inline static typename std::result_of::type invoke (F&& f, Args&&... args) + { + return std::forward(f)(std::forward(args)...); + } + }; + template + struct InvokerHelper; + + template<> + struct InvokerHelper + { + template + inline static auto get (T1&& t1) -> decltype(*std::forward(t1)) + { + return *std::forward(t1); + } + + template + inline static auto get (const std::reference_wrapper& t1) -> decltype(t1.get()) + { + return t1.get(); + } + }; + + template<> + struct InvokerHelper + { + template + inline static auto get (T1&& t1) -> decltype(std::forward(t1)) + { + return std::forward(t1); + } + }; + + template<> + struct Invoker + { + template + inline static auto invoke (F T::* f, T1&& t1, Args&&... args) ->\ + decltype((InvokerHelper::type>::value>::get(std::forward(t1)).*f)(std::forward(args)...)) + { + return (InvokerHelper::type>::value>::get(std::forward(t1)).*f)(std::forward(args)...); + } + }; + + template<> + struct Invoker + { + template + inline static auto invoke (F T::* f, T1&& t1, Args&&... args) ->\ + decltype(InvokerHelper::type>::value>::get(t1).*f) + { + return InvokerHelper::type>::value>::get(t1).*f; + } + }; + + template + struct InvokeResult + { + typedef Invoker::type>::value, + std::is_member_object_pointer::type>::value && + (sizeof...(Args) == 1)> invoker; + inline static auto invoke (F&& f, Args&&... args) -> decltype(invoker::invoke(std::forward(f), std::forward(args)...)) + { + return invoker::invoke(std::forward(f), std::forward(args)...); + } + }; + + template + auto invoke (F&& f, Args&&... args) -> decltype(InvokeResult::invoke(std::forward(f), std::forward(args)...)) + { + return InvokeResult::invoke(std::forward(f), std::forward(args)...); + } +#else + using std::invoke; +#endif +} // Namespace "detail" +} // Namespace "mingw_stdthread" + +#endif diff --git a/thirdparty/mingw-std-threads/mingw.mutex.h b/thirdparty/mingw-std-threads/mingw.mutex.h new file mode 100644 index 00000000000..73698d13cbf --- /dev/null +++ b/thirdparty/mingw-std-threads/mingw.mutex.h @@ -0,0 +1,500 @@ +/** +* @file mingw.mutex.h +* @brief std::mutex et al 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 WIN32STDMUTEX_H +#define WIN32STDMUTEX_H + +#if !defined(__cplusplus) || (__cplusplus < 201103L) +#error A C++11 compiler is required! +#endif +// Recursion checks on non-recursive locks have some performance penalty, and +// the C++ standard does not mandate them. The user might want to explicitly +// enable or disable such checks. If the user has no preference, enable such +// checks in debug builds, but not in release builds. +#ifdef STDMUTEX_RECURSION_CHECKS +#elif defined(NDEBUG) +#define STDMUTEX_RECURSION_CHECKS 0 +#else +#define STDMUTEX_RECURSION_CHECKS 1 +#endif + +#include +#include +#include +#include //need for call_once() + +#if STDMUTEX_RECURSION_CHECKS || !defined(NDEBUG) +#include +#endif + +#include // 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 // No further granularity can be expected. +#else +#if STDMUTEX_RECURSION_CHECKS +#include // For GetCurrentThreadId +#endif +#include // For InitializeCriticalSection, etc. +#include // For GetLastError +#include +#endif + +// Need for the implementation of invoke +#include "mingw.invoke.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 +{ +// The _NonRecursive class has mechanisms that do not play nice with direct +// manipulation of the native handle. This forward declaration is part of +// a friend class declaration. +#if STDMUTEX_RECURSION_CHECKS +namespace vista +{ +class condition_variable; +} +#endif +// To make this namespace equivalent to the thread-related subset of std, +// pull in the classes and class templates supplied by std but not by this +// implementation. +using std::lock_guard; +using std::unique_lock; +using std::adopt_lock_t; +using std::defer_lock_t; +using std::try_to_lock_t; +using std::adopt_lock; +using std::defer_lock; +using std::try_to_lock; + +class recursive_mutex +{ + CRITICAL_SECTION mHandle; +public: + typedef LPCRITICAL_SECTION native_handle_type; + native_handle_type native_handle() {return &mHandle;} + recursive_mutex() noexcept : mHandle() + { + InitializeCriticalSection(&mHandle); + } + recursive_mutex (const recursive_mutex&) = delete; + recursive_mutex& operator=(const recursive_mutex&) = delete; + ~recursive_mutex() noexcept + { + DeleteCriticalSection(&mHandle); + } + void lock() + { + EnterCriticalSection(&mHandle); + } + void unlock() + { + LeaveCriticalSection(&mHandle); + } + bool try_lock() + { + return (TryEnterCriticalSection(&mHandle)!=0); + } +}; + +#if STDMUTEX_RECURSION_CHECKS +struct _OwnerThread +{ +// If this is to be read before locking, then the owner-thread variable must +// be atomic to prevent a torn read from spuriously causing errors. + std::atomic mOwnerThread; + constexpr _OwnerThread () noexcept : mOwnerThread(0) {} + static void on_deadlock (void) + { + using namespace std; + fprintf(stderr, "FATAL: Recursive locking of non-recursive mutex\ + detected. Throwing system exception\n"); + fflush(stderr); + __builtin_trap(); + } + DWORD checkOwnerBeforeLock() const + { + DWORD self = GetCurrentThreadId(); + if (mOwnerThread.load(std::memory_order_relaxed) == self) + on_deadlock(); + return self; + } + void setOwnerAfterLock(DWORD id) + { + mOwnerThread.store(id, std::memory_order_relaxed); + } + void checkSetOwnerBeforeUnlock() + { + DWORD self = GetCurrentThreadId(); + if (mOwnerThread.load(std::memory_order_relaxed) != self) + on_deadlock(); + mOwnerThread.store(0, std::memory_order_relaxed); + } +}; +#endif + +// Though the Slim Reader-Writer (SRW) locks used here are not complete until +// Windows 7, implementing partial functionality in Vista will simplify the +// interaction with condition variables. + +//Define SRWLOCK_INIT. + +#if !defined(SRWLOCK_INIT) +#pragma message "SRWLOCK_INIT macro is not defined. Defining automatically." +#define SRWLOCK_INIT {0} +#endif + +#if defined(_WIN32) && (WINVER >= _WIN32_WINNT_VISTA) +namespace windows7 +{ +class mutex +{ + SRWLOCK mHandle; +// Track locking thread for error checking. +#if STDMUTEX_RECURSION_CHECKS + friend class vista::condition_variable; + _OwnerThread mOwnerThread {}; +#endif +public: + typedef PSRWLOCK native_handle_type; +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wzero-as-null-pointer-constant" + constexpr mutex () noexcept : mHandle(SRWLOCK_INIT) { } +#pragma GCC diagnostic pop + mutex (const mutex&) = delete; + mutex & operator= (const mutex&) = delete; + void lock (void) + { +// Note: Undefined behavior if called recursively. +#if STDMUTEX_RECURSION_CHECKS + DWORD self = mOwnerThread.checkOwnerBeforeLock(); +#endif + AcquireSRWLockExclusive(&mHandle); +#if STDMUTEX_RECURSION_CHECKS + mOwnerThread.setOwnerAfterLock(self); +#endif + } + void unlock (void) + { +#if STDMUTEX_RECURSION_CHECKS + mOwnerThread.checkSetOwnerBeforeUnlock(); +#endif + ReleaseSRWLockExclusive(&mHandle); + } +// TryAcquireSRW functions are a Windows 7 feature. +#if (WINVER >= _WIN32_WINNT_WIN7) + bool try_lock (void) + { +#if STDMUTEX_RECURSION_CHECKS + DWORD self = mOwnerThread.checkOwnerBeforeLock(); +#endif + BOOL ret = TryAcquireSRWLockExclusive(&mHandle); +#if STDMUTEX_RECURSION_CHECKS + if (ret) + mOwnerThread.setOwnerAfterLock(self); +#endif + return ret; + } +#endif + native_handle_type native_handle (void) + { + return &mHandle; + } +}; +} // Namespace windows7 +#endif // Compiling for Vista +namespace xp +{ +class mutex +{ + CRITICAL_SECTION mHandle; + std::atomic_uchar mState; +// Track locking thread for error checking. +#if STDMUTEX_RECURSION_CHECKS + friend class vista::condition_variable; + _OwnerThread mOwnerThread {}; +#endif +public: + typedef PCRITICAL_SECTION native_handle_type; + constexpr mutex () noexcept : mHandle(), mState(2) { } + mutex (const mutex&) = delete; + mutex & operator= (const mutex&) = delete; + ~mutex() noexcept + { +// Undefined behavior if the mutex is held (locked) by any thread. +// Undefined behavior if a thread terminates while holding ownership of the +// mutex. + DeleteCriticalSection(&mHandle); + } + void lock (void) + { + unsigned char state = mState.load(std::memory_order_acquire); + while (state) { + if ((state == 2) && mState.compare_exchange_weak(state, 1, std::memory_order_acquire)) + { + InitializeCriticalSection(&mHandle); + mState.store(0, std::memory_order_release); + break; + } + if (state == 1) + { + Sleep(0); + state = mState.load(std::memory_order_acquire); + } + } +#if STDMUTEX_RECURSION_CHECKS + DWORD self = mOwnerThread.checkOwnerBeforeLock(); +#endif + EnterCriticalSection(&mHandle); +#if STDMUTEX_RECURSION_CHECKS + mOwnerThread.setOwnerAfterLock(self); +#endif + } + void unlock (void) + { +#if STDMUTEX_RECURSION_CHECKS + mOwnerThread.checkSetOwnerBeforeUnlock(); +#endif + LeaveCriticalSection(&mHandle); + } + bool try_lock (void) + { + unsigned char state = mState.load(std::memory_order_acquire); + if ((state == 2) && mState.compare_exchange_strong(state, 1, std::memory_order_acquire)) + { + InitializeCriticalSection(&mHandle); + mState.store(0, std::memory_order_release); + } + if (state == 1) + return false; +#if STDMUTEX_RECURSION_CHECKS + DWORD self = mOwnerThread.checkOwnerBeforeLock(); +#endif + BOOL ret = TryEnterCriticalSection(&mHandle); +#if STDMUTEX_RECURSION_CHECKS + if (ret) + mOwnerThread.setOwnerAfterLock(self); +#endif + return ret; + } + native_handle_type native_handle (void) + { + return &mHandle; + } +}; +} // Namespace "xp" +#if (WINVER >= _WIN32_WINNT_WIN7) +using windows7::mutex; +#else +using xp::mutex; +#endif + +class recursive_timed_mutex +{ + static constexpr DWORD kWaitAbandoned = 0x00000080l; + static constexpr DWORD kWaitObject0 = 0x00000000l; + static constexpr DWORD kInfinite = 0xffffffffl; + inline bool try_lock_internal (DWORD ms) noexcept + { + DWORD ret = WaitForSingleObject(mHandle, ms); +#ifndef NDEBUG + if (ret == kWaitAbandoned) + { + using namespace std; + fprintf(stderr, "FATAL: Thread terminated while holding a mutex."); + terminate(); + } +#endif + return (ret == kWaitObject0) || (ret == kWaitAbandoned); + } +protected: + HANDLE mHandle; +// Track locking thread for error checking of non-recursive timed_mutex. For +// standard compliance, this must be defined in same class and at the same +// access-control level as every other variable in the timed_mutex. +#if STDMUTEX_RECURSION_CHECKS + friend class vista::condition_variable; + _OwnerThread mOwnerThread {}; +#endif +public: + typedef HANDLE native_handle_type; + native_handle_type native_handle() const {return mHandle;} + recursive_timed_mutex(const recursive_timed_mutex&) = delete; + recursive_timed_mutex& operator=(const recursive_timed_mutex&) = delete; + recursive_timed_mutex(): mHandle(CreateMutex(NULL, FALSE, NULL)) {} + ~recursive_timed_mutex() + { + CloseHandle(mHandle); + } + void lock() + { + DWORD ret = WaitForSingleObject(mHandle, kInfinite); +// If (ret == WAIT_ABANDONED), then the thread that held ownership was +// terminated. Behavior is undefined, but Windows will pass ownership to this +// thread. +#ifndef NDEBUG + if (ret == kWaitAbandoned) + { + using namespace std; + fprintf(stderr, "FATAL: Thread terminated while holding a mutex."); + terminate(); + } +#endif + if ((ret != kWaitObject0) && (ret != kWaitAbandoned)) + { + __builtin_trap(); + } + } + void unlock() + { + if (!ReleaseMutex(mHandle)) + __builtin_trap(); + } + bool try_lock() + { + return try_lock_internal(0); + } + template + bool try_lock_for(const std::chrono::duration& dur) + { + using namespace std::chrono; + auto timeout = duration_cast(dur).count(); + while (timeout > 0) + { + constexpr auto kMaxStep = static_cast(kInfinite-1); + auto step = (timeout < kMaxStep) ? timeout : kMaxStep; + if (try_lock_internal(static_cast(step))) + return true; + timeout -= step; + } + return false; + } + template + bool try_lock_until(const std::chrono::time_point& timeout_time) + { + return try_lock_for(timeout_time - Clock::now()); + } +}; + +// Override if, and only if, it is necessary for error-checking. +#if STDMUTEX_RECURSION_CHECKS +class timed_mutex: recursive_timed_mutex +{ +public: + timed_mutex() = default; + timed_mutex(const timed_mutex&) = delete; + timed_mutex& operator=(const timed_mutex&) = delete; + void lock() + { + DWORD self = mOwnerThread.checkOwnerBeforeLock(); + recursive_timed_mutex::lock(); + mOwnerThread.setOwnerAfterLock(self); + } + void unlock() + { + mOwnerThread.checkSetOwnerBeforeUnlock(); + recursive_timed_mutex::unlock(); + } + template + bool try_lock_for(const std::chrono::duration& dur) + { + DWORD self = mOwnerThread.checkOwnerBeforeLock(); + bool ret = recursive_timed_mutex::try_lock_for(dur); + if (ret) + mOwnerThread.setOwnerAfterLock(self); + return ret; + } + template + bool try_lock_until(const std::chrono::time_point& timeout_time) + { + return try_lock_for(timeout_time - Clock::now()); + } + bool try_lock () + { + return try_lock_for(std::chrono::milliseconds(0)); + } +}; +#else +typedef recursive_timed_mutex timed_mutex; +#endif + +class once_flag +{ +// 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 once_flag. +#if (_WIN32_WINNT == _WIN32_WINNT_VISTA) + windows7::mutex mMutex; +#else + mutex mMutex; +#endif + std::atomic_bool mHasRun; + once_flag(const once_flag&) = delete; + once_flag& operator=(const once_flag&) = delete; + template + friend void call_once(once_flag& once, Callable&& f, Args&&... args); +public: + constexpr once_flag() noexcept: mMutex(), mHasRun(false) {} +}; + +template +void call_once(once_flag& flag, Callable&& func, Args&&... args) +{ + if (flag.mHasRun.load(std::memory_order_acquire)) + return; + lock_guard lock(flag.mMutex); + if (flag.mHasRun.load(std::memory_order_relaxed)) + return; + detail::invoke(std::forward(func),std::forward(args)...); + flag.mHasRun.store(true, std::memory_order_release); +} +} // 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::recursive_mutex; +using mingw_stdthread::mutex; +using mingw_stdthread::recursive_timed_mutex; +using mingw_stdthread::timed_mutex; +using mingw_stdthread::once_flag; +using mingw_stdthread::call_once; +#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 // WIN32STDMUTEX_H diff --git a/thirdparty/mingw-std-threads/mingw.shared_mutex.h b/thirdparty/mingw-std-threads/mingw.shared_mutex.h new file mode 100644 index 00000000000..5375b0fbd15 --- /dev/null +++ b/thirdparty/mingw-std-threads/mingw.shared_mutex.h @@ -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 +/// 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 +// For descriptive errors. +#include +// Implementing a shared_mutex without OS support will require atomic read- +// modify-write capacity. +#include +// For timing in shared_lock and shared_timed_mutex. +#include +#include + +// 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 +#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 // 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 // No further granularity can be expected. +#else +#include +#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 mCounter {0}; + static constexpr counter_type kWriteBit = 1 << (std::numeric_limits::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(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(~kWriteBit); + if (expected + 1 == kWriteBit) + return false; + else + return mCounter.compare_exchange_strong( expected, + static_cast(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(~kWriteBit))) + __builtin_trap(); +#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) + __builtin_trap(); +#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& 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& 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& 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& 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 shared_lock +{ + Mutex * mMutex; + bool mOwns; +// Reduce code redundancy + void verify_lockable (void) + { + using namespace std; + if (mMutex == nullptr) + __builtin_trap(); + if (mOwns) + __builtin_trap(); + } +public: + typedef Mutex mutex_type; + + shared_lock (void) noexcept + : mMutex(nullptr), mOwns(false) + { + } + + shared_lock (shared_lock && 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& 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& timeout_time ) + : mMutex(&m), mOwns(m.try_lock_shared_until(timeout_time)) + { + } + + shared_lock& operator= (shared_lock && 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 &) = delete; + shared_lock& operator= (const shared_lock &) = 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& 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& rel_time) + { + return try_lock_until(std::chrono::steady_clock::now() + rel_time); + } + + void unlock (void) + { + using namespace std; + if (!mOwns) + __builtin_trap(); + mMutex->unlock_shared(); + mOwns = false; + } + +// Modifiers + void swap (shared_lock & 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& lhs, shared_lock& 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_ diff --git a/thirdparty/mingw-std-threads/mingw.thread.h b/thirdparty/mingw-std-threads/mingw.thread.h new file mode 100644 index 00000000000..7ca09e25f58 --- /dev/null +++ b/thirdparty/mingw-std-threads/mingw.thread.h @@ -0,0 +1,360 @@ +/** +* @file mingw.thread.h +* @brief std::thread 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 WIN32STDTHREAD_H +#define WIN32STDTHREAD_H + +#if !defined(__cplusplus) || (__cplusplus < 201103L) +#error A C++11 compiler is required! +#endif + +// Use the standard classes for std::, if available. +#include + +#include // For std::size_t +#include // Detect error type. +#include // For std::terminate +#include // For std::system_error +#include // For std::hash +#include // For std::tuple +#include // For sleep timing. +#include // For std::unique_ptr +#include // Stream output for thread ids. +#include // For std::swap, std::forward + +#include "mingw.invoke.h" + +#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 // No further granularity can be expected. +#else +#include // For WaitForSingleObject +#include // For CloseHandle, etc. +#include // For GetNativeSystemInfo +#include // For GetCurrentThreadId +#endif +#include // For _beginthreadex + +#ifndef NDEBUG +#include +#endif + +#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 + +// Instead of INVALID_HANDLE_VALUE, _beginthreadex returns 0. +namespace mingw_stdthread +{ +namespace detail +{ + template + struct IntSeq {}; + + template + struct GenIntSeq : GenIntSeq { }; + + template + struct GenIntSeq<0, S...> { typedef IntSeq type; }; + +// Use a template specialization to avoid relying on compiler optimization +// when determining the parameter integer sequence. + template + class ThreadFuncCall; +// We can't define the Call struct in the function - the standard forbids template methods in that case + template + class ThreadFuncCall, Args...> + { + static_assert(sizeof...(S) == sizeof...(Args), "Args must match."); + using Tuple = std::tuple::type...>; + typename std::decay::type mFunc; + Tuple mArgs; + + public: + ThreadFuncCall(Func&& aFunc, Args&&... aArgs) + : mFunc(std::forward(aFunc)), + mArgs(std::forward(aArgs)...) + { + } + + void callFunc() + { + detail::invoke(std::move(mFunc), std::move(std::get(mArgs)) ...); + } + }; + +// Allow construction of threads without exposing implementation. + class ThreadIdTool; +} // Namespace "detail" + +class thread +{ +public: + class id + { + DWORD mId = 0; + friend class thread; + friend class std::hash; + friend class detail::ThreadIdTool; + explicit id(DWORD aId) noexcept : mId(aId){} + public: + id (void) noexcept = default; + friend bool operator==(id x, id y) noexcept {return x.mId == y.mId; } + friend bool operator!=(id x, id y) noexcept {return x.mId != y.mId; } + friend bool operator< (id x, id y) noexcept {return x.mId < y.mId; } + friend bool operator<=(id x, id y) noexcept {return x.mId <= y.mId; } + friend bool operator> (id x, id y) noexcept {return x.mId > y.mId; } + friend bool operator>=(id x, id y) noexcept {return x.mId >= y.mId; } + + template + friend std::basic_ostream<_CharT, _Traits>& + operator<<(std::basic_ostream<_CharT, _Traits>& __out, id __id) + { + if (__id.mId == 0) + { + return __out << "(invalid std::thread::id)"; + } + else + { + return __out << __id.mId; + } + } + }; +private: + static constexpr HANDLE kInvalidHandle = nullptr; + static constexpr DWORD kInfinite = 0xffffffffl; + HANDLE mHandle; + id mThreadId; + + template + static unsigned __stdcall threadfunc(void* arg) + { + std::unique_ptr call(static_cast(arg)); + call->callFunc(); + return 0; + } + + static unsigned int _hardware_concurrency_helper() noexcept + { + SYSTEM_INFO sysinfo; +// This is one of the few functions used by the library which has a nearly- +// equivalent function defined in earlier versions of Windows. Include the +// workaround, just as a reminder that it does exist. +#if defined(_WIN32_WINNT) && (_WIN32_WINNT >= 0x0501) + ::GetNativeSystemInfo(&sysinfo); +#else + ::GetSystemInfo(&sysinfo); +#endif + return sysinfo.dwNumberOfProcessors; + } +public: + typedef HANDLE native_handle_type; + id get_id() const noexcept {return mThreadId;} + native_handle_type native_handle() const {return mHandle;} + thread(): mHandle(kInvalidHandle), mThreadId(){} + + thread(thread&& other) + :mHandle(other.mHandle), mThreadId(other.mThreadId) + { + other.mHandle = kInvalidHandle; + other.mThreadId = id{}; + } + + thread(const thread &other)=delete; + + template + explicit thread(Func&& func, Args&&... args) : mHandle(), mThreadId() + { + using ArgSequence = typename detail::GenIntSeq::type; + using Call = detail::ThreadFuncCall; + auto call = new Call( + std::forward(func), std::forward(args)...); + unsigned id_receiver; + auto int_handle = _beginthreadex(NULL, 0, threadfunc, + static_cast(call), 0, &id_receiver); + if (int_handle == 0) + { + mHandle = kInvalidHandle; + int errnum = errno; + delete call; +// Note: Should only throw EINVAL, EAGAIN, EACCES + __builtin_trap(); + } else { + mThreadId.mId = id_receiver; + mHandle = reinterpret_cast(int_handle); + } + } + + bool joinable() const {return mHandle != kInvalidHandle;} + +// Note: Due to lack of synchronization, this function has a race condition +// if called concurrently, which leads to undefined behavior. The same applies +// to all other member functions of this class, but this one is mentioned +// explicitly. + void join() + { + using namespace std; + if (get_id() == id(GetCurrentThreadId())) + __builtin_trap(); + if (mHandle == kInvalidHandle) + __builtin_trap(); + if (!joinable()) + __builtin_trap(); + WaitForSingleObject(mHandle, kInfinite); + CloseHandle(mHandle); + mHandle = kInvalidHandle; + mThreadId = id{}; + } + + ~thread() + { + if (joinable()) + { +#ifndef NDEBUG + std::printf("Error: Must join() or detach() a thread before \ +destroying it.\n"); +#endif + std::terminate(); + } + } + thread& operator=(const thread&) = delete; + thread& operator=(thread&& other) noexcept + { + if (joinable()) + { +#ifndef NDEBUG + std::printf("Error: Must join() or detach() a thread before \ +moving another thread to it.\n"); +#endif + std::terminate(); + } + swap(std::forward(other)); + return *this; + } + void swap(thread&& other) noexcept + { + std::swap(mHandle, other.mHandle); + std::swap(mThreadId.mId, other.mThreadId.mId); + } + + static unsigned int hardware_concurrency() noexcept + { + static unsigned int cached = _hardware_concurrency_helper(); + return cached; + } + + void detach() + { + if (!joinable()) + { + using namespace std; + __builtin_trap(); + } + if (mHandle != kInvalidHandle) + { + CloseHandle(mHandle); + mHandle = kInvalidHandle; + } + mThreadId = id{}; + } +}; + +namespace detail +{ + class ThreadIdTool + { + public: + static thread::id make_id (DWORD base_id) noexcept + { + return thread::id(base_id); + } + }; +} // Namespace "detail" + +namespace this_thread +{ + inline thread::id get_id() noexcept + { + return detail::ThreadIdTool::make_id(GetCurrentThreadId()); + } + inline void yield() noexcept {Sleep(0);} + template< class Rep, class Period > + void sleep_for( const std::chrono::duration& sleep_duration) + { + static constexpr DWORD kInfinite = 0xffffffffl; + using namespace std::chrono; + using rep = milliseconds::rep; + rep ms = duration_cast(sleep_duration).count(); + while (ms > 0) + { + constexpr rep kMaxRep = static_cast(kInfinite - 1); + auto sleepTime = (ms < kMaxRep) ? ms : kMaxRep; + Sleep(static_cast(sleepTime)); + ms -= sleepTime; + } + } + template + void sleep_until(const std::chrono::time_point& sleep_time) + { + sleep_for(sleep_time-Clock::now()); + } +} +} // 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 defined(__MINGW32__ ) && !defined(_GLIBCXX_HAS_GTHREADS) +using mingw_stdthread::thread; +// Remove ambiguity immediately, to avoid problems arising from the above. +//using std::thread; +namespace this_thread +{ +using namespace mingw_stdthread::this_thread; +} +#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 + +// Specialize hash for this implementation's thread::id, even if the +// std::thread::id already has a hash. +template<> +struct hash +{ + typedef mingw_stdthread::thread::id argument_type; + typedef size_t result_type; + size_t operator() (const argument_type & i) const noexcept + { + return i.mId; + } +}; +} +#endif // WIN32STDTHREAD_H diff --git a/thirdparty/mingw-std-threads/no_except.patch b/thirdparty/mingw-std-threads/no_except.patch new file mode 100644 index 00000000000..6151103a8a4 --- /dev/null +++ b/thirdparty/mingw-std-threads/no_except.patch @@ -0,0 +1,137 @@ +diff --git a/thirdparty/mingw-std-threads/mingw.condition_variable.h b/thirdparty/mingw-std-threads/mingw.condition_variable.h +index 50c5ebd6df..f9e248c154 100644 +--- a/thirdparty/mingw-std-threads/mingw.condition_variable.h ++++ b/thirdparty/mingw-std-threads/mingw.condition_variable.h +@@ -87,12 +87,12 @@ public: + : mSemaphore(CreateSemaphoreA(NULL, 0, 0xFFFF, NULL)) + { + if (mSemaphore == NULL) +- throw std::system_error(GetLastError(), std::generic_category()); ++ __builtin_trap(); + mWakeEvent = CreateEvent(NULL, FALSE, FALSE, NULL); + if (mWakeEvent == NULL) + { + CloseHandle(mSemaphore); +- throw std::system_error(GetLastError(), std::generic_category()); ++ __builtin_trap(); + } + } + ~condition_variable_any() +@@ -132,7 +132,7 @@ private: + else + { + using namespace std; +- throw system_error(make_error_code(errc::protocol_error)); ++ __builtin_trap(); + } + } + public: +diff --git a/thirdparty/mingw-std-threads/mingw.mutex.h b/thirdparty/mingw-std-threads/mingw.mutex.h +index 03efa13f8b..73698d13cb 100644 +--- a/thirdparty/mingw-std-threads/mingw.mutex.h ++++ b/thirdparty/mingw-std-threads/mingw.mutex.h +@@ -132,7 +132,7 @@ struct _OwnerThread + fprintf(stderr, "FATAL: Recursive locking of non-recursive mutex\ + detected. Throwing system exception\n"); + fflush(stderr); +- throw system_error(make_error_code(errc::resource_deadlock_would_occur)); ++ __builtin_trap(); + } + DWORD checkOwnerBeforeLock() const + { +@@ -364,13 +364,13 @@ public: + #endif + if ((ret != kWaitObject0) && (ret != kWaitAbandoned)) + { +- throw std::system_error(GetLastError(), std::system_category()); ++ __builtin_trap(); + } + } + void unlock() + { + if (!ReleaseMutex(mHandle)) +- throw std::system_error(GetLastError(), std::system_category()); ++ __builtin_trap(); + } + bool try_lock() + { +diff --git a/thirdparty/mingw-std-threads/mingw.shared_mutex.h b/thirdparty/mingw-std-threads/mingw.shared_mutex.h +index ff1ac65135..5375b0fbd1 100644 +--- a/thirdparty/mingw-std-threads/mingw.shared_mutex.h ++++ b/thirdparty/mingw-std-threads/mingw.shared_mutex.h +@@ -134,7 +134,7 @@ public: + using namespace std; + #ifndef NDEBUG + if (!(mCounter.fetch_sub(1, memory_order_release) & static_cast(~kWriteBit))) +- throw system_error(make_error_code(errc::operation_not_permitted)); ++ __builtin_trap(); + #else + mCounter.fetch_sub(1, memory_order_release); + #endif +@@ -187,7 +187,7 @@ public: + using namespace std; + #ifndef NDEBUG + if (mCounter.load(memory_order_relaxed) != kWriteBit) +- throw system_error(make_error_code(errc::operation_not_permitted)); ++ __builtin_trap(); + #endif + mCounter.store(0, memory_order_release); + } +@@ -317,9 +317,9 @@ class shared_lock + { + using namespace std; + if (mMutex == nullptr) +- throw system_error(make_error_code(errc::operation_not_permitted)); ++ __builtin_trap(); + if (mOwns) +- throw system_error(make_error_code(errc::resource_deadlock_would_occur)); ++ __builtin_trap(); + } + public: + typedef Mutex mutex_type; +@@ -432,7 +432,7 @@ public: + { + using namespace std; + if (!mOwns) +- throw system_error(make_error_code(errc::operation_not_permitted)); ++ __builtin_trap(); + mMutex->unlock_shared(); + mOwns = false; + } +diff --git a/thirdparty/mingw-std-threads/mingw.thread.h b/thirdparty/mingw-std-threads/mingw.thread.h +index bcdd1a36a8..7ca09e25f5 100644 +--- a/thirdparty/mingw-std-threads/mingw.thread.h ++++ b/thirdparty/mingw-std-threads/mingw.thread.h +@@ -196,7 +196,7 @@ public: + int errnum = errno; + delete call; + // Note: Should only throw EINVAL, EAGAIN, EACCES +- throw std::system_error(errnum, std::generic_category()); ++ __builtin_trap(); + } else { + mThreadId.mId = id_receiver; + mHandle = reinterpret_cast(int_handle); +@@ -213,11 +213,11 @@ public: + { + using namespace std; + if (get_id() == id(GetCurrentThreadId())) +- throw system_error(make_error_code(errc::resource_deadlock_would_occur)); ++ __builtin_trap(); + if (mHandle == kInvalidHandle) +- throw system_error(make_error_code(errc::no_such_process)); ++ __builtin_trap(); + if (!joinable()) +- throw system_error(make_error_code(errc::invalid_argument)); ++ __builtin_trap(); + WaitForSingleObject(mHandle, kInfinite); + CloseHandle(mHandle); + mHandle = kInvalidHandle; +@@ -266,7 +266,7 @@ moving another thread to it.\n"); + if (!joinable()) + { + using namespace std; +- throw system_error(make_error_code(errc::invalid_argument)); ++ __builtin_trap(); + } + if (mHandle != kInvalidHandle) + {