]> git.sesse.net Git - stockfish/blobdiff - src/search.cpp
Rewrite async I/O
[stockfish] / src / search.cpp
index bdb64ab5ea1cd300e8f69b3ccffb12bca37633a4..560f64f1778e0e19d0a54be6cfe56e05c85a1715 100644 (file)
@@ -43,6 +43,10 @@ using std::cout;
 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
@@ -150,7 +154,7 @@ namespace {
 
   // Easy move margin. An easy move candidate must be at least this much
   // better than the second best move.
-  const Value EasyMoveMargin = Value(0x200);
+  const Value EasyMoveMargin = Value(0x150);
 
 
   /// Namespace variables
@@ -162,9 +166,8 @@ namespace {
   int MultiPV, UCIMultiPV, MultiPVIdx;
 
   // Time management variables
-  bool StopOnPonderhit, FirstRootMove, StopRequest, QuitRequest, AspirationFailLow;
+  volatile bool StopOnPonderhit, FirstRootMove, StopRequest, AspirationFailLow;
   TimeManager TimeMgr;
-  SearchLimits Limits;
 
   // 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);
-  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
@@ -351,17 +353,19 @@ int64_t perft(Position& pos, Depth depth) {
 /// 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
 
-  // Initialize global search-related variables
-  StopOnPonderhit = StopRequest = QuitRequest = AspirationFailLow = false;
+  Position& pos = *RootPosition;
+
+  // Save "search start" time and reset elapsed time to zero
   elapsed_search_time(get_system_time());
-  Limits = limits;
-  TimeMgr.init(Limits, pos.startpos_ply_counter());
 
-  // Set output stream in normal or chess960 mode
+  // Initialize global search-related variables
+  StopOnPonderhit = StopRequest = AspirationFailLow = false;
+
+  // Set output stream mode: normal or chess960. Castling notation is different
   cout << set960(pos.is_chess960());
 
   // Look for a book move
@@ -373,25 +377,15 @@ bool think(Position& pos, const SearchLimits& limits, Move searchMoves[]) {
       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;
-          return !QuitRequest;
+          return;
       }
   }
 
-  // Set best timer interval to avoid lagging under time pressure. Timer is
-  // used to check for remaining available thinking time.
-  if (TimeMgr.available_time())
-      Threads.set_timer(std::min(100, std::max(TimeMgr.available_time() / 8, 20)));
-  else
-      Threads.set_timer(100);
-
-  // Read UCI options
-  UCIMultiPV = Options["MultiPV"].value<int>();
-  SkillLevel = Options["Skill Level"].value<int>();
-
+  // Read UCI options: GUI could change UCI parameters during the game
   read_evaluation_uci_options(pos.side_to_move());
   Threads.read_uci_options();
 
@@ -404,22 +398,14 @@ bool think(Position& pos, const SearchLimits& limits, Move searchMoves[]) {
       TT.clear();
   }
 
+  UCIMultiPV = Options["MultiPV"].value<int>();
+  SkillLevel = Options["Skill Level"].value<int>();
+
   // Do we have to play with skill handicap? In this case enable MultiPV that
   // we will use behind the scenes to retrieve a set of possible moves.
   SkillLevelEnabled = (SkillLevel < 20);
   MultiPV = (SkillLevelEnabled ? std::max(UCIMultiPV, 4) : UCIMultiPV);
 
-  // Wake up needed threads and reset maxPly counter
-  for (int i = 0; i < Threads.size(); i++)
-  {
-      Threads[i].wake_up();
-      Threads[i].maxPly = 0;
-  }
-
-  // Start async mode to catch UCI commands sent to us while searching,
-  // like "quit", "stop", etc.
-  Threads.start_listener();
-
   // Write current search header to log file
   if (Options["Use Search Log"].value<bool>())
   {
@@ -433,9 +419,31 @@ bool think(Position& pos, const SearchLimits& limits, Move searchMoves[]) {
           << endl;
   }
 
+  // Wake up needed threads and reset maxPly counter
+  for (int i = 0; i < Threads.size(); i++)
+  {
+      Threads[i].maxPly = 0;
+      Threads[i].wake_up();
+  }
+
+  // Set best timer interval to avoid lagging under time pressure. Timer is
+  // used to check for remaining available thinking time.
+   TimeMgr.init(Limits, pos.startpos_ply_counter());
+
+  if (TimeMgr.available_time())
+      Threads.set_timer(std::min(100, std::max(TimeMgr.available_time() / 8, 20)));
+  else
+      Threads.set_timer(100);
+
   // We're ready to start thinking. Call the iterative deepening loop function
   Move ponderMove = MOVE_NONE;
-  Move bestMove = id_loop(pos, searchMoves, &ponderMove);
+  Move bestMove = id_loop(pos, &SearchMoves[0], &ponderMove);
+
+  // Stop timer, no need to check for available time any more
+  Threads.set_timer(0);
+
+  // This makes all the slave threads to go to sleep, if not already sleeping
+  Threads.set_size(1);
 
   // Write current search final statistics to log file
   if (Options["Use Search Log"].value<bool>())
@@ -453,19 +461,11 @@ bool think(Position& pos, const SearchLimits& limits, Move searchMoves[]) {
       pos.undo_move(bestMove); // Return from think() with unchanged position
   }
 
-  // This makes all the threads to go to sleep
-  Threads.set_size(1);
-
-  // From now on any UCI command will be read in-sync with Threads.getline()
-  Threads.stop_listener();
-
-  // Stop timer, no need to check for available time any more
-  Threads.set_timer(0);
-
-  // If we are pondering or in infinite search, we shouldn't print the
-  // best move before we are told to do so.
-  if (Limits.ponder || Limits.infinite)
-      wait_for_stop_or_ponderhit();
+  // When we reach max depth we arrive here even without a StopRequest, but if
+  // 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))
+      Threads.wait_for_stop_or_ponderhit();
 
   // Could be MOVE_NONE when searching on a stalemate position
   cout << "bestmove " << bestMove;
@@ -476,8 +476,6 @@ bool think(Position& pos, const SearchLimits& limits, Move searchMoves[]) {
       cout << " ponder " << ponderMove;
 
   cout << endl;
-
-  return !QuitRequest;
 }
 
 
@@ -493,16 +491,17 @@ namespace {
     Value bestValues[PLY_MAX_PLUS_2];
     int bestMoveChanges[PLY_MAX_PLUS_2];
     int depth, aspirationDelta;
-    Value value, alpha, beta;
-    Move bestMove, easyMove, skillBest, skillPonder;
+    Value bestValue, alpha, beta;
+    Move bestMove, skillBest, skillPonder;
+    bool bestMoveNeverChanged = true;
 
     // Initialize stuff before a new search
     memset(ss, 0, 4 * sizeof(SearchStack));
     TT.new_search();
     H.clear();
-    *ponderMove = bestMove = easyMove = skillBest = skillPonder = MOVE_NONE;
+    *ponderMove = bestMove = skillBest = skillPonder = MOVE_NONE;
     depth = aspirationDelta = 0;
-    value = alpha = -VALUE_INFINITE, beta = VALUE_INFINITE;
+    bestValue = alpha = -VALUE_INFINITE, beta = VALUE_INFINITE;
     ss->currentMove = MOVE_NULL; // Hack to skip update gains
 
     // Moves to search are verified and copied
@@ -552,7 +551,7 @@ namespace {
             do {
                 // Search starts from ss+1 to allow referencing (ss-1). This is
                 // needed by update gains and ss copy when splitting at Root.
-                value = search<Root>(pos, ss+1, alpha, beta, depth * ONE_PLY);
+                bestValue = search<Root>(pos, ss+1, alpha, beta, depth * ONE_PLY);
 
                 // Bring to front the best move. It is critical that sorting is
                 // done with a stable algorithm because all the values but the first
@@ -566,7 +565,7 @@ namespace {
                 // the fail high/low loop then reorder the PV moves, otherwise
                 // leave the last PV move in its position so to be searched again.
                 // Of course this is needed only in MultiPV search.
-                if (MultiPVIdx && value > alpha && value < beta)
+                if (MultiPVIdx && bestValue > alpha && bestValue < beta)
                     sort<RootMove>(Rml.begin(), Rml.begin() + MultiPVIdx);
 
                 // Write PV back to transposition table in case the relevant entries
@@ -584,7 +583,7 @@ namespace {
                 // if we have a fail high/low and we are deep in the search. UCI
                 // protocol requires to send all the PV lines also if are still
                 // to be searched and so refer to the previous search's score.
-                if ((value > alpha && value < beta) || elapsed_search_time() > 2000)
+                if ((bestValue > alpha && bestValue < beta) || elapsed_search_time() > 2000)
                     for (int i = 0; i < std::min(UCIMultiPV, (int)Rml.size()); i++)
                     {
                         bool updated = (i <= MultiPVIdx);
@@ -605,12 +604,12 @@ namespace {
 
                 // In case of failing high/low increase aspiration window and
                 // research, otherwise exit the fail high/low loop.
-                if (value >= beta)
+                if (bestValue >= beta)
                 {
                     beta = std::min(beta + aspirationDelta, VALUE_INFINITE);
                     aspirationDelta += aspirationDelta / 2;
                 }
-                else if (value <= alpha)
+                else if (bestValue <= alpha)
                 {
                     AspirationFailLow = true;
                     StopOnPonderhit = false;
@@ -621,13 +620,13 @@ namespace {
                 else
                     break;
 
-            } while (abs(value) < VALUE_KNOWN_WIN);
+            } while (abs(bestValue) < VALUE_KNOWN_WIN);
         }
 
         // Collect info about search result
         bestMove = Rml[0].pv[0];
         *ponderMove = Rml[0].pv[1];
-        bestValues[depth] = value;
+        bestValues[depth] = bestValue;
         bestMoveChanges[depth] = Rml.bestMoveChanges;
 
         // Skills: Do we need to pick now the best and the ponder moves ?
@@ -637,30 +636,16 @@ namespace {
         if (Options["Use Search Log"].value<bool>())
         {
             Log log(Options["Search Log Filename"].value<string>());
-            log << pretty_pv(pos, depth, value, elapsed_search_time(), &Rml[0].pv[0]) << endl;
+            log << pretty_pv(pos, depth, bestValue, elapsed_search_time(), &Rml[0].pv[0]) << endl;
         }
 
-        // Init easyMove at first iteration or drop it if differs from the best move
-        if (depth == 1 && (Rml.size() == 1 || Rml[0].score > Rml[1].score + EasyMoveMargin))
-            easyMove = bestMove;
-        else if (bestMove != easyMove)
-            easyMove = MOVE_NONE;
+        // Filter out startup noise when monitoring best move stability
+        if (depth > 2 && bestMoveChanges[depth])
+            bestMoveNeverChanged = false;
 
-        // Check for some early stop condition
-        if (!StopRequest && Limits.useTimeManagement())
+        // Do we have time for the next iteration? Can we stop searching now?
+        if (!StopRequest && !StopOnPonderhit && Limits.useTimeManagement())
         {
-            // Easy move: Stop search early if one move seems to be much better
-            // than the others or if there is only a single legal move. Also in
-            // the latter case search to some depth anyway to get a proper score.
-            if (   depth >= 7
-                && easyMove == bestMove
-                && (   Rml.size() == 1
-                    ||(   Rml[0].nodes > (pos.nodes_searched() * 85) / 100
-                       && elapsed_search_time() > TimeMgr.available_time() / 16)
-                    ||(   Rml[0].nodes > (pos.nodes_searched() * 98) / 100
-                       && elapsed_search_time() > TimeMgr.available_time() / 32)))
-                StopRequest = true;
-
             // Take in account some extra time if the best move has changed
             if (depth > 4 && depth < 50)
                 TimeMgr.pv_instability(bestMoveChanges[depth], bestMoveChanges[depth - 1]);
@@ -670,8 +655,25 @@ namespace {
             if (elapsed_search_time() > (TimeMgr.available_time() * 62) / 100)
                 StopRequest = true;
 
+            // Stop search early if one move seems to be much better than others
+            if (   depth >= 10
+                && !StopRequest
+                && (   bestMoveNeverChanged
+                    || elapsed_search_time() > (TimeMgr.available_time() * 40) / 100))
+            {
+                Value rBeta = bestValue - EasyMoveMargin;
+                (ss+1)->excludedMove = bestMove;
+                (ss+1)->skipNullMove = true;
+                Value v = search<NonPV>(pos, ss+1, rBeta - 1, rBeta, (depth * ONE_PLY) / 2);
+                (ss+1)->skipNullMove = false;
+                (ss+1)->excludedMove = MOVE_NONE;
+
+                if (v < rBeta)
+                    StopRequest = true;
+            }
+
             // If we are allowed to ponder do not stop the search now but keep pondering
-            if (StopRequest && Limits.ponder)
+            if (StopRequest && Limits.ponder) // FIXME Limits.ponder is racy
             {
                 StopRequest = false;
                 StopOnPonderhit = true;
@@ -722,7 +724,8 @@ namespace {
     ValueType vt;
     Value bestValue, value, oldAlpha;
     Value refinedValue, nullValue, futilityBase, futilityValue;
-    bool isPvMove, inCheck, singularExtensionNode, givesCheck, captureOrPromotion, dangerous;
+    bool isPvMove, inCheck, singularExtensionNode, givesCheck;
+    bool captureOrPromotion, dangerous, doFullDepthSearch;
     int moveCount = 0, playedMoveCount = 0;
     Thread& thread = Threads[pos.thread()];
     SplitPoint* sp = NULL;
@@ -1024,8 +1027,7 @@ split_point_start: // At split points actual search starts from here
                    << " currmovenumber " << moveCount + MultiPVIdx << endl;
       }
 
-      // At Root and at first iteration do a PV search on all the moves to score root moves
-      isPvMove = (PvNode && moveCount <= (RootNode && depth <= ONE_PLY ? MAX_MOVES : 1));
+      isPvMove = (PvNode && moveCount <= 1);
       givesCheck = pos.move_gives_check(move, ci);
       captureOrPromotion = pos.is_capture_or_promotion(move);
 
@@ -1049,11 +1051,11 @@ split_point_start: // At split points actual search starts from here
               Value rBeta = ttValue - int(depth);
               ss->excludedMove = move;
               ss->skipNullMove = true;
-              Value v = search<NonPV>(pos, ss, rBeta - 1, rBeta, depth / 2);
+              value = search<NonPV>(pos, ss, rBeta - 1, rBeta, depth / 2);
               ss->skipNullMove = false;
               ss->excludedMove = MOVE_NONE;
               ss->bestMove = MOVE_NONE;
-              if (v < rBeta)
+              if (value < rBeta)
                   ext = ONE_PLY;
           }
       }
@@ -1127,51 +1129,44 @@ split_point_start: // At split points actual search starts from here
       // Step 14. Make the move
       pos.do_move(move, st, ci, givesCheck);
 
-      // Step extra. pv search (only in PV nodes)
-      // The first move in list is the expected PV
-      if (isPvMove)
-          value = newDepth < ONE_PLY ? -qsearch<PV>(pos, ss+1, -beta, -alpha, DEPTH_ZERO)
-                                     : - search<PV>(pos, ss+1, -beta, -alpha, newDepth);
-      else
+      // Step 15. Reduced depth search (LMR). If the move fails high will be
+      // re-searched at full depth.
+      if (   depth > 3 * ONE_PLY
+          && !isPvMove
+          && !captureOrPromotion
+          && !dangerous
+          && !is_castle(move)
+          &&  ss->killers[0] != move
+          &&  ss->killers[1] != move)
       {
-          // Step 15. Reduced depth search
-          // If the move fails high will be re-searched at full depth.
-          bool doFullDepthSearch = true;
-
-          if (    depth > 3 * ONE_PLY
-              && !captureOrPromotion
-              && !dangerous
-              && !is_castle(move)
-              &&  ss->killers[0] != move
-              &&  ss->killers[1] != move
-              && (ss->reduction = reduction<PvNode>(depth, moveCount)) != DEPTH_ZERO)
-          {
-              Depth d = newDepth - ss->reduction;
-              alpha = SpNode ? sp->alpha : alpha;
+          ss->reduction = reduction<PvNode>(depth, moveCount);
+          Depth d = newDepth - ss->reduction;
+          alpha = SpNode ? sp->alpha : alpha;
 
-              value = d < ONE_PLY ? -qsearch<NonPV>(pos, ss+1, -(alpha+1), -alpha, DEPTH_ZERO)
-                                  : - search<NonPV>(pos, ss+1, -(alpha+1), -alpha, d);
+          value = d < ONE_PLY ? -qsearch<NonPV>(pos, ss+1, -(alpha+1), -alpha, DEPTH_ZERO)
+                              : - search<NonPV>(pos, ss+1, -(alpha+1), -alpha, d);
 
-              ss->reduction = DEPTH_ZERO;
-              doFullDepthSearch = (value > alpha);
-          }
+          doFullDepthSearch = (value > alpha && ss->reduction != DEPTH_ZERO);
+          ss->reduction = DEPTH_ZERO;
+      }
+      else
+          doFullDepthSearch = !isPvMove;
 
-          // Step 16. Full depth search
-          if (doFullDepthSearch)
-          {
-              alpha = SpNode ? sp->alpha : alpha;
-              value = newDepth < ONE_PLY ? -qsearch<NonPV>(pos, ss+1, -(alpha+1), -alpha, DEPTH_ZERO)
-                                         : - search<NonPV>(pos, ss+1, -(alpha+1), -alpha, newDepth);
-
-              // Step extra. pv search (only in PV nodes)
-              // Search only for possible new PV nodes, if instead value >= beta then
-              // parent node fails low with value <= alpha and tries another move.
-              if (PvNode && value > alpha && (RootNode || value < beta))
-                  value = newDepth < ONE_PLY ? -qsearch<PV>(pos, ss+1, -beta, -alpha, DEPTH_ZERO)
-                                             : - search<PV>(pos, ss+1, -beta, -alpha, newDepth);
-          }
+      // Step 16. Full depth search, when LMR is skipped or fails high
+      if (doFullDepthSearch)
+      {
+          alpha = SpNode ? sp->alpha : alpha;
+          value = newDepth < ONE_PLY ? -qsearch<NonPV>(pos, ss+1, -(alpha+1), -alpha, DEPTH_ZERO)
+                                     : - search<NonPV>(pos, ss+1, -(alpha+1), -alpha, newDepth);
       }
 
+      // Only for PV nodes do a full PV search on the first move or after a fail
+      // high, in the latter case search only if value < beta, otherwise let the
+      // parent node to fail low with value <= alpha and to try another move.
+      if (PvNode && (isPvMove || (value > alpha && (RootNode || value < beta))))
+          value = newDepth < ONE_PLY ? -qsearch<PV>(pos, ss+1, -beta, -alpha, DEPTH_ZERO)
+                                     : - search<PV>(pos, ss+1, -beta, -alpha, newDepth);
+
       // Step 17. Undo move
       pos.undo_move(move);
 
@@ -1780,6 +1775,7 @@ split_point_start: // At split points actual search starts from here
     return s.str();
   }
 
+
   // pv_to_uci() returns a string with information on the current PV line
   // formatted according to UCI specification.
 
@@ -1795,6 +1791,7 @@ split_point_start: // At split points actual search starts from here
     return s.str();
   }
 
+
   // depth_to_uci() returns a string with information on the current depth and
   // seldepth formatted according to UCI specification.
 
@@ -1845,6 +1842,7 @@ split_point_start: // At split points actual search starts from here
     return s.str();
   }
 
+
   // pretty_pv() creates a human-readable string from a position and a PV.
   // It is used to write search information to the log file (which is created
   // when the UCI parameter "Use Search Log" is "true").
@@ -1898,28 +1896,9 @@ 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.
+
   void do_skill_level(Move* best, Move* ponder) {
 
     assert(MultiPV > 1);
@@ -1998,6 +1977,7 @@ split_point_start: // At split points actual search starts from here
     return NULL;
   }
 
+
   // extract_pv_from_tt() builds a PV by adding moves from the transposition table.
   // We consider also failing high nodes and not only VALUE_TYPE_EXACT nodes. This
   // allow to always have a ponder move even when we fail high at root and also a
@@ -2032,6 +2012,7 @@ split_point_start: // At split points actual search starts from here
     do pos.undo_move(pv[--ply]); while (ply);
   }
 
+
   // insert_pv_in_tt() is called at the end of a search iteration, and inserts
   // the PV back into the TT. This makes sure the old PV moves are searched
   // first, even if the old TT entries have been overwritten.
@@ -2062,20 +2043,8 @@ split_point_start: // At split points actual search starts from here
 
     do pos.undo_move(pv[--ply]); while (ply);
   }
-} // namespace
-
-
-// Little helper used by idle_loop() to check that all the slave threads of a
-// split point have finished searching.
 
-static bool all_slaves_finished(SplitPoint* sp) {
-
-  for (int i = 0; i < Threads.size(); i++)
-      if (sp->is_slave[i])
-          return false;
-
-  return true;
-}
+} // namespace
 
 
 // Thread::idle_loop() is where the thread is parked when it has no work to do.
@@ -2105,7 +2074,7 @@ void Thread::idle_loop(SplitPoint* sp) {
           lock_grab(&sleepLock);
 
           // If we are master and all slaves have finished don't go to sleep
-          if (sp && all_slaves_finished(sp))
+          if (sp && Threads.split_point_finished(sp))
           {
               lock_release(&sleepLock);
               break;
@@ -2157,7 +2126,7 @@ void Thread::idle_loop(SplitPoint* sp) {
 
       // If this thread is the master of a split point and all slaves have
       // finished their work at this split point, return from the idle loop.
-      if (sp && all_slaves_finished(sp))
+      if (sp && Threads.split_point_finished(sp))
       {
           // Because sp->is_slave[] is reset under lock protection,
           // be sure sp->lock has been released before to return.
@@ -2169,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")
@@ -2200,8 +2188,8 @@ void do_timer_event() {
   static int lastInfoTime;
   int e = elapsed_search_time();
 
-  // Print debug information every second
-  if (get_system_time() - lastInfoTime >= 1000)
+  // Print debug information every one second
+  if (!lastInfoTime || get_system_time() - lastInfoTime >= 1000)
   {
       lastInfoTime = get_system_time();