From d1143794a01cd0540cf47e3415815cd60cde9422 Mon Sep 17 00:00:00 2001 From: Marco Costalba Date: Mon, 14 Jan 2013 00:32:30 +0100 Subject: [PATCH] Polymorphic Thread hierarchy Subclass MainThread and TimerThread and declare idle_loop() virtual. This allow us to cleanly remove a good bunch of hacks, relying on C++ polymorphism to do the job. No functional change. --- src/search.cpp | 6 +++--- src/thread.cpp | 42 ++++++++++++++++++------------------------ src/thread.h | 31 +++++++++++++++++++------------ 3 files changed, 40 insertions(+), 39 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index 0dbba7e0..0be69124 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -235,7 +235,7 @@ void Search::think() { // Set best timer interval to avoid lagging under time pressure. Timer is // used to check for remaining available thinking time. - Threads.timer_thread()->maxPly = /* Hack: we use maxPly to set timer interval */ + Threads.timer_thread()->msec = Limits.use_time_management() ? std::min(100, std::max(TimeMgr.available_time() / 16, TimerResolution)) : Limits.nodes ? 2 * TimerResolution : 100; @@ -244,7 +244,7 @@ void Search::think() { id_loop(RootPos); // Let's start searching ! - Threads.timer_thread()->maxPly = 0; // Stop the timer + Threads.timer_thread()->msec = 0; // Stop the timer Threads.sleepWhileIdle = true; // Send idle threads to sleep if (Options["Use Search Log"]) @@ -1655,7 +1655,7 @@ void Thread::idle_loop() { // If this thread has been assigned work, launch a search if (is_searching) { - assert(/*!is_finished &&*/ !do_exit); + assert(!do_exit); Threads.mutex.lock(); diff --git a/src/thread.cpp b/src/thread.cpp index 7f5ce714..bc0b3fcc 100644 --- a/src/thread.cpp +++ b/src/thread.cpp @@ -32,26 +32,23 @@ ThreadPool Threads; // Global object namespace { extern "C" { // start_routine() is the C function which is called when a new thread - // is launched. It is a wrapper to member function pointed by start_fn. + // is launched. It is a wrapper to the virtual function idle_loop(). - long start_routine(Thread* th) { (th->*(th->start_fn))(); return 0; } + long start_routine(Thread* th) { th->idle_loop(); return 0; } } } // Thread c'tor starts a newly-created thread of execution that will call -// the idle loop function pointed by start_fn going immediately to sleep. +// the the virtual function idle_loop(), going immediately to sleep. -Thread::Thread(Fn fn) : splitPoints() { +Thread::Thread() : splitPoints() { is_searching = do_exit = false; maxPly = splitPointsCnt = 0; curSplitPoint = NULL; - start_fn = fn; idx = Threads.size(); - is_finished = (fn != &Thread::main_loop); // Avoid a race with start_searching() - if (!thread_create(handle, start_routine, this)) { std::cerr << "Failed to create thread number " << idx << std::endl; @@ -60,39 +57,37 @@ Thread::Thread(Fn fn) : splitPoints() { } -// Thread d'tor waits for thread termination before to return. +// Thread d'tor waits for thread termination before to return Thread::~Thread() { - assert(is_finished); - do_exit = true; // Search must be already finished notify_one(); thread_join(handle); // Wait for thread termination } -// Thread::timer_loop() is where the timer thread waits maxPly milliseconds and -// then calls check_time(). If maxPly is 0 thread sleeps until is woken up. +// TimerThread::idle_loop() is where the timer thread waits msec milliseconds +// and then calls check_time(). If msec is 0 thread sleeps until is woken up. extern void check_time(); -void Thread::timer_loop() { +void TimerThread::idle_loop() { while (!do_exit) { mutex.lock(); - while (!maxPly && !do_exit) - sleepCondition.wait_for(mutex, maxPly ? maxPly : INT_MAX); + while (!msec && !do_exit) + sleepCondition.wait_for(mutex, msec ? msec : INT_MAX); mutex.unlock(); check_time(); } } -// Thread::main_loop() is where the main thread is parked waiting to be started +// MainThread::idle_loop() is where the main thread is parked waiting to be started // when there is a new search. Main thread will launch all the slave threads. -void Thread::main_loop() { +void MainThread::idle_loop() { while (true) { @@ -121,8 +116,7 @@ void Thread::main_loop() { } -// Thread::notify_one() wakes up the thread, normally at the beginning of the -// search or, if "sleeping threads" is used at split time. +// Thread::notify_one() wakes up the thread, normally at split time void Thread::notify_one() { @@ -184,9 +178,9 @@ bool Thread::is_available_to(Thread* master) const { void ThreadPool::init() { - timer = new Thread(&Thread::timer_loop); - threads.push_back(new Thread(&Thread::main_loop)); sleepWhileIdle = true; + timer = new TimerThread(); + threads.push_back(new MainThread()); read_uci_options(); } @@ -216,7 +210,7 @@ void ThreadPool::read_uci_options() { assert(requested > 0); while (threads.size() < requested) - threads.push_back(new Thread(&Thread::idle_loop)); + threads.push_back(new Thread()); while (threads.size() > requested) { @@ -319,7 +313,7 @@ Value ThreadPool::split(Position& pos, Stack* ss, Value alpha, Value beta, // their work at this split point. if (slavesCnt || Fake) { - master->idle_loop(); + master->Thread::idle_loop(); // Force a call to base class idle_loop() // In helpful master concept a master can help only a sub-tree of its split // point, and because here is all finished is not possible master is booked. @@ -354,7 +348,7 @@ template Value ThreadPool::split(Position&, Stack*, Value, Value, Value, M void ThreadPool::wait_for_search_finished() { - Thread* t = main_thread(); + MainThread* t = main_thread(); t->mutex.lock(); while (!t->is_finished) sleepCondition.wait(t->mutex); t->mutex.unlock(); diff --git a/src/thread.h b/src/thread.h index 2fc958c1..9d04e5bd 100644 --- a/src/thread.h +++ b/src/thread.h @@ -93,18 +93,14 @@ struct SplitPoint { class Thread { - typedef void (Thread::* Fn) (); // Pointer to member function - public: - Thread(Fn fn); - ~Thread(); + Thread(); + virtual ~Thread(); + virtual void idle_loop(); void notify_one(); bool cutoff_occurred() const; bool is_available_to(Thread* master) const; - void idle_loop(); - void main_loop(); - void timer_loop(); void wait_for(volatile const bool& b); SplitPoint splitPoints[MAX_SPLITPOINTS_PER_THREAD]; @@ -116,14 +112,24 @@ public: Mutex mutex; ConditionVariable sleepCondition; NativeHandle handle; - Fn start_fn; SplitPoint* volatile curSplitPoint; volatile int splitPointsCnt; volatile bool is_searching; - volatile bool is_finished; volatile bool do_exit; }; +struct TimerThread : public Thread { + TimerThread() : msec(0) {} + virtual void idle_loop(); + int msec; +}; + +struct MainThread : public Thread { + MainThread() : is_finished(false) {} // Avoid a race with start_searching() + virtual void idle_loop(); + volatile bool is_finished; +}; + /// ThreadPool class handles all the threads related stuff like init, starting, /// parking and, the most important, launching a slave thread at a split point. @@ -138,8 +144,8 @@ public: Thread& operator[](size_t id) { return *threads[id]; } int min_split_depth() const { return minimumSplitDepth; } size_t size() const { return threads.size(); } - Thread* main_thread() { return threads[0]; } - Thread* timer_thread() { return timer; } + MainThread* main_thread() { return static_cast(threads[0]); } + TimerThread* timer_thread() { return timer; } void read_uci_options(); bool available_slave_exists(Thread* master) const; @@ -152,10 +158,11 @@ public: Depth depth, Move threatMove, int moveCount, MovePicker& mp, int nodeType); private: friend class Thread; + friend struct MainThread; friend void check_time(); std::vector threads; - Thread* timer; + TimerThread* timer; Mutex mutex; ConditionVariable sleepCondition; Depth minimumSplitDepth; -- 2.39.2