mirror of
https://github.com/opelly27/Stockfish.git
synced 2026-05-20 06:17:49 +00:00
Rewrite async I/O
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>
This commit is contained in:
+39
-46
@@ -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
|
||||
@@ -162,9 +166,8 @@ namespace {
|
||||
int MultiPV, UCIMultiPV, MultiPVIdx;
|
||||
|
||||
// Time management variables
|
||||
volatile 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,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.
|
||||
|
||||
bool think(Position& pos, const SearchLimits& limits, Move searchMoves[]) {
|
||||
void think() {
|
||||
|
||||
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
|
||||
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());
|
||||
@@ -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)
|
||||
{
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -432,16 +435,9 @@ bool think(Position& pos, const SearchLimits& limits, Move searchMoves[]) {
|
||||
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;
|
||||
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);
|
||||
@@ -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))
|
||||
wait_for_stop_or_ponderhit();
|
||||
Threads.wait_for_stop_or_ponderhit();
|
||||
|
||||
// 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;
|
||||
|
||||
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.
|
||||
|
||||
@@ -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 do_uci_async_cmd(const std::string& cmd) {
|
||||
void ThreadsManager::wait_for_stop_or_ponderhit() {
|
||||
|
||||
if (cmd == "quit")
|
||||
QuitRequest = StopRequest = true;
|
||||
StopOnPonderhit = true;
|
||||
|
||||
else if (cmd == "stop")
|
||||
Thread& main = threads[0];
|
||||
|
||||
lock_grab(&main.sleepLock);
|
||||
|
||||
while (!StopRequest)
|
||||
cond_wait(&main.sleepCond, &main.sleepLock);
|
||||
|
||||
lock_release(&main.sleepLock);
|
||||
}
|
||||
|
||||
|
||||
// uci_async_command() is called when a 'cmd' input line is received from the
|
||||
// GUI while searching.
|
||||
|
||||
void uci_async_command(const std::string& cmd) {
|
||||
|
||||
if (cmd == "quit" || cmd == "stop")
|
||||
StopRequest = true;
|
||||
|
||||
else if (cmd == "ponderhit")
|
||||
|
||||
Reference in New Issue
Block a user