Retire ThreadBase
authorMarco Costalba <mcostalba@gmail.com>
Thu, 5 Nov 2015 07:40:23 +0000 (08:40 +0100)
committerMarco Costalba <mcostalba@gmail.com>
Fri, 13 Nov 2015 07:22:44 +0000 (08:22 +0100)
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.

src/search.cpp
src/thread.cpp
src/thread.h

index 205e992723108b2b90c750706235410c7da1baf9..13fb4b300936492657ee375d0d14c3e9bf460e46 100644 (file)
@@ -220,11 +220,11 @@ uint64_t Search::perft(Position& pos, Depth depth) {
 template uint64_t Search::perft<true>(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();
     }
index c76b4b707e9f97dfe641670de06021b982481287..858a09d9c011bbe57a5b80feef21fe1c1d2eb670 100644 (file)
@@ -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<typename T> 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<Mutex> 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<Mutex> 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<Mutex> 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<Mutex> lk(mutex);
+  sleepCondition.wait(lk, [&]{ return bool(condition); });
 }
 
 
@@ -102,51 +91,22 @@ void Thread::idle_loop() {
   {
       std::unique_lock<Mutex> 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<Mutex> 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<Mutex> 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<MainThread>());
+  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<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
 }
index 6cceca7244f59b44205105aa26f3595217751ec4..0567f5e24828f3edcfa9c63677b9e0ca3da02794 100644 (file)
 #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();
 };