From: Marco Costalba Date: Thu, 5 Nov 2015 07:40:23 +0000 (+0100) Subject: Retire ThreadBase X-Git-Url: https://git.sesse.net/?p=stockfish;a=commitdiff_plain;h=76ed0ab5015f41715453a7efcedd57a7a5c962da Retire ThreadBase Now that we don't have anymore TimerThread, there is no need of this long class hierarchy. Also assorted reformatting while there. To verify no regression, passed at STC with 7 threads: LLR: 2.97 (-2.94,2.94) [-5.00,0.00] Total: 30990 W: 4945 L: 4942 D: 21103 No functional change. --- diff --git a/src/search.cpp b/src/search.cpp index 205e9927..13fb4b30 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -220,11 +220,11 @@ uint64_t Search::perft(Position& pos, Depth depth) { template uint64_t Search::perft(Position&, Depth); -/// MainThread::think() is called by the main thread when the program receives +/// MainThread::search() is called by the main thread when the program receives /// the UCI 'go' command. It searches from root position and at the end prints /// the "bestmove" to output. -void MainThread::think() { +void MainThread::search() { Color us = rootPos.side_to_move(); Time.init(Limits, us, rootPos.game_ply()); @@ -299,7 +299,7 @@ void MainThread::think() { } } - search(true); // Let's start searching! + Thread::search(); // Let's start searching! } // When playing in 'nodes as time' mode, subtract the searched nodes from @@ -324,7 +324,7 @@ void MainThread::think() { // Wait until all threads have finished for (Thread* th : Threads) if (th != this) - th->wait_while(th->searching); + th->join(); // Check if there are threads with a better score than main thread. Thread* bestThread = this; @@ -351,11 +351,12 @@ void MainThread::think() { // repeatedly with increasing depth until the allocated thinking time has been // consumed, user stops the search, or the maximum search depth is reached. -void Thread::search(bool isMainThread) { +void Thread::search() { Stack stack[MAX_PLY+4], *ss = stack+2; // To allow referencing (ss-2) and (ss+2) Value bestValue, alpha, beta, delta; Move easyMove = MOVE_NONE; + bool isMainThread = (this == Threads.main()); std::memset(ss-2, 0, 5 * sizeof(Stack)); @@ -532,9 +533,6 @@ void Thread::search(bool isMainThread) { } } - searching = false; - notify_one(); // Wake up main thread if is sleeping waiting for us - if (!isMainThread) return; @@ -583,15 +581,15 @@ namespace { ss->ply = (ss-1)->ply + 1; // Check for available remaining time - if (thisThread->resetCallsCnt.load(std::memory_order_relaxed)) + if (thisThread->resetCalls.load(std::memory_order_relaxed)) { - thisThread->resetCallsCnt = false; + thisThread->resetCalls = false; thisThread->callsCnt = 0; } if (++thisThread->callsCnt > 4096) { for (Thread* th : Threads) - th->resetCallsCnt = true; + th->resetCalls = true; check_time(); } diff --git a/src/thread.cpp b/src/thread.cpp index c76b4b70..858a09d9 100644 --- a/src/thread.cpp +++ b/src/thread.cpp @@ -29,68 +29,57 @@ using namespace Search; ThreadPool Threads; // Global object -namespace { +// Thread constructor makes some init and launches the thread that will go to +// sleep in idle_loop(). - // Helpers to launch a thread after creation and joining before delete. Outside the - // Thread constructor and destructor because the object must be fully initialized - // when start_routine (and hence virtual idle_loop) is called and when joining. - - template T* new_thread() { - std::thread* th = new T; - *th = std::thread(&T::idle_loop, (T*)th); // Will go to sleep - return (T*)th; - } - - void delete_thread(ThreadBase* th) { - - th->mutex.lock(); - th->exit = true; // Search must be already finished - th->mutex.unlock(); - - th->notify_one(); - th->join(); // Wait for thread termination - delete th; - } +Thread::Thread() { + searching = true; // Avoid a race with start_thinking() + exit = resetCalls = false; + maxPly = callsCnt = 0; + history.clear(); + counterMoves.clear(); + idx = Threads.size(); // Starts from 0 + std::thread::operator=(std::thread(&Thread::idle_loop, this)); } -// ThreadBase::notify_one() wakes up the thread when there is some work to do +// Thread destructor waits for thread termination before deleting -void ThreadBase::notify_one() { +Thread::~Thread() { - std::unique_lock lk(mutex); - sleepCondition.notify_one(); -} + mutex.lock(); + exit = true; // Search must be already finished + mutex.unlock(); + notify_one(); + std::thread::join(); // Wait for thread termination +} -// ThreadBase::wait() set the thread to sleep until 'condition' turns true -void ThreadBase::wait(std::atomic_bool& condition) { +// Thread::join() waits for the thread to finish searching +void Thread::join() { std::unique_lock lk(mutex); - sleepCondition.wait(lk, [&]{ return bool(condition); }); + sleepCondition.wait(lk, [&]{ return !searching; }); } -// ThreadBase::wait_while() set the thread to sleep until 'condition' turns false -void ThreadBase::wait_while(std::atomic_bool& condition) { +// Thread::notify_one() wakes up the thread when there is some work to do + +void Thread::notify_one() { std::unique_lock lk(mutex); - sleepCondition.wait(lk, [&]{ return !condition; }); + sleepCondition.notify_one(); } -// Thread constructor makes some init but does not launch any execution thread, -// which will be started only when the constructor returns. +// Thread::wait() set the thread to sleep until 'condition' turns true -Thread::Thread() { +void Thread::wait(std::atomic_bool& condition) { - searching = resetCallsCnt = false; - maxPly = callsCnt = 0; - history.clear(); - counterMoves.clear(); - idx = Threads.size(); // Starts from 0 + std::unique_lock lk(mutex); + sleepCondition.wait(lk, [&]{ return bool(condition); }); } @@ -102,51 +91,22 @@ void Thread::idle_loop() { { std::unique_lock lk(mutex); - while (!searching && !exit) - sleepCondition.wait(lk); + searching = false; - lk.unlock(); - - if (!exit && searching) - search(); - } -} - - -// MainThread::idle_loop() is where the main thread is parked waiting to be started -// when there is a new search. The main thread will launch all the slave threads. - -void MainThread::idle_loop() { - - while (!exit) - { - std::unique_lock lk(mutex); - - thinking = false; - - while (!thinking && !exit) + while (!searching && !exit) { - sleepCondition.notify_one(); // Wake up the UI thread if needed + sleepCondition.notify_one(); // Wake up main thread if needed sleepCondition.wait(lk); } lk.unlock(); - if (!exit) - think(); + if (!exit && searching) + search(); } } -// MainThread::join() waits for main thread to finish thinking - -void MainThread::join() { - - std::unique_lock lk(mutex); - sleepCondition.wait(lk, [&]{ return !thinking; }); -} - - // ThreadPool::init() is called at startup to create and launch requested threads, // that will go immediately to sleep. We cannot use a constructor because Threads // is a static object and we need a fully initialized engine at this point due to @@ -154,7 +114,7 @@ void MainThread::join() { void ThreadPool::init() { - push_back(new_thread()); + push_back(new MainThread); read_uci_options(); } @@ -165,7 +125,7 @@ void ThreadPool::init() { void ThreadPool::exit() { for (Thread* th : *this) - delete_thread(th); + delete th; clear(); // Get rid of stale pointers } @@ -184,11 +144,11 @@ void ThreadPool::read_uci_options() { assert(requested > 0); while (size() < requested) - push_back(new_thread()); + push_back(new Thread); while (size() > requested) { - delete_thread(back()); + delete back(); pop_back(); } } @@ -210,7 +170,8 @@ int64_t ThreadPool::nodes_searched() { void ThreadPool::start_thinking(const Position& pos, const LimitsType& limits, StateStackPtr& states) { - main()->join(); + for (Thread* th : Threads) + th->join(); Signals.stopOnPonderhit = Signals.firstRootMove = false; Signals.stop = Signals.failedLowAtRoot = false; @@ -229,6 +190,6 @@ void ThreadPool::start_thinking(const Position& pos, const LimitsType& limits, || std::count(limits.searchmoves.begin(), limits.searchmoves.end(), m)) main()->rootMoves.push_back(RootMove(m)); - main()->thinking = true; - main()->notify_one(); // Wake up main thread: 'thinking' must be already set + main()->searching = true; + main()->notify_one(); // Wake up main thread: 'searching' must be already set } diff --git a/src/thread.h b/src/thread.h index 6cceca72..0567f5e2 100644 --- a/src/thread.h +++ b/src/thread.h @@ -35,41 +35,30 @@ #include "thread_win32.h" -/// ThreadBase struct is the base of the hierarchy from where we derive all the -/// specialized thread classes. +/// Thread struct keeps together all the thread related stuff. We also use +/// per-thread pawn and material hash tables so that once we get a pointer to an +/// entry its life time is unlimited and we don't have to care about someone +/// changing the entry under our feet. -struct ThreadBase : public std::thread { +struct Thread : public std::thread { - ThreadBase() { exit = false; } - virtual ~ThreadBase() = default; - virtual void idle_loop() = 0; + Thread(); + virtual ~Thread(); + virtual void search(); + void idle_loop(); + void join(); void notify_one(); void wait(std::atomic_bool& b); - void wait_while(std::atomic_bool& b); + std::atomic_bool exit, searching, resetCalls; Mutex mutex; ConditionVariable sleepCondition; - std::atomic_bool exit; -}; - - -/// Thread struct keeps together all the thread related stuff like locks, state, -/// history and countermoves tables. We also use per-thread pawn and material hash -/// tables so that once we get a pointer to an entry its life time is unlimited -/// and we don't have to care about someone changing the entry under our feet. - -struct Thread : public ThreadBase { - - Thread(); - virtual void idle_loop(); - void search(bool isMainThread = false); Pawns::Table pawnsTable; Material::Table materialTable; Endgames endgames; size_t idx, PVIdx; int maxPly, callsCnt; - std::atomic_bool searching, resetCallsCnt; Position rootPos; Search::RootMoveVector rootMoves; @@ -83,11 +72,7 @@ struct Thread : public ThreadBase { /// MainThread is a derived classes used to characterize the the main one struct MainThread : public Thread { - MainThread() { thinking = true; } // Avoid a race with start_thinking() - virtual void idle_loop(); - void join(); - void think(); - std::atomic_bool thinking; + virtual void search(); };