From 8725494966f91af42c77d2f81d2c8a7fe1864316 Mon Sep 17 00:00:00 2001 From: Marco Costalba Date: Tue, 10 Mar 2015 12:42:40 +0100 Subject: [PATCH] Add thread_win32.h header Workaround slow std::thread implementation in mingw and gcc for Windows with our own old low level thread functions. No functional change. --- src/misc.cpp | 2 +- src/search.cpp | 2 +- src/thread.cpp | 10 ++--- src/thread.h | 7 ++-- src/thread_win32.h | 100 +++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 111 insertions(+), 10 deletions(-) create mode 100644 src/thread_win32.h diff --git a/src/misc.cpp b/src/misc.cpp index f09694cc..7df4287a 100644 --- a/src/misc.cpp +++ b/src/misc.cpp @@ -146,7 +146,7 @@ void dbg_print() { std::ostream& operator<<(std::ostream& os, SyncCout sc) { - static std::mutex m; + static Mutex m; if (sc == IO_LOCK) m.lock(); diff --git a/src/search.cpp b/src/search.cpp index 681acb81..3b35e3a0 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1636,7 +1636,7 @@ void Thread::idle_loop() { } // Avoid races with notify_one() fired from last slave of the split point - std::unique_lock lk(mutex); + std::unique_lock lk(mutex); // If we are master and all slaves have finished then exit idle_loop if (this_sp && this_sp->slavesMask.none()) diff --git a/src/thread.cpp b/src/thread.cpp index ff52576b..6ddbf3f2 100644 --- a/src/thread.cpp +++ b/src/thread.cpp @@ -61,7 +61,7 @@ namespace { void ThreadBase::notify_one() { - std::unique_lock(this->mutex); + std::unique_lock(this->mutex); sleepCondition.notify_one(); } @@ -70,7 +70,7 @@ void ThreadBase::notify_one() { void ThreadBase::wait_for(volatile const bool& condition) { - std::unique_lock lk(mutex); + std::unique_lock lk(mutex); sleepCondition.wait(lk, [&]{ return condition; }); } @@ -224,7 +224,7 @@ void TimerThread::idle_loop() { while (!exit) { - std::unique_lock lk(mutex); + std::unique_lock lk(mutex); if (!exit) sleepCondition.wait_for(lk, std::chrono::milliseconds(run ? Resolution : INT_MAX)); @@ -244,7 +244,7 @@ void MainThread::idle_loop() { while (!exit) { - std::unique_lock lk(mutex); + std::unique_lock lk(mutex); thinking = false; @@ -340,7 +340,7 @@ Thread* ThreadPool::available_slave(const SplitPoint* sp) const { void ThreadPool::wait_for_think_finished() { - std::unique_lock lk(main()->mutex); + std::unique_lock lk(main()->mutex); sleepCondition.wait(lk, [&]{ return !main()->thinking; }); } diff --git a/src/thread.h b/src/thread.h index b6809f4e..b4aad5cb 100644 --- a/src/thread.h +++ b/src/thread.h @@ -32,6 +32,7 @@ #include "pawns.h" #include "position.h" #include "search.h" +#include "thread_win32.h" struct Thread; @@ -98,8 +99,8 @@ struct ThreadBase { void wait_for(volatile const bool& b); std::thread nativeThread; - std::mutex mutex; - std::condition_variable sleepCondition; + Mutex mutex; + ConditionVariable sleepCondition; volatile bool exit = false; }; @@ -167,7 +168,7 @@ struct ThreadPool : public std::vector { Depth minimumSplitDepth; Spinlock spinlock; - std::condition_variable sleepCondition; + ConditionVariable sleepCondition; TimerThread* timer; }; diff --git a/src/thread_win32.h b/src/thread_win32.h new file mode 100644 index 00000000..f0c33fb5 --- /dev/null +++ b/src/thread_win32.h @@ -0,0 +1,100 @@ +/* + Stockfish, a UCI chess playing engine derived from Glaurung 2.1 + Copyright (C) 2004-2008 Tord Romstad (Glaurung author) + Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad + + Stockfish is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + Stockfish 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. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#ifndef THREAD_WIN32_H_INCLUDED +#define THREAD_WIN32_H_INCLUDED + +/// STL thread library uded by gcc and mingw compilers is implemented above +/// POSIX pthread. Unfortunatly this yields to a much slower speed (about 30%) +/// than the native Win32 calls. So use our own implementation that relies on +/// the Windows specific low level calls. + +#if defined(_WIN32) && !defined(_MSC_VER) + +#ifndef NOMINMAX +# define NOMINMAX // disable macros min() and max() +#endif + +#define WIN32_LEAN_AND_MEAN +#include +#undef WIN32_LEAN_AND_MEAN +#undef NOMINMAX + +// We use critical sections on Windows to support Windows XP and older versions. +// Unfortunately, cond_wait() is racy between lock_release() and WaitForSingleObject() +// but apart from this they have the same speed performance of SRW locks. +typedef CRITICAL_SECTION Lock; +typedef HANDLE WaitCondition; +typedef HANDLE NativeHandle; + +// On Windows 95 and 98 parameter lpThreadId may not be null +inline DWORD* dwWin9xKludge() { static DWORD dw; return &dw; } + +# define lock_init(x) InitializeCriticalSection(&(x)) +# define lock_grab(x) EnterCriticalSection(&(x)) +# define lock_release(x) LeaveCriticalSection(&(x)) +# define lock_destroy(x) DeleteCriticalSection(&(x)) +# define cond_init(x) { x = CreateEvent(0, FALSE, FALSE, 0); } +# define cond_destroy(x) CloseHandle(x) +# define cond_signal(x) SetEvent(x) +# define cond_wait(x,y) { lock_release(y); WaitForSingleObject(x, INFINITE); lock_grab(y); } +# define cond_timedwait(x,y,z) { lock_release(y); WaitForSingleObject(x,z); lock_grab(y); } + +/// Mutex and ConditionVariable struct are wrappers of the low level locking +/// machinery and are modeled after the corresponding C++11 classes. + +struct Mutex { + Mutex() { lock_init(l); } + ~Mutex() { lock_destroy(l); } + + void lock() { lock_grab(l); } + void unlock() { lock_release(l); } + +private: + friend struct ConditionVariable; + + Lock l; +}; + +struct ConditionVariable { + ConditionVariable() { cond_init(c); } + ~ConditionVariable() { cond_destroy(c); } + + void notify_one() { cond_signal(c); } + void wait(std::unique_lock& lk) { cond_wait(c, lk.mutex()->l); } + + template + void wait(std::unique_lock& lk, Predicate p) { while (!p()) this->wait(lk); } + + void wait_for(std::unique_lock& lk, const std::chrono::milliseconds& ms) { + cond_timedwait(c, lk.mutex()->l, ms.count()); + } + +private: + WaitCondition c; +}; + +#else // Default case: use STL classes + +typedef std::mutex Mutex; +typedef std::condition_variable ConditionVariable; + +#endif + +#endif // #ifndef THREAD_WIN32_H_INCLUDED -- 2.39.2