Merge pull request #9 from glinscott/master
authorMarco Costalba <mcostalba@gmail.com>
Mon, 26 Mar 2012 18:55:32 +0000 (11:55 -0700)
committerMarco Costalba <mcostalba@gmail.com>
Mon, 26 Mar 2012 18:57:27 +0000 (19:57 +0100)
Penalty for undefended rook

Almost no change at longer TC, but perhaps there
is a tiny increase....

After 17522 games at 10"+0.05
Mod vs Orig 3064 - 2967 - 11491 ELO +2

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
12 files changed:
src/benchmark.cpp
src/evaluate.cpp
src/main.cpp
src/material.cpp
src/material.h
src/platform.h
src/search.cpp
src/thread.cpp
src/thread.h
src/tt.h
src/uci.cpp
src/ucioption.cpp

index 4124899..35fd725 100644 (file)
@@ -118,7 +118,8 @@ void benchmark(int argc, char* argv[]) {
       }
       else
       {
-          Threads.start_thinking(pos, limits);
+          Threads.start_searching(pos, limits);
+          Threads.wait_for_search_finished();
           nodes += Search::RootPosition.nodes_searched();
       }
   }
index c03aa99..6e515d8 100644 (file)
@@ -357,13 +357,12 @@ namespace {
 template<bool Trace>
 Value do_evaluate(const Position& pos, Value& margin) {
 
+  assert(!pos.in_check());
+
   EvalInfo ei;
   Value margins[2];
   Score score, mobilityWhite, mobilityBlack;
 
-  assert(pos.thread() >= 0 && pos.thread() < MAX_THREADS);
-  assert(!pos.in_check());
-
   // Initialize score by reading the incrementally updated scores included
   // in the position object (material + piece square tables).
   score = pos.value();
@@ -679,13 +678,13 @@ Value do_evaluate(const Position& pos, Value& margin) {
 
     const Color Them = (Us == WHITE ? BLACK : WHITE);
 
-    Bitboard b;
+    Bitboard b, undefended, undefendedMinors, weakEnemies;
     Score score = SCORE_ZERO;
 
     // Undefended pieces get penalized even if not under attack
-    Bitboard undefended = pos.pieces(Them) & ~ei.attackedBy[Them][0];
-    const Bitboard undefendedMinors = undefended & (pos.pieces(BISHOP) | pos.pieces(KNIGHT));
-    
+    undefended = pos.pieces(Them) & ~ei.attackedBy[Them][0];
+    undefendedMinors = undefended & (pos.pieces(BISHOP) | pos.pieces(KNIGHT));
+
     if (undefendedMinors)
         score += single_bit(undefendedMinors) ? UndefendedPiecePenalty
                                               : UndefendedPiecePenalty * 2;
@@ -693,9 +692,10 @@ Value do_evaluate(const Position& pos, Value& margin) {
         score += UndefendedPiecePenalty;
 
     // Enemy pieces not defended by a pawn and under our attack
-    Bitboard weakEnemies =  pos.pieces(Them)
-                          & ~ei.attackedBy[Them][PAWN]
-                          & ei.attackedBy[Us][0];
+    weakEnemies =  pos.pieces(Them)
+                 & ~ei.attackedBy[Them][PAWN]
+                 & ei.attackedBy[Us][0];
+
     if (!weakEnemies)
         return score;
 
index 1d5c6ea..3dcb3f6 100644 (file)
@@ -55,6 +55,4 @@ int main(int argc, char* argv[]) {
       cerr << "\nUsage: stockfish bench [hash size = 128] [threads = 1] "
            << "[limit = 12] [fen positions file = default] "
            << "[limited by depth, time, nodes or perft = depth]" << endl;
-
-  Threads.exit();
 }
index 7369bca..e2c64ff 100644 (file)
@@ -84,12 +84,6 @@ namespace {
 } // namespace
 
 
-/// MaterialInfoTable c'tor and d'tor allocate and free the space for Endgames
-
-void MaterialInfoTable::init() { Base::init(); if (!funcs) funcs = new Endgames(); }
-MaterialInfoTable::~MaterialInfoTable() { delete funcs; }
-
-
 /// MaterialInfoTable::material_info() takes a position object as input,
 /// computes or looks up a MaterialInfo object, and returns a pointer to it.
 /// If the material configuration is not already present in the table, it
index 45bfe8b..6f84492 100644 (file)
@@ -72,8 +72,9 @@ private:
 
 class MaterialInfoTable : public SimpleHash<MaterialInfo, MaterialTableSize> {
 public:
-  ~MaterialInfoTable();
-  void init();
+  MaterialInfoTable() : funcs(new Endgames()) {}
+  ~MaterialInfoTable() { delete funcs; }
+
   MaterialInfo* material_info(const Position& pos) const;
   static Phase game_phase(const Position& pos);
 
index 26c3abf..e002d21 100644 (file)
@@ -53,8 +53,8 @@ inline uint64_t time_to_msec(const sys_time_t& t) { return t.tv_sec * 1000LL + t
 #  include <pthread.h>
 typedef pthread_mutex_t Lock;
 typedef pthread_cond_t WaitCondition;
-typedef pthread_t ThreadHandle;
-typedef void*(*start_fn)(void*);
+typedef pthread_t NativeHandle;
+typedef void*(*pt_start_fn)(void*);
 
 #  define lock_init(x) pthread_mutex_init(&(x), NULL)
 #  define lock_grab(x) pthread_mutex_lock(&(x))
@@ -65,7 +65,7 @@ typedef void*(*start_fn)(void*);
 #  define cond_signal(x) pthread_cond_signal(&(x))
 #  define cond_wait(x,y) pthread_cond_wait(&(x),&(y))
 #  define cond_timedwait(x,y,z) pthread_cond_timedwait(&(x),&(y),z)
-#  define thread_create(x,f,id) !pthread_create(&(x),NULL,(start_fn)f,&(id))
+#  define thread_create(x,f,t) !pthread_create(&(x),NULL,(pt_start_fn)f,t)
 #  define thread_join(x) pthread_join(x, NULL)
 
 #else // Windows and MinGW
@@ -90,7 +90,7 @@ inline uint64_t time_to_msec(const sys_time_t& t) { return t.time * 1000LL + t.m
 // but apart from this they have the same speed performance of SRW locks.
 typedef CRITICAL_SECTION Lock;
 typedef HANDLE WaitCondition;
-typedef HANDLE ThreadHandle;
+typedef HANDLE NativeHandle;
 
 #  define lock_init(x) InitializeCriticalSection(&(x))
 #  define lock_grab(x) EnterCriticalSection(&(x))
@@ -101,7 +101,7 @@ typedef HANDLE ThreadHandle;
 #  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); }
-#  define thread_create(x,f,id) (x = CreateThread(NULL,0,(LPTHREAD_START_ROUTINE)f,&(id),0,NULL), x != NULL)
+#  define thread_create(x,f,t) (x = CreateThread(NULL,0,(LPTHREAD_START_ROUTINE)f,t,0,NULL), x != NULL)
 #  define thread_join(x) { WaitForSingleObject(x, INFINITE); CloseHandle(x); }
 
 #endif
index fb5d375..a76a952 100644 (file)
@@ -298,7 +298,7 @@ void Search::think() {
           << endl;
   }
 
-  Threads.set_size(Options["Threads"]);
+  Threads.wake_up();
 
   // Set best timer interval to avoid lagging under time pressure. Timer is
   // used to check for remaining available thinking time.
@@ -310,9 +310,8 @@ void Search::think() {
   // We're ready to start searching. Call the iterative deepening loop function
   id_loop(pos);
 
-  // Stop timer and send all the slaves to sleep, if not already sleeping
-  Threads.set_timer(0);
-  Threads.set_size(1);
+  Threads.set_timer(0); // Stop timer
+  Threads.sleep();
 
   if (Options["Use Search Log"])
   {
index 5c23aa5..10f432b 100644 (file)
@@ -32,29 +32,61 @@ ThreadsManager Threads; // Global object
 namespace { extern "C" {
 
  // start_routine() is the C function which is called when a new thread
- // is launched. It simply calls idle_loop() of the supplied thread. The first
- // and last thread are special. First one is the main search thread while the
- // last one mimics a timer, they run in main_loop() and timer_loop().
+ // is launched. It is a wrapper to member function pointed by start_fn.
 
-  long start_routine(Thread* th) {
+ long start_routine(Thread* th) { (th->*(th->start_fn))(); return 0; }
 
-    if (th->threadID == 0)
-        th->main_loop();
+} }
+
+
+// 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.
+
+Thread::Thread(Fn fn) {
+
+  is_searching = do_exit = false;
+  maxPly = splitPointsCnt = 0;
+  curSplitPoint = NULL;
+  start_fn = fn;
+  threadID = Threads.size();
 
-    else if (th->threadID == MAX_THREADS)
-        th->timer_loop();
+  do_sleep = (fn != &Thread::main_loop); // Avoid a race with start_searching()
 
-    else
-        th->idle_loop(NULL);
+  lock_init(sleepLock);
+  cond_init(sleepCond);
+
+  for (int j = 0; j < MAX_SPLITPOINTS_PER_THREAD; j++)
+      lock_init(splitPoints[j].lock);
 
-    return 0;
+  if (!thread_create(handle, start_routine, this))
+  {
+      std::cerr << "Failed to create thread number " << threadID << std::endl;
+      ::exit(EXIT_FAILURE);
   }
+}
 
-} }
+
+// Thread d'tor waits for thread termination before to return.
+
+Thread::~Thread() {
+
+  assert(do_sleep);
+
+  do_exit = true; // Search must be already finished
+  wake_up();
+
+  thread_join(handle); // Wait for thread termination
+
+  lock_destroy(sleepLock);
+  cond_destroy(sleepCond);
+
+  for (int j = 0; j < MAX_SPLITPOINTS_PER_THREAD; j++)
+      lock_destroy(splitPoints[j].lock);
+}
 
 
 // Thread::timer_loop() is where the timer thread waits maxPly milliseconds and
-// then calls do_timer_event(). If maxPly is 0 thread sleeps until is woken up.
+// then calls check_time(). If maxPly is 0 thread sleeps until is woken up.
 extern void check_time();
 
 void Thread::timer_loop() {
@@ -100,11 +132,12 @@ void Thread::main_loop() {
 
 
 // Thread::wake_up() wakes up the thread, normally at the beginning of the search
-// or, if "sleeping threads" is used, when there is some work to do.
+// or, if "sleeping threads" is used at split time.
 
 void Thread::wake_up() {
 
   lock_grab(sleepLock);
+  do_sleep = false;
   cond_signal(sleepCond);
   lock_release(sleepLock);
 }
@@ -122,16 +155,13 @@ void Thread::wait_for_stop_or_ponderhit() {
   Signals.stopOnPonderhit = true;
 
   lock_grab(sleepLock);
-
-  while (!Signals.stop)
-      cond_wait(sleepCond, sleepLock);
-
+  while (!Signals.stop) cond_wait(sleepCond, sleepLock);
   lock_release(sleepLock);
 }
 
 
-// cutoff_occurred() checks whether a beta cutoff has occurred in the current
-// active split point, or in some ancestor of the split point.
+// Thread::cutoff_occurred() checks whether a beta cutoff has occurred in the
+// current active split point, or in some ancestor of the split point.
 
 bool Thread::cutoff_occurred() const {
 
@@ -143,12 +173,12 @@ bool Thread::cutoff_occurred() const {
 }
 
 
-// is_available_to() checks whether the thread is available to help the thread with
-// threadID "master" at a split point. An obvious requirement is that thread must be
-// idle. With more than two threads, this is not by itself sufficient: If the thread
-// is the master of some active split point, it is only available as a slave to the
-// threads which are busy searching the split point at the top of "slave"'s split
-// point stack (the "helpful master concept" in YBWC terminology).
+// Thread::is_available_to() checks whether the thread is available to help the
+// thread with threadID "master" at a split point. An obvious requirement is that
+// thread must be idle. With more than two threads, this is not sufficient: If
+// the thread is the master of some active split point, it is only available as a
+// slave to the threads which are busy searching the split point at the top of
+// "slave"'s split point stack (the "helpful master concept" in YBWC terminology).
 
 bool Thread::is_available_to(int master) const {
 
@@ -165,104 +195,82 @@ bool Thread::is_available_to(int master) const {
 }
 
 
-// read_uci_options() updates internal threads parameters from the corresponding
-// UCI options. It is called before to start a new search.
+// init() is called at startup. Initializes lock and condition variable and
+// launches requested threads sending them immediately to sleep. We cannot use
+// a c'tor becuase Threads is a static object and we need a fully initialized
+// engine at this point due to allocation of endgames in Thread c'tor.
 
-void ThreadsManager::read_uci_options() {
+void ThreadsManager::init() {
 
-  maxThreadsPerSplitPoint = Options["Max Threads per Split Point"];
-  minimumSplitDepth       = Options["Min Split Depth"] * ONE_PLY;
-  useSleepingThreads      = Options["Use Sleeping Threads"];
+  cond_init(sleepCond);
+  lock_init(splitLock);
+  timer = new Thread(&Thread::timer_loop);
+  threads.push_back(new Thread(&Thread::main_loop));
+  read_uci_options();
 }
 
 
-// set_size() changes the number of active threads and raises do_sleep flag for
-// all the unused threads that will go immediately to sleep.
+// d'tor cleanly terminates the threads when the program exits.
 
-void ThreadsManager::set_size(int cnt) {
+ThreadsManager::~ThreadsManager() {
 
-  assert(cnt > 0 && cnt < MAX_THREADS);
+  for (int i = 0; i < size(); i++)
+      delete threads[i];
 
-  activeThreads = cnt;
-
-  for (int i = 0; i < MAX_THREADS; i++)
-      if (i < activeThreads)
-      {
-          // Dynamically allocate pawn and material hash tables according to the
-          // number of active threads. This avoids preallocating memory for all
-          // possible threads if only few are used.
-          threads[i].pawnTable.init();
-          threads[i].materialTable.init();
-          threads[i].maxPly = 0;
-
-          threads[i].do_sleep = false;
-
-          if (!useSleepingThreads)
-              threads[i].wake_up();
-      }
-      else
-          threads[i].do_sleep = true;
+  delete timer;
+  lock_destroy(splitLock);
+  cond_destroy(sleepCond);
 }
 
 
-// init() is called during startup. Initializes locks and condition variables
-// and launches all threads sending them immediately to sleep.
+// read_uci_options() updates internal threads parameters from the corresponding
+// UCI options and creates/destroys threads to match the requested number. Thread
+// objects are dynamically allocated to avoid creating in advance all possible
+// threads, with included pawns and material tables, if only few are used.
 
-void ThreadsManager::init() {
+void ThreadsManager::read_uci_options() {
 
-  read_uci_options();
+  maxThreadsPerSplitPoint = Options["Max Threads per Split Point"];
+  minimumSplitDepth       = Options["Min Split Depth"] * ONE_PLY;
+  useSleepingThreads      = Options["Use Sleeping Threads"];
+  int requested           = Options["Threads"];
 
-  cond_init(sleepCond);
-  lock_init(splitLock);
+  assert(requested > 0);
 
-  // Allocate main thread tables to call evaluate() also when not searching
-  threads[0].pawnTable.init();
-  threads[0].materialTable.init();
+  while (size() < requested)
+      threads.push_back(new Thread(&Thread::idle_loop));
 
-  // Create and launch all the threads, threads will go immediately to sleep
-  for (int i = 0; i <= MAX_THREADS; i++)
+  while (size() > requested)
   {
-      threads[i].is_searching = false;
-      threads[i].do_sleep = (i != 0); // Avoid a race with start_thinking()
-      threads[i].threadID = i;
-
-      lock_init(threads[i].sleepLock);
-      cond_init(threads[i].sleepCond);
-
-      for (int j = 0; j < MAX_SPLITPOINTS_PER_THREAD; j++)
-          lock_init(threads[i].splitPoints[j].lock);
-
-      if (!thread_create(threads[i].handle, start_routine, threads[i]))
-      {
-          std::cerr << "Failed to create thread number " << i << std::endl;
-          ::exit(EXIT_FAILURE);
-      }
+      delete threads.back();
+      threads.pop_back();
   }
 }
 
 
-// exit() is called to cleanly terminate the threads when the program finishes
+// wake_up() is called before a new search to start the threads that are waiting
+// on the sleep condition and to reset maxPly. When useSleepingThreads is set
+// threads will be woken up at split time.
 
-void ThreadsManager::exit() {
+void ThreadsManager::wake_up() const {
 
-  for (int i = 0; i <= MAX_THREADS; i++)
+  for (int i = 0; i < size(); i++)
   {
-      assert(threads[i].do_sleep);
+      threads[i]->maxPly = 0;
 
-      threads[i].do_exit = true; // Search must be already finished
-      threads[i].wake_up();
+      if (!useSleepingThreads)
+          threads[i]->wake_up();
+  }
+}
 
-      thread_join(threads[i].handle); // Wait for thread termination
 
-      lock_destroy(threads[i].sleepLock);
-      cond_destroy(threads[i].sleepCond);
+// sleep() is called after the search finishes to ask all the threads but the
+// main one to go waiting on a sleep condition.
 
-      for (int j = 0; j < MAX_SPLITPOINTS_PER_THREAD; j++)
-          lock_destroy(threads[i].splitPoints[j].lock);
-  }
+void ThreadsManager::sleep() const {
 
-  lock_destroy(splitLock);
-  cond_destroy(sleepCond);
+  for (int i = 1; i < size(); i++) // Main thread will go to sleep by itself
+      threads[i]->do_sleep = true; // to avoid a race with start_searching()
 }
 
 
@@ -271,10 +279,10 @@ void ThreadsManager::exit() {
 
 bool ThreadsManager::available_slave_exists(int master) const {
 
-  assert(master >= 0 && master < activeThreads);
+  assert(master >= 0 && master < size());
 
-  for (int i = 0; i < activeThreads; i++)
-      if (threads[i].is_available_to(master))
+  for (int i = 0; i < size(); i++)
+      if (threads[i]->is_available_to(master))
           return true;
 
   return false;
@@ -300,11 +308,9 @@ Value ThreadsManager::split(Position& pos, Stack* ss, Value alpha, Value beta,
   assert(alpha < beta);
   assert(beta <= VALUE_INFINITE);
   assert(depth > DEPTH_ZERO);
-  assert(pos.thread() >= 0 && pos.thread() < activeThreads);
-  assert(activeThreads > 1);
 
   int master = pos.thread();
-  Thread& masterThread = threads[master];
+  Thread& masterThread = *threads[master];
 
   if (masterThread.splitPointsCnt >= MAX_SPLITPOINTS_PER_THREAD)
       return bestValue;
@@ -340,15 +346,15 @@ Value ThreadsManager::split(Position& pos, Stack* ss, Value alpha, Value beta,
   lock_grab(sp->lock);
   lock_grab(splitLock);
 
-  for (int i = 0; i < activeThreads && !Fake; i++)
-      if (threads[i].is_available_to(master))
+  for (int i = 0; i < size() && !Fake; ++i)
+      if (threads[i]->is_available_to(master))
       {
           sp->slavesMask |= 1ULL << i;
-          threads[i].curSplitPoint = sp;
-          threads[i].is_searching = true; // Slave leaves idle_loop()
+          threads[i]->curSplitPoint = sp;
+          threads[i]->is_searching = true; // Slave leaves idle_loop()
 
           if (useSleepingThreads)
-              threads[i].wake_up();
+              threads[i]->wake_up();
 
           if (++slavesCnt + 1 >= maxThreadsPerSplitPoint) // Master is always included
               break;
@@ -399,72 +405,42 @@ template Value ThreadsManager::split<true>(Position&, Stack*, Value, Value, Valu
 
 void ThreadsManager::set_timer(int msec) {
 
-  Thread& timer = threads[MAX_THREADS];
-
-  lock_grab(timer.sleepLock);
-  timer.maxPly = msec;
-  cond_signal(timer.sleepCond); // Wake up and restart the timer
-  lock_release(timer.sleepLock);
+  lock_grab(timer->sleepLock);
+  timer->maxPly = msec;
+  cond_signal(timer->sleepCond); // Wake up and restart the timer
+  lock_release(timer->sleepLock);
 }
 
 
-// ThreadsManager::start_thinking() is used by UI thread to wake up the main
-// thread parked in main_loop() and starting a new search. If asyncMode is true
-// then function returns immediately, otherwise caller is blocked waiting for
-// the search to finish.
+// ThreadsManager::wait_for_search_finished() waits for main thread to go to
+// sleep, this means search is finished. Then returns.
 
-void ThreadsManager::start_thinking(const Position& pos, const LimitsType& limits,
-                                    const std::set<Move>& searchMoves, bool async) {
-  Thread& main = threads[0];
+void ThreadsManager::wait_for_search_finished() {
+
+  Thread* main = threads[0];
+  lock_grab(main->sleepLock);
+  while (!main->do_sleep) cond_wait(sleepCond, main->sleepLock);
+  lock_release(main->sleepLock);
+}
 
-  lock_grab(main.sleepLock);
 
-  // Wait main thread has finished before to launch a new search
-  while (!main.do_sleep)
-      cond_wait(sleepCond, main.sleepLock);
+// ThreadsManager::start_searching() wakes up the main thread sleeping in
+// main_loop() so to start a new search, then returns immediately.
+
+void ThreadsManager::start_searching(const Position& pos, const LimitsType& limits,
+                                     const std::set<Move>& searchMoves) {
+  wait_for_search_finished();
+
+  Signals.stopOnPonderhit = Signals.firstRootMove = false;
+  Signals.stop = Signals.failedLowAtRoot = false;
 
-  // Copy input arguments to initialize the search
   RootPosition.copy(pos, 0);
   Limits = limits;
   RootMoves.clear();
 
-  // Populate RootMoves with all the legal moves (default) or, if a searchMoves
-  // set is given, with the subset of legal moves to search.
   for (MoveList<MV_LEGAL> ml(pos); !ml.end(); ++ml)
       if (searchMoves.empty() || searchMoves.count(ml.move()))
           RootMoves.push_back(RootMove(ml.move()));
 
-  // Reset signals before to start the new search
-  Signals.stopOnPonderhit = Signals.firstRootMove = false;
-  Signals.stop = Signals.failedLowAtRoot = false;
-
-  main.do_sleep = false;
-  cond_signal(main.sleepCond); // Wake up main thread and start searching
-
-  if (!async)
-      while (!main.do_sleep)
-          cond_wait(sleepCond, main.sleepLock);
-
-  lock_release(main.sleepLock);
-}
-
-
-// ThreadsManager::stop_thinking() is used by UI thread to raise a stop request
-// and to wait for the main thread finishing the search. Needed to wait exiting
-// and terminate the threads after a 'quit' command.
-
-void ThreadsManager::stop_thinking() {
-
-  Thread& main = threads[0];
-
-  Search::Signals.stop = true;
-
-  lock_grab(main.sleepLock);
-
-  cond_signal(main.sleepCond); // In case is waiting for stop or ponderhit
-
-  while (!main.do_sleep)
-      cond_wait(sleepCond, main.sleepLock);
-
-  lock_release(main.sleepLock);
+  threads[0]->wake_up();
 }
index e71db83..9b0a8bc 100644 (file)
@@ -21,6 +21,7 @@
 #define THREAD_H_INCLUDED
 
 #include <set>
+#include <vector>
 
 #include "material.h"
 #include "movepick.h"
@@ -46,7 +47,6 @@ struct SplitPoint {
   MovePicker* mp;
   SplitPoint* parent;
 
-
   // Shared data
   Lock lock;
   volatile uint64_t slavesMask;
@@ -64,12 +64,22 @@ struct SplitPoint {
 /// 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 {
+class Thread {
+
+  Thread(const Thread&);            // Only declared to disable the default ones
+  Thread& operator=(const Thread&); // that are not suitable in this case.
+
+  typedef void (Thread::* Fn) ();
+
+public:
+  Thread(Fn fn);
+  ~Thread();
 
   void wake_up();
   bool cutoff_occurred() const;
   bool is_available_to(int master) const;
   void idle_loop(SplitPoint* sp_master);
+  void idle_loop() { idle_loop(NULL); } // Hack to allow storing in start_fn
   void main_loop();
   void timer_loop();
   void wait_for_stop_or_ponderhit();
@@ -81,7 +91,8 @@ struct Thread {
   int maxPly;
   Lock sleepLock;
   WaitCondition sleepCond;
-  ThreadHandle handle;
+  NativeHandle handle;
+  Fn start_fn;
   SplitPoint* volatile curSplitPoint;
   volatile int splitPointsCnt;
   volatile bool is_searching;
@@ -100,34 +111,35 @@ class ThreadsManager {
      static storage duration are automatically set to zero before enter main()
   */
 public:
-  Thread& operator[](int threadID) { return threads[threadID]; }
-  void init();
-  void exit();
+  void init(); // No c'tor becuase Threads is static and we need stuff initialized
+  ~ThreadsManager();
 
+  Thread& operator[](int id) { return *threads[id]; }
   bool use_sleeping_threads() const { return useSleepingThreads; }
   int min_split_depth() const { return minimumSplitDepth; }
-  int size() const { return activeThreads; }
+  int size() const { return (int)threads.size(); }
 
-  void set_size(int cnt);
+  void wake_up() const;
+  void sleep() const;
   void read_uci_options();
   bool available_slave_exists(int master) const;
   void set_timer(int msec);
-  void stop_thinking();
-  void start_thinking(const Position& pos, const Search::LimitsType& limits,
-                      const std::set<Move>& = std::set<Move>(), bool async = false);
+  void wait_for_search_finished();
+  void start_searching(const Position& pos, const Search::LimitsType& limits,
+                       const std::set<Move>& = std::set<Move>());
 
   template <bool Fake>
   Value split(Position& pos, Search::Stack* ss, Value alpha, Value beta, Value bestValue, Move* bestMove,
               Depth depth, Move threatMove, int moveCount, MovePicker* mp, int nodeType);
 private:
-  friend struct Thread;
+  friend class Thread;
 
-  Thread threads[MAX_THREADS + 1]; // Last one is used as a timer
+  std::vector<Thread*> threads;
+  Thread* timer;
   Lock splitLock;
   WaitCondition sleepCond;
   Depth minimumSplitDepth;
   int maxThreadsPerSplitPoint;
-  int activeThreads;
   bool useSleepingThreads;
 };
 
index 39c161d..ae530c2 100644 (file)
--- a/src/tt.h
+++ b/src/tt.h
@@ -139,24 +139,21 @@ inline void TranspositionTable::refresh(const TTEntry* tte) const {
 
 /// A simple fixed size hash table used to store pawns and material
 /// configurations. It is basically just an array of Entry objects.
-/// Without cluster concept or overwrite policy.
+/// Without cluster concept, overwrite policy nor resizing.
 
 template<class Entry, int HashSize>
 struct SimpleHash {
 
   typedef SimpleHash<Entry, HashSize> Base;
 
-  void init() {
-
-    if (entries)
-        return;
+  SimpleHash() {
 
     entries = new (std::nothrow) Entry[HashSize];
     if (!entries)
     {
         std::cerr << "Failed to allocate " << HashSize * sizeof(Entry)
                   << " bytes for hash table." << std::endl;
-        exit(EXIT_FAILURE);
+        ::exit(EXIT_FAILURE);
     }
     memset(entries, 0, HashSize * sizeof(Entry));
   }
index 718c787..7c2bd1e 100644 (file)
@@ -67,7 +67,12 @@ void uci_loop() {
       is >> skipws >> token;
 
       if (token == "quit" || token == "stop")
-          Threads.stop_thinking();
+      {
+          Search::Signals.stop = true;
+
+          if (token == "quit") // Cannot quit while threads are still running
+              Threads.wait_for_search_finished();
+      }
 
       else if (token == "ponderhit")
       {
@@ -77,7 +82,7 @@ void uci_loop() {
           Search::Limits.ponder = false;
 
           if (Search::Signals.stopOnPonderhit)
-              Threads.stop_thinking();
+              Search::Signals.stop = true;
       }
 
       else if (token == "go")
@@ -223,7 +228,7 @@ namespace {
     limits.time = time[pos.side_to_move()];
     limits.increment = inc[pos.side_to_move()];
 
-    Threads.start_thinking(pos, limits, searchMoves, true);
+    Threads.start_searching(pos, limits, searchMoves);
   }
 
 
index df73670..4be580f 100644 (file)
@@ -73,7 +73,7 @@ OptionsMap::OptionsMap() {
   o["Cowardice"]                   = UCIOption(100, 0, 200, on_eval);
   o["Min Split Depth"]             = UCIOption(msd, 4, 7, on_threads);
   o["Max Threads per Split Point"] = UCIOption(5, 4, 8, on_threads);
-  o["Threads"]                     = UCIOption(cpus, 1, MAX_THREADS);
+  o["Threads"]                     = UCIOption(cpus, 1, MAX_THREADS, on_threads);
   o["Use Sleeping Threads"]        = UCIOption(true, on_threads);
   o["Hash"]                        = UCIOption(32, 4, 8192, on_hash_size);
   o["Clear Hash"]                  = UCIOption(on_clear_hash);