]> git.sesse.net Git - stockfish/commitdiff
Rewrite async I/O
authorMarco Costalba <mcostalba@gmail.com>
Wed, 23 Nov 2011 19:07:29 +0000 (20:07 +0100)
committerMarco Costalba <mcostalba@gmail.com>
Sat, 26 Nov 2011 10:51:12 +0000 (11:51 +0100)
Use the starting thread to wait for GUI input and instead use
the other threads to search. The consequence is that now think()
is alwasy started on a differnt thread than the caller that
returns immediately waiting for input. This reformat greatly
simplifies the code and is more in line with the common way
to implement this feature.

As a side effect now we don't need anymore Makefile tricks
with sleep() to allow profile builds.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
src/Makefile
src/benchmark.cpp
src/search.cpp
src/search.h
src/thread.cpp
src/thread.h
src/uci.cpp

index 1e765faad581025f642e44dce80baf18a9306a9f..86afd58ac6b449d177eb8c251bd9e6dae02985fa 100644 (file)
@@ -389,7 +389,7 @@ profile-build:
        $(MAKE) ARCH=$(ARCH) COMP=$(COMP) $(profile_make)
        @echo ""
        @echo "Step 2/4. Running benchmark for pgo-build ..."
        $(MAKE) ARCH=$(ARCH) COMP=$(COMP) $(profile_make)
        @echo ""
        @echo "Step 2/4. Running benchmark for pgo-build ..."
-       @sleep 10 | $(PGOBENCH) > /dev/null
+       @$(PGOBENCH) > /dev/null
        @echo ""
        @echo "Step 3/4. Building final executable ..."
        @touch *.cpp
        @echo ""
        @echo "Step 3/4. Building final executable ..."
        @touch *.cpp
@@ -409,14 +409,14 @@ double-profile-build:
        $(MAKE) ARCH=x86-64 COMP=$(COMP) $(profile_make)
        @echo ""
        @echo "Step 2/6. Running benchmark for pgo-build (popcnt disabled)..."
        $(MAKE) ARCH=x86-64 COMP=$(COMP) $(profile_make)
        @echo ""
        @echo "Step 2/6. Running benchmark for pgo-build (popcnt disabled)..."
-       @sleep 10 | $(PGOBENCH) > /dev/null
+       @$(PGOBENCH) > /dev/null
        @echo ""
        @echo "Step 3/6. Building executable for benchmark (popcnt enabled)..."
        @touch *.cpp *.h
        $(MAKE) ARCH=x86-64-modern COMP=$(COMP) $(profile_make)
        @echo ""
        @echo "Step 4/6. Running benchmark for pgo-build (popcnt enabled)..."
        @echo ""
        @echo "Step 3/6. Building executable for benchmark (popcnt enabled)..."
        @touch *.cpp *.h
        $(MAKE) ARCH=x86-64-modern COMP=$(COMP) $(profile_make)
        @echo ""
        @echo "Step 4/6. Running benchmark for pgo-build (popcnt enabled)..."
-       @sleep 10 | $(PGOBENCH) > /dev/null
+       @$(PGOBENCH) > /dev/null
        @echo ""
        @echo "Step 5/6. Building final executable ..."
        @touch *.cpp *.h
        @echo ""
        @echo "Step 5/6. Building final executable ..."
        @touch *.cpp *.h
index 95dd88ac01fad7b525eb65a90d3e0cce1ab40e54..551c341b1043df21dfc56d0a0c0cdecdae42a589 100644 (file)
@@ -23,6 +23,7 @@
 
 #include "position.h"
 #include "search.h"
 
 #include "position.h"
 #include "search.h"
+#include "thread.h"
 #include "ucioption.h"
 
 using namespace std;
 #include "ucioption.h"
 
 using namespace std;
@@ -59,7 +60,6 @@ static const string Defaults[] = {
 void benchmark(int argc, char* argv[]) {
 
   vector<string> fenList;
 void benchmark(int argc, char* argv[]) {
 
   vector<string> fenList;
-  SearchLimits limits;
   int64_t totalNodes;
   int time;
 
   int64_t totalNodes;
   int time;
 
@@ -76,11 +76,11 @@ void benchmark(int argc, char* argv[]) {
 
   // Search should be limited by nodes, time or depth ?
   if (valType == "nodes")
 
   // Search should be limited by nodes, time or depth ?
   if (valType == "nodes")
-      limits.maxNodes = atoi(valStr.c_str());
+      Limits.maxNodes = atoi(valStr.c_str());
   else if (valType == "time")
   else if (valType == "time")
-      limits.maxTime = 1000 * atoi(valStr.c_str()); // maxTime is in ms
+      Limits.maxTime = 1000 * atoi(valStr.c_str()); // maxTime is in ms
   else
   else
-      limits.maxDepth = atoi(valStr.c_str());
+      Limits.maxDepth = atoi(valStr.c_str());
 
   // Do we need to load positions from a given FEN file?
   if (fenFile != "default")
 
   // Do we need to load positions from a given FEN file?
   if (fenFile != "default")
@@ -107,28 +107,27 @@ void benchmark(int argc, char* argv[]) {
   // Ok, let's start the benchmark !
   totalNodes = 0;
   time = get_system_time();
   // Ok, let's start the benchmark !
   totalNodes = 0;
   time = get_system_time();
+  SearchMoves.push_back(MOVE_NONE);
 
   for (size_t i = 0; i < fenList.size(); i++)
   {
 
   for (size_t i = 0; i < fenList.size(); i++)
   {
-      Move moves[] = { MOVE_NONE };
       Position pos(fenList[i], false, 0);
       Position pos(fenList[i], false, 0);
+      RootPosition = &pos;
 
       cerr << "\nBench position: " << i + 1 << '/' << fenList.size() << endl;
 
       if (valType == "perft")
       {
 
       cerr << "\nBench position: " << i + 1 << '/' << fenList.size() << endl;
 
       if (valType == "perft")
       {
-          int64_t cnt = perft(pos, limits.maxDepth * ONE_PLY);
+          int64_t cnt = perft(pos, Limits.maxDepth * ONE_PLY);
 
 
-          cerr << "\nPerft " << limits.maxDepth
+          cerr << "\nPerft " << Limits.maxDepth
                << " nodes counted: " << cnt << endl;
 
           totalNodes += cnt;
       }
       else
       {
                << " nodes counted: " << cnt << endl;
 
           totalNodes += cnt;
       }
       else
       {
-          if (!think(pos, limits, moves))
-              break;
-
+          Threads.start_thinking(false);
           totalNodes += pos.nodes_searched();
       }
   }
           totalNodes += pos.nodes_searched();
       }
   }
index 402433ac80fb40924600628c2e249728a053ff8f..560f64f1778e0e19d0a54be6cfe56e05c85a1715 100644 (file)
@@ -43,6 +43,10 @@ using std::cout;
 using std::endl;
 using std::string;
 
 using std::endl;
 using std::string;
 
+SearchLimits Limits;
+std::vector<Move> SearchMoves;
+Position* RootPosition;
+
 namespace {
 
   // Set to true to force running with one thread. Used for debugging
 namespace {
 
   // Set to true to force running with one thread. Used for debugging
@@ -162,9 +166,8 @@ namespace {
   int MultiPV, UCIMultiPV, MultiPVIdx;
 
   // Time management variables
   int MultiPV, UCIMultiPV, MultiPVIdx;
 
   // Time management variables
-  volatile bool StopOnPonderhit, FirstRootMove, StopRequest, QuitRequest, AspirationFailLow;
+  volatile bool StopOnPonderhit, FirstRootMove, StopRequest, AspirationFailLow;
   TimeManager TimeMgr;
   TimeManager TimeMgr;
-  SearchLimits Limits;
 
   // Skill level adjustment
   int SkillLevel;
 
   // Skill level adjustment
   int SkillLevel;
@@ -200,7 +203,6 @@ namespace {
   string pv_to_uci(const Move pv[], int pvNum, bool chess960);
   string pretty_pv(Position& pos, int depth, Value score, int time, Move pv[]);
   string depth_to_uci(Depth depth);
   string pv_to_uci(const Move pv[], int pvNum, bool chess960);
   string pretty_pv(Position& pos, int depth, Value score, int time, Move pv[]);
   string depth_to_uci(Depth depth);
-  void wait_for_stop_or_ponderhit();
 
   // MovePickerExt template class extends MovePicker and allows to choose at compile
   // time the proper moves source according to the type of node. In the default case
 
   // MovePickerExt template class extends MovePicker and allows to choose at compile
   // time the proper moves source according to the type of node. In the default case
@@ -351,16 +353,17 @@ int64_t perft(Position& pos, Depth depth) {
 /// variables, and calls id_loop(). It returns false when a "quit" command is
 /// received during the search.
 
 /// variables, and calls id_loop(). It returns false when a "quit" command is
 /// received during the search.
 
-bool think(Position& pos, const SearchLimits& limits, Move searchMoves[]) {
+void think() {
 
   static Book book; // Defined static to initialize the PRNG only once
 
 
   static Book book; // Defined static to initialize the PRNG only once
 
+  Position& pos = *RootPosition;
+
   // Save "search start" time and reset elapsed time to zero
   elapsed_search_time(get_system_time());
 
   // Initialize global search-related variables
   // Save "search start" time and reset elapsed time to zero
   elapsed_search_time(get_system_time());
 
   // Initialize global search-related variables
-  StopOnPonderhit = StopRequest = QuitRequest = AspirationFailLow = false;
-  Limits = limits;
+  StopOnPonderhit = StopRequest = AspirationFailLow = false;
 
   // Set output stream mode: normal or chess960. Castling notation is different
   cout << set960(pos.is_chess960());
 
   // Set output stream mode: normal or chess960. Castling notation is different
   cout << set960(pos.is_chess960());
@@ -374,11 +377,11 @@ bool think(Position& pos, const SearchLimits& limits, Move searchMoves[]) {
       Move bookMove = book.probe(pos, Options["Best Book Move"].value<bool>());
       if (bookMove != MOVE_NONE)
       {
       Move bookMove = book.probe(pos, Options["Best Book Move"].value<bool>());
       if (bookMove != MOVE_NONE)
       {
-          if (Limits.ponder)
-              wait_for_stop_or_ponderhit();
+          if (!StopRequest && (Limits.ponder || Limits.infinite))
+              Threads.wait_for_stop_or_ponderhit();
 
           cout << "bestmove " << bookMove << endl;
 
           cout << "bestmove " << bookMove << endl;
-          return !QuitRequest;
+          return;
       }
   }
 
       }
   }
 
@@ -432,16 +435,9 @@ bool think(Position& pos, const SearchLimits& limits, Move searchMoves[]) {
   else
       Threads.set_timer(100);
 
   else
       Threads.set_timer(100);
 
-  // Start async mode to catch UCI commands sent to us while searching,
-  // like "quit", "stop", etc.
-  Threads.start_listener();
-
   // We're ready to start thinking. Call the iterative deepening loop function
   Move ponderMove = MOVE_NONE;
   // We're ready to start thinking. Call the iterative deepening loop function
   Move ponderMove = MOVE_NONE;
-  Move bestMove = id_loop(pos, searchMoves, &ponderMove);
-
-  // From now on any UCI command will be read in-sync with Threads.getline()
-  Threads.stop_listener();
+  Move bestMove = id_loop(pos, &SearchMoves[0], &ponderMove);
 
   // Stop timer, no need to check for available time any more
   Threads.set_timer(0);
 
   // Stop timer, no need to check for available time any more
   Threads.set_timer(0);
@@ -469,7 +465,7 @@ bool think(Position& pos, const SearchLimits& limits, Move searchMoves[]) {
   // we are pondering or in infinite search, we shouldn't print the best move
   // before we are told to do so.
   if (!StopRequest && (Limits.ponder || Limits.infinite))
   // we are pondering or in infinite search, we shouldn't print the best move
   // before we are told to do so.
   if (!StopRequest && (Limits.ponder || Limits.infinite))
-      wait_for_stop_or_ponderhit();
+      Threads.wait_for_stop_or_ponderhit();
 
   // Could be MOVE_NONE when searching on a stalemate position
   cout << "bestmove " << bestMove;
 
   // Could be MOVE_NONE when searching on a stalemate position
   cout << "bestmove " << bestMove;
@@ -480,8 +476,6 @@ bool think(Position& pos, const SearchLimits& limits, Move searchMoves[]) {
       cout << " ponder " << ponderMove;
 
   cout << endl;
       cout << " ponder " << ponderMove;
 
   cout << endl;
-
-  return !QuitRequest;
 }
 
 
 }
 
 
@@ -1902,26 +1896,6 @@ split_point_start: // At split points actual search starts from here
   }
 
 
   }
 
 
-  // wait_for_stop_or_ponderhit() is called when the maximum depth is reached
-  // while the program is pondering. The point is to work around a wrinkle in
-  // the UCI protocol: When pondering, the engine is not allowed to give a
-  // "bestmove" before the GUI sends it a "stop" or "ponderhit" command.
-  // We simply wait here until one of these commands (that raise StopRequest) is
-  // sent, and return, after which the bestmove and pondermove will be printed.
-
-  void wait_for_stop_or_ponderhit() {
-
-    string cmd;
-    StopOnPonderhit = true;
-
-    while (!StopRequest)
-    {
-        Threads.getline(cmd);
-        do_uci_async_cmd(cmd);
-    }
-  }
-
-
   // When playing with strength handicap choose best move among the MultiPV set
   // using a statistical rule dependent on SkillLevel. Idea by Heinz van Saanen.
 
   // When playing with strength handicap choose best move among the MultiPV set
   // using a statistical rule dependent on SkillLevel. Idea by Heinz van Saanen.
 
@@ -2164,15 +2138,34 @@ void Thread::idle_loop(SplitPoint* sp) {
 }
 
 
 }
 
 
-// do_uci_async_cmd() is called by listener thread when in async mode and 'cmd'
-// input line is received from the GUI.
+// ThreadsManager::wait_for_stop_or_ponderhit() is called when the maximum depth
+// is reached while the program is pondering. The point is to work around a wrinkle
+// in the UCI protocol: When pondering, the engine is not allowed to give a
+// "bestmove" before the GUI sends it a "stop" or "ponderhit" command.
+// We simply wait here until one of these commands (that raise StopRequest) is
+// sent, and return, after which the bestmove and pondermove will be printed.
+
+void ThreadsManager::wait_for_stop_or_ponderhit() {
+
+  StopOnPonderhit = true;
+
+  Thread& main = threads[0];
+
+  lock_grab(&main.sleepLock);
+
+  while (!StopRequest)
+      cond_wait(&main.sleepCond, &main.sleepLock);
+
+  lock_release(&main.sleepLock);
+}
+
 
 
-void do_uci_async_cmd(const std::string& cmd) {
+// uci_async_command() is called when a 'cmd' input line is received from the
+// GUI while searching.
 
 
-  if (cmd == "quit")
-      QuitRequest = StopRequest = true;
+void uci_async_command(const std::string& cmd) {
 
 
-  else if (cmd == "stop")
+  if (cmd == "quit" || cmd == "stop")
       StopRequest = true;
 
   else if (cmd == "ponderhit")
       StopRequest = true;
 
   else if (cmd == "ponderhit")
index 757aeb002ae110896c7f89d0b0afb019da1e523e..3262c757cbe05aa4ff50e07ad3711ac3ec453aab 100644 (file)
 #if !defined(SEARCH_H_INCLUDED)
 #define SEARCH_H_INCLUDED
 
 #if !defined(SEARCH_H_INCLUDED)
 #define SEARCH_H_INCLUDED
 
-#include <cstring>
-
 #include "move.h"
 #include "types.h"
 
 #include "move.h"
 #include "types.h"
 
+#include <vector>
+
 class Position;
 struct SplitPoint;
 
 class Position;
 struct SplitPoint;
 
@@ -53,21 +53,19 @@ struct SearchStack {
 
 struct SearchLimits {
 
 
 struct SearchLimits {
 
-  SearchLimits() { memset(this, 0, sizeof(SearchLimits)); }
-
-  SearchLimits(int t, int i, int mtg, int mt, int md, int mn, bool inf, bool pon)
-              : time(t), increment(i), movesToGo(mtg), maxTime(mt), maxDepth(md),
-                maxNodes(mn), infinite(inf), ponder(pon) {}
-
   bool useTimeManagement() const { return !(maxTime | maxDepth | maxNodes | infinite); }
 
   int time, increment, movesToGo, maxTime, maxDepth, maxNodes, infinite, ponder;
 };
 
   bool useTimeManagement() const { return !(maxTime | maxDepth | maxNodes | infinite); }
 
   int time, increment, movesToGo, maxTime, maxDepth, maxNodes, infinite, ponder;
 };
 
+extern SearchLimits Limits;
+extern std::vector<Move> SearchMoves;
+extern Position* RootPosition;
+
 extern void init_search();
 extern int64_t perft(Position& pos, Depth depth);
 extern void init_search();
 extern int64_t perft(Position& pos, Depth depth);
-extern bool think(Position& pos, const SearchLimits& limits, Move searchMoves[]);
-extern void do_uci_async_cmd(const std::string& cmd);
+extern void think();
+extern void uci_async_command(const std::string& cmd);
 extern void do_timer_event();
 
 #endif // !defined(SEARCH_H_INCLUDED)
 extern void do_timer_event();
 
 #endif // !defined(SEARCH_H_INCLUDED)
index 4a37d393edff4ca6f83eaa9a7f86917ec8dcc6ef..57faa6e90bb39b53ccff0a3cf3f5c6e84ef6f225 100644 (file)
@@ -38,10 +38,10 @@ namespace { extern "C" {
   void* start_routine(void* thread) {
 #endif
 
   void* start_routine(void* thread) {
 #endif
 
-    if (((Thread*)thread)->threadID == MAX_THREADS)
-        ((Thread*)thread)->listener_loop();
+    if (((Thread*)thread)->threadID == 0)
+        ((Thread*)thread)->main_loop();
 
 
-    else if (((Thread*)thread)->threadID == MAX_THREADS + 1)
+    else if (((Thread*)thread)->threadID == MAX_THREADS)
         ((Thread*)thread)->timer_loop();
     else
         ((Thread*)thread)->idle_loop(NULL);
         ((Thread*)thread)->timer_loop();
     else
         ((Thread*)thread)->idle_loop(NULL);
@@ -124,7 +124,7 @@ void ThreadsManager::set_size(int cnt) {
 
   activeThreads = cnt;
 
 
   activeThreads = cnt;
 
-  for (int i = 0; i < MAX_THREADS; i++)
+  for (int i = 1; i < MAX_THREADS; i++) // Ignore main thread
       if (i < activeThreads)
       {
           // Dynamically allocate pawn and material hash tables according to the
       if (i < activeThreads)
       {
           // Dynamically allocate pawn and material hash tables according to the
@@ -147,14 +147,14 @@ void ThreadsManager::set_size(int cnt) {
 
 void ThreadsManager::init() {
 
 
 void ThreadsManager::init() {
 
-  // Initialize sleep condition used to block waiting for GUI input
+  // Initialize sleep condition used to block waiting for end of searching
   cond_init(&sleepCond);
 
   // Initialize threads lock, used when allocating slaves during splitting
   lock_init(&threadsLock);
 
   // Initialize sleep and split point locks
   cond_init(&sleepCond);
 
   // Initialize threads lock, used when allocating slaves during splitting
   lock_init(&threadsLock);
 
   // Initialize sleep and split point locks
-  for (int i = 0; i < MAX_THREADS + 2; i++)
+  for (int i = 0; i <= MAX_THREADS; i++)
   {
       lock_init(&threads[i].sleepLock);
       cond_init(&threads[i].sleepCond);
   {
       lock_init(&threads[i].sleepLock);
       cond_init(&threads[i].sleepCond);
@@ -164,15 +164,14 @@ void ThreadsManager::init() {
   }
 
   // Initialize main thread's associated data
   }
 
   // Initialize main thread's associated data
-  threads[0].is_searching = true;
-  threads[0].threadID = 0;
-  set_size(1); // This makes all the threads but the main to go to sleep
+  threads[0].pawnTable.init();
+  threads[0].materialTable.init();
 
 
-  // Create and launch all the threads but the main that is already running,
-  // threads will go immediately to sleep.
-  for (int i = 1; i < MAX_THREADS + 2; i++)
+  // Create and launch all the threads, threads will go immediately to sleep
+  for (int i = 0; i <= MAX_THREADS; i++)
   {
       threads[i].is_searching = false;
   {
       threads[i].is_searching = false;
+      threads[i].do_sleep = true;
       threads[i].threadID = i;
 
 #if defined(_MSC_VER)
       threads[i].threadID = i;
 
 #if defined(_MSC_VER)
@@ -195,21 +194,18 @@ void ThreadsManager::init() {
 
 void ThreadsManager::exit() {
 
 
 void ThreadsManager::exit() {
 
-  for (int i = 0; i < MAX_THREADS + 2; i++)
+  for (int i = 0; i <= MAX_THREADS; i++)
   {
   {
-      if (i != 0)
-      {
-          threads[i].do_terminate = true;
-          threads[i].wake_up();
+      threads[i].do_terminate = true;
+      threads[i].wake_up();
 
 
-          // Wait for slave termination
+      // Wait for slave termination
 #if defined(_MSC_VER)
 #if defined(_MSC_VER)
-          WaitForSingleObject(threads[i].handle, 0);
-          CloseHandle(threads[i].handle);
+      WaitForSingleObject(threads[i].handle, 0);
+      CloseHandle(threads[i].handle);
 #else
 #else
-          pthread_join(threads[i].handle, NULL);
+      pthread_join(threads[i].handle, NULL);
 #endif
 #endif
-      }
 
       // Now we can safely destroy locks and wait conditions
       lock_destroy(&threads[i].sleepLock);
 
       // Now we can safely destroy locks and wait conditions
       lock_destroy(&threads[i].sleepLock);
@@ -387,7 +383,7 @@ void Thread::timer_loop() {
 
 void ThreadsManager::set_timer(int msec) {
 
 
 void ThreadsManager::set_timer(int msec) {
 
-  Thread& timer = threads[MAX_THREADS + 1];
+  Thread& timer = threads[MAX_THREADS];
 
   lock_grab(&timer.sleepLock);
   timer.maxPly = msec;
 
   lock_grab(&timer.sleepLock);
   timer.maxPly = msec;
@@ -396,113 +392,57 @@ void ThreadsManager::set_timer(int msec) {
 }
 
 
 }
 
 
-// Thread::listener_loop() is where the listener thread, used for I/O, waits for
-// input. When is_searching is false then input is read in sync with main thread
-// (that blocks), otherwise the listener thread reads any input asynchronously
-// and processes the input line calling do_uci_async_cmd().
+// Thread::main_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::listener_loop() {
-
-  std::string cmd;
+void Thread::main_loop() {
 
   while (true)
   {
       lock_grab(&sleepLock);
 
 
   while (true)
   {
       lock_grab(&sleepLock);
 
-      Threads.inputLine = cmd;
-      do_sleep = !is_searching;
+      do_sleep = true; // Always return to sleep after a search
+
+      is_searching = false;
 
 
-      // Here the thread is parked in sync mode after a line has been read
-      while (do_sleep && !do_terminate) // Catches spurious wake ups
+      while (do_sleep && !do_terminate)
       {
       {
-          cond_signal(&Threads.sleepCond);   // Wake up main thread
-          cond_wait(&sleepCond, &sleepLock); // Sleep here
+          cond_signal(&Threads.sleepCond); // Wake up UI thread if needed
+          cond_wait(&sleepCond, &sleepLock);
       }
 
       }
 
+      is_searching = true;
+
       lock_release(&sleepLock);
 
       if (do_terminate)
           return;
 
       lock_release(&sleepLock);
 
       if (do_terminate)
           return;
 
-      if (!std::getline(std::cin, cmd)) // Block waiting for input
-          cmd = "quit";
-
-      lock_grab(&sleepLock);
-
-      // If we are in async mode then process the command now
-      if (is_searching)
-      {
-          // Command "quit" is the last one received by the GUI, so park the
-          // thread waiting for exiting. Also, after a "stop", for instance on a
-          // ponder miss, GUI can immediately send the new position to search,
-          // so return to in-sync mode to avoid discarding good data.
-          if (cmd == "quit" || cmd == "stop")
-              is_searching = false;
-
-          do_uci_async_cmd(cmd);
-          cmd = ""; // Input has been consumed
-      }
-
-      lock_release(&sleepLock);
+      think(); // Search entry point
   }
 }
 
 
   }
 }
 
 
-// ThreadsManager::getline() is used by main thread to block and wait for input,
-// the behaviour mimics std::getline().
-
-void ThreadsManager::getline(std::string& cmd) {
-
-  Thread& listener = threads[MAX_THREADS];
+// 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.
 
 
-  lock_grab(&listener.sleepLock);
+void ThreadsManager::start_thinking(bool asyncMode) {
 
 
-  listener.is_searching = false; // Set sync mode
-
-  // If there is already some input to grab then skip without to wake up the
-  // listener. This can happen if after we send the "bestmove", the GUI sends
-  // a command that the listener buffers in inputLine before going to sleep.
-  if (inputLine.empty())
-  {
-      listener.do_sleep = false;
-      cond_signal(&listener.sleepCond); // Wake up listener thread
-
-      while (!listener.do_sleep)
-          cond_wait(&sleepCond, &listener.sleepLock); // Wait for input
-  }
-
-  cmd = inputLine;
-  inputLine = ""; // Input has been consumed
-
-  lock_release(&listener.sleepLock);
-}
-
-
-// ThreadsManager::start_listener() is called at the beginning of the search to
-// swith from sync behaviour (default) to async and so be able to read from UCI
-// while other threads are searching. This avoids main thread polling for input.
-
-void ThreadsManager::start_listener() {
-
-  Thread& listener = threads[MAX_THREADS];
-
-  lock_grab(&listener.sleepLock);
-  listener.is_searching = true;
-  listener.do_sleep = false;
-  cond_signal(&listener.sleepCond); // Wake up listener thread
-  lock_release(&listener.sleepLock);
-}
+  Thread& main = threads[0];
 
 
+  lock_grab(&main.sleepLock);
 
 
-// ThreadsManager::stop_listener() is called before to send "bestmove" to GUI to
-// return to in-sync behaviour. This is needed because while in async mode any
-// command is discarded without being processed (except for a very few ones).
+  // Wait main thread has finished before to launch a new search
+  while (!main.do_sleep)
+      cond_wait(&sleepCond, &main.sleepLock);
 
 
-void ThreadsManager::stop_listener() {
+  main.do_sleep = false;
+  cond_signal(&main.sleepCond); // Wake up main thread
 
 
-  Thread& listener = threads[MAX_THREADS];
+  if (!asyncMode)
+      cond_wait(&sleepCond, &main.sleepLock);
 
 
-  lock_grab(&listener.sleepLock);
-  listener.is_searching = false;
-  lock_release(&listener.sleepLock);
+  lock_release(&main.sleepLock);
 }
 }
index e6d400942c3a3cf9102496f783d6a06473fe4b46..df42c5aa756d0ff11ceb9cc34e720c5827d2ae8e 100644 (file)
@@ -27,6 +27,7 @@
 #include "movepick.h"
 #include "pawns.h"
 #include "position.h"
 #include "movepick.h"
 #include "pawns.h"
 #include "position.h"
+#include "search.h"
 
 const int MAX_THREADS = 32;
 const int MAX_ACTIVE_SPLIT_POINTS = 8;
 
 const int MAX_THREADS = 32;
 const int MAX_ACTIVE_SPLIT_POINTS = 8;
@@ -69,7 +70,7 @@ struct Thread {
   bool cutoff_occurred() const;
   bool is_available_to(int master) const;
   void idle_loop(SplitPoint* sp);
   bool cutoff_occurred() const;
   bool is_available_to(int master) const;
   void idle_loop(SplitPoint* sp);
-  void listener_loop();
+  void main_loop();
   void timer_loop();
 
   SplitPoint splitPoints[MAX_ACTIVE_SPLIT_POINTS];
   void timer_loop();
 
   SplitPoint splitPoints[MAX_ACTIVE_SPLIT_POINTS];
@@ -116,10 +117,9 @@ public:
   bool available_slave_exists(int master) const;
   bool split_point_finished(SplitPoint* sp) const;
 
   bool available_slave_exists(int master) const;
   bool split_point_finished(SplitPoint* sp) const;
 
-  void getline(std::string& cmd);
-  void start_listener();
-  void stop_listener();
+  void start_thinking(bool asyncMode = true);
   void set_timer(int msec);
   void set_timer(int msec);
+  void wait_for_stop_or_ponderhit();
 
   template <bool Fake>
   Value split(Position& pos, SearchStack* ss, Value alpha, Value beta, Value bestValue,
 
   template <bool Fake>
   Value split(Position& pos, SearchStack* ss, Value alpha, Value beta, Value bestValue,
@@ -134,7 +134,6 @@ private:
   int activeThreads;
   bool useSleepingThreads;
   WaitCondition sleepCond;
   int activeThreads;
   bool useSleepingThreads;
   WaitCondition sleepCond;
-  std::string inputLine;
 };
 
 extern ThreadsManager Threads;
 };
 
 extern ThreadsManager Threads;
index ff804f97a28476c3e644bd41f3ec71a744a066c7..ebb74d439f0bb3d852db7f45e488af8575e5e4cf 100644 (file)
@@ -45,7 +45,7 @@ namespace {
 
   void set_option(istringstream& up);
   void set_position(Position& pos, istringstream& up);
 
   void set_option(istringstream& up);
   void set_position(Position& pos, istringstream& up);
-  bool go(Position& pos, istringstream& up);
+  void go(Position& pos, istringstream& up);
   void perft(Position& pos, istringstream& up);
 }
 
   void perft(Position& pos, istringstream& up);
 }
 
@@ -61,22 +61,22 @@ void uci_loop() {
   string cmd, token;
   bool quit = false;
 
   string cmd, token;
   bool quit = false;
 
-  while (!quit)
+  while (!quit && getline(cin, cmd))
   {
   {
-      Threads.getline(cmd);
-
       istringstream is(cmd);
 
       is >> skipws >> token;
 
       istringstream is(cmd);
 
       is >> skipws >> token;
 
-      if (token == "quit")
-          quit = true;
+      quit = (token == "quit");
 
 
-      else if (token == "stop")
-      { /* avoid to reply "Unknown command: stop" */ }
+      if (token == "quit" || token == "stop" || token == "ponderhit")
+      {
+          uci_async_command(token);
+          Threads[0].wake_up(); // In case is waiting for stop or ponderhit
+      }
 
       else if (token == "go")
 
       else if (token == "go")
-          quit = !go(pos, is);
+          go(pos, is);
 
       else if (token == "ucinewgame")
           pos.from_fen(StarFEN, false);
 
       else if (token == "ucinewgame")
           pos.from_fen(StarFEN, false);
@@ -190,19 +190,21 @@ namespace {
   // string, and then calls think(). Returns false if a quit command
   // is received while thinking, true otherwise.
 
   // string, and then calls think(). Returns false if a quit command
   // is received while thinking, true otherwise.
 
-  bool go(Position& pos, istringstream& is) {
+  void go(Position& pos, istringstream& is) {
 
     string token;
 
     string token;
-    SearchLimits limits;
-    std::vector<Move> searchMoves;
     int time[] = { 0, 0 }, inc[] = { 0, 0 };
 
     int time[] = { 0, 0 }, inc[] = { 0, 0 };
 
+    memset(&Limits, 0, sizeof(SearchLimits));
+    SearchMoves.clear();
+    RootPosition = &pos;
+
     while (is >> token)
     {
         if (token == "infinite")
     while (is >> token)
     {
         if (token == "infinite")
-            limits.infinite = true;
+            Limits.infinite = true;
         else if (token == "ponder")
         else if (token == "ponder")
-            limits.ponder = true;
+            Limits.ponder = true;
         else if (token == "wtime")
             is >> time[WHITE];
         else if (token == "btime")
         else if (token == "wtime")
             is >> time[WHITE];
         else if (token == "btime")
@@ -212,23 +214,23 @@ namespace {
         else if (token == "binc")
             is >> inc[BLACK];
         else if (token == "movestogo")
         else if (token == "binc")
             is >> inc[BLACK];
         else if (token == "movestogo")
-            is >> limits.movesToGo;
+            is >> Limits.movesToGo;
         else if (token == "depth")
         else if (token == "depth")
-            is >> limits.maxDepth;
+            is >> Limits.maxDepth;
         else if (token == "nodes")
         else if (token == "nodes")
-            is >> limits.maxNodes;
+            is >> Limits.maxNodes;
         else if (token == "movetime")
         else if (token == "movetime")
-            is >> limits.maxTime;
+            is >> Limits.maxTime;
         else if (token == "searchmoves")
             while (is >> token)
         else if (token == "searchmoves")
             while (is >> token)
-                searchMoves.push_back(move_from_uci(pos, token));
+                SearchMoves.push_back(move_from_uci(pos, token));
     }
 
     }
 
-    searchMoves.push_back(MOVE_NONE);
-    limits.time = time[pos.side_to_move()];
-    limits.increment = inc[pos.side_to_move()];
+    SearchMoves.push_back(MOVE_NONE);
+    Limits.time = time[pos.side_to_move()];
+    Limits.increment = inc[pos.side_to_move()];
 
 
-    return think(pos, limits, &searchMoves[0]);
+    Threads.start_thinking();
   }
 
 
   }