From 26dabb1e6bcd8035f0f3516c4358a73f2b041a1c Mon Sep 17 00:00:00 2001 From: Marco Costalba Date: Fri, 20 Mar 2015 17:58:43 +0100 Subject: [PATCH 1/1] Use only one ConditionVariable to sync UI To sync UI with main thread it is enough a single condition variable because here we have a single producer / single consumer design pattern. Two condition variables are strictly needed just for many producers / many consumers case. Note that this is possible because now we don't send to sleep idle threads anymore while searching, so that now only UI can wake up the main thread and we can use the same ConditionVariable for both threads. The natural consequence is to retire wait_for_think_finished() and move all the logic under MainThread class, yielding the rename of teh function to join() No functional change. --- src/benchmark.cpp | 2 +- src/thread.cpp | 22 +++++++++++----------- src/thread.h | 3 +-- src/uci.cpp | 2 +- 4 files changed, 14 insertions(+), 15 deletions(-) diff --git a/src/benchmark.cpp b/src/benchmark.cpp index e27e81fe..fbf7b0e3 100644 --- a/src/benchmark.cpp +++ b/src/benchmark.cpp @@ -158,7 +158,7 @@ void benchmark(const Position& current, istream& is) { else { Threads.start_thinking(pos, limits, st); - Threads.wait_for_think_finished(); + Threads.main()->join(); nodes += Search::RootPos.nodes_searched(); } } diff --git a/src/thread.cpp b/src/thread.cpp index b4958ea3..a715d44e 100644 --- a/src/thread.cpp +++ b/src/thread.cpp @@ -251,7 +251,7 @@ void MainThread::idle_loop() { while (!thinking && !exit) { - Threads.sleepCondition.notify_one(); // Wake up the UI thread if needed + sleepCondition.notify_one(); // Wake up the UI thread if needed sleepCondition.wait(lk); } @@ -271,6 +271,15 @@ void MainThread::idle_loop() { } +// MainThread::join() waits for main thread to finish the search + +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 c'tor because Threads is a // static object and we need a fully initialized engine at this point due to @@ -337,21 +346,12 @@ Thread* ThreadPool::available_slave(const SplitPoint* sp) const { } -// ThreadPool::wait_for_think_finished() waits for main thread to finish the search - -void ThreadPool::wait_for_think_finished() { - - std::unique_lock lk(main()->mutex); - sleepCondition.wait(lk, [&]{ return !main()->thinking; }); -} - - // ThreadPool::start_thinking() wakes up the main thread sleeping in // MainThread::idle_loop() and starts a new search, then returns immediately. void ThreadPool::start_thinking(const Position& pos, const LimitsType& limits, StateStackPtr& states) { - wait_for_think_finished(); + main()->join(); SearchTime = now(); // As early as possible diff --git a/src/thread.h b/src/thread.h index 7932ad45..215b27c6 100644 --- a/src/thread.h +++ b/src/thread.h @@ -137,6 +137,7 @@ struct Thread : public ThreadBase { struct MainThread : public Thread { virtual void idle_loop(); + void join(); volatile bool thinking = true; // Avoid a race with start_thinking() }; @@ -162,11 +163,9 @@ struct ThreadPool : public std::vector { MainThread* main() { return static_cast(at(0)); } void read_uci_options(); Thread* available_slave(const SplitPoint* sp) const; - void wait_for_think_finished(); void start_thinking(const Position&, const Search::LimitsType&, Search::StateStackPtr&); Depth minimumSplitDepth; - ConditionVariable sleepCondition; TimerThread* timer; }; diff --git a/src/uci.cpp b/src/uci.cpp index b7127b75..7a0ead60 100644 --- a/src/uci.cpp +++ b/src/uci.cpp @@ -205,7 +205,7 @@ void UCI::loop(int argc, char* argv[]) { } while (token != "quit" && argc == 1); // Passed args have one-shot behaviour - Threads.wait_for_think_finished(); // Cannot quit whilst the search is running + Threads.main()->join(); // Cannot quit whilst the search is running } -- 2.39.2