From a88e762b4ea4a3696ecc0431237f54090a5aa1e2 Mon Sep 17 00:00:00 2001 From: Marco Costalba Date: Thu, 7 May 2009 12:45:46 +0200 Subject: [PATCH] Rewrite the way application exits Centralize in a single object all the global resources management and avoid a bunch of sparse exit() calls. This is more reliable and clean and more stick to C++ coding practices. No functional change. Signed-off-by: Marco Costalba --- src/application.cpp | 80 +++++++++++++++++++++++++++++++++++++++++++++ src/application.h | 46 ++++++++++++++++++++++++++ src/benchmark.cpp | 15 ++++----- src/book.cpp | 4 +-- src/evaluate.cpp | 2 ++ src/main.cpp | 68 +++++++++++++------------------------- src/material.cpp | 2 +- src/misc.h | 2 ++ src/pawns.cpp | 2 +- src/search.cpp | 39 +++++++++++----------- src/search.h | 2 +- src/tt.cpp | 2 +- src/uci.cpp | 78 +++++++++++++++++++------------------------ 13 files changed, 218 insertions(+), 124 deletions(-) create mode 100644 src/application.cpp create mode 100644 src/application.h diff --git a/src/application.cpp b/src/application.cpp new file mode 100644 index 00000000..b7ebbbb9 --- /dev/null +++ b/src/application.cpp @@ -0,0 +1,80 @@ +/* + Stockfish, a UCI chess playing engine derived from Glaurung 2.1 + Copyright (C) 2004-2008 Tord Romstad (Glaurung author) + Copyright (C) 2008 Marco Costalba + + Stockfish is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + Stockfish is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + + +//// +//// Includes +//// + +#include "bitboard.h" +#include "direction.h" +#include "endgame.h" +#include "evaluate.h" +#include "material.h" +#include "mersenne.h" +#include "misc.h" +#include "movepick.h" +#include "position.h" +#include "search.h" +#include "thread.h" +#include "ucioption.h" + + +/// Application class is in charge of initializing global resources +/// at startup and cleanly releases them when program terminates. + +Application::Application() { + + init_mersenne(); + init_direction_table(); + init_bitboards(); + init_uci_options(); + Position::init_zobrist(); + Position::init_piece_square_tables(); + MovePicker::init_phase_table(); + init_eval(1); + init_bitbases(); + init_threads(); + + // Make random number generation less deterministic, for book moves + for (int i = abs(get_system_time() % 10000); i > 0; i--) + genrand_int32(); +} + +Application::~Application() { + + stop_threads(); + quit_eval(); +} + +void Application::initialize() { + + instance(); +} + +Application& Application::instance() { + + static Application singleton; + return singleton; +} + +void Application::exit_with_failure() { + + exit(EXIT_FAILURE); // d'tor will be called automatically +} diff --git a/src/application.h b/src/application.h new file mode 100644 index 00000000..f7149384 --- /dev/null +++ b/src/application.h @@ -0,0 +1,46 @@ +/* + Stockfish, a UCI chess playing engine derived from Glaurung 2.1 + Copyright (C) 2004-2008 Tord Romstad (Glaurung author) + Copyright (C) 2008 Marco Costalba + + Stockfish is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + Stockfish is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + + +#if !defined(APPLICATION_H_INCLUDED) +#define APPLICATION_H_INCLUDED + + +/// Singleton class used to housekeep memory and global resources +/// so to be sure we always leave in a clean state. + +class Application { + + Application(); + Application(const Application&); + +public: + static void initialize(); + static void exit_with_failure(); + + ~Application(); + +private: + static Application& instance(); + + void init(); + void deallocateAll(); +}; + +#endif // !defined(APPLICATION_H_INCLUDED) diff --git a/src/benchmark.cpp b/src/benchmark.cpp index 9b1b4009..6e8357f2 100644 --- a/src/benchmark.cpp +++ b/src/benchmark.cpp @@ -79,15 +79,14 @@ void benchmark(const std::string& commandLine) { if (val < 4 || val > 1024) { std::cerr << "The hash table size must be between 4 and 1024" << std::endl; - exit(EXIT_FAILURE); + Application::exit_with_failure(); } csStr >> threads; csVal >> val; if (val < 1 || val > THREAD_MAX) { - std::cerr << "The number of threads must be between 1 and " << THREAD_MAX - << std::endl; - exit(EXIT_FAILURE); + std::cerr << "The number of threads must be between 1 and " << THREAD_MAX << std::endl; + Application::exit_with_failure(); } set_option_value("Hash", ttSize); set_option_value("Threads", threads); @@ -115,9 +114,8 @@ void benchmark(const std::string& commandLine) { std::ifstream fenFile(fileName.c_str()); if (!fenFile.is_open()) { - std::cerr << "Unable to open positions file " << fileName - << std::endl; - exit(EXIT_FAILURE); + std::cerr << "Unable to open positions file " << fileName << std::endl; + Application::exit_with_failure(); } std::string pos; while (fenFile.good()) @@ -141,7 +139,8 @@ void benchmark(const std::string& commandLine) { int dummy[2] = {0, 0}; Position pos(*it); std::cout << "\nProcessing position " << cnt << '/' << positions.size() << std::endl << std::endl; - think(pos, true, false, 0, dummy, dummy, 0, maxDepth, maxNodes, secsPerPos, moves); + if (!think(pos, true, false, 0, dummy, dummy, 0, maxDepth, maxNodes, secsPerPos, moves)) + break; totalNodes += nodes_searched(); } std::cout << "\nProcessing time (ms) " << get_system_time() - startTime << std::endl diff --git a/src/book.cpp b/src/book.cpp index c69eab73..c6e1e436 100644 --- a/src/book.cpp +++ b/src/book.cpp @@ -372,7 +372,7 @@ void Book::open(const string& fName) { if (!good()) { cerr << "Failed to open book file " << fileName << endl; - exit(EXIT_FAILURE); + Application::exit_with_failure(); } } @@ -489,7 +489,7 @@ void Book::read_entry(BookEntry& entry, int idx) { if (!good()) { cerr << "Failed to read book entry at index " << idx << endl; - exit(EXIT_FAILURE); + Application::exit_with_failure(); } } diff --git a/src/evaluate.cpp b/src/evaluate.cpp index c29fd5e6..d4fdac21 100644 --- a/src/evaluate.cpp +++ b/src/evaluate.cpp @@ -505,6 +505,8 @@ void quit_eval() { { delete PawnTable[i]; delete MaterialTable[i]; + PawnTable[i] = NULL; + MaterialTable[i] = NULL; } } diff --git a/src/main.cpp b/src/main.cpp index 01fb4786..36534ae3 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -20,32 +20,24 @@ // To profile with callgrind uncomment following line //#define USE_CALLGRIND + //// //// Includes //// #include +#include #include "benchmark.h" -#include "bitboard.h" -#include "direction.h" -#include "endgame.h" -#include "evaluate.h" -#include "material.h" -#include "mersenne.h" #include "misc.h" -#include "movepick.h" -#include "position.h" -#include "search.h" -#include "thread.h" #include "uci.h" -#include "ucioption.h" #ifdef USE_CALLGRIND #include #endif -using std::string; +using namespace std; + //// //// Functions @@ -54,54 +46,38 @@ using std::string; int main(int argc, char *argv[]) { // Disable IO buffering - std::cout.rdbuf()->pubsetbuf(NULL, 0); - std::cin.rdbuf()->pubsetbuf(NULL, 0); - - // Initialization - init_mersenne(); - init_direction_table(); - init_bitboards(); - init_uci_options(); - Position::init_zobrist(); - Position::init_piece_square_tables(); - MovePicker::init_phase_table(); - init_eval(1); - init_bitbases(); - init_threads(); + cout.rdbuf()->pubsetbuf(NULL, 0); + cin.rdbuf()->pubsetbuf(NULL, 0); + + // Initialization through global resources manager + Application::initialize(); #ifdef USE_CALLGRIND CALLGRIND_START_INSTRUMENTATION; #endif - // Make random number generation less deterministic, for book moves - for (int i = abs(get_system_time() % 10000); i > 0; i--) - genrand_int32(); - - // Process command line arguments - if (argc >= 2 && string(argv[1]) == "bench") + // Process command line arguments if any + if (argc > 1) { - if (argc < 4 || argc > 7) + if (string(argv[1]) != "bench" || argc < 4 || argc > 7) + cout << "Usage: stockfish bench " + << "[time = 60s] [fen positions file = default] " + << "[time, depth or node limited = time]" << endl; + else { - std::cout << "Usage: stockfish bench " - << "[time = 60s] [fen positions file = default] " - << "[time, depth or node limited = time]" - << std::endl; - exit(0); + string time = argc > 4 ? argv[4] : "60"; + string fen = argc > 5 ? argv[5] : "default"; + string lim = argc > 6 ? argv[6] : "time"; + benchmark(string(argv[2]) + " " + string(argv[3]) + " " + time + " " + fen + " " + lim); } - string time = argc > 4 ? argv[4] : "60"; - string fen = argc > 5 ? argv[5] : "default"; - string lim = argc > 6 ? argv[6] : "time"; - benchmark(string(argv[2]) + " " + string(argv[3]) + " " + time + " " + fen + " " + lim); return 0; } // Print copyright notice - std::cout << engine_name() << ". Copyright (C) " - << "2004-2008 Tord Romstad, Marco Costalba. " - << std::endl; + cout << engine_name() << ". Copyright (C) " + << "2004-2008 Tord Romstad, Marco Costalba. " << endl; // Enter UCI mode uci_main_loop(); - return 0; } diff --git a/src/material.cpp b/src/material.cpp index 442dab56..b5f85de1 100644 --- a/src/material.cpp +++ b/src/material.cpp @@ -90,7 +90,7 @@ MaterialInfoTable::MaterialInfoTable(unsigned int numOfEntries) { { std::cerr << "Failed to allocate " << (numOfEntries * sizeof(MaterialInfo)) << " bytes for material hash table." << std::endl; - exit(EXIT_FAILURE); + Application::exit_with_failure(); } clear(); } diff --git a/src/misc.h b/src/misc.h index aff7cf3e..8c0a1f05 100644 --- a/src/misc.h +++ b/src/misc.h @@ -29,6 +29,7 @@ #include #include +#include "application.h" //// //// Macros @@ -47,6 +48,7 @@ extern int get_system_time(); extern int cpu_count(); extern int Bioskey(); + //// //// Debug //// diff --git a/src/pawns.cpp b/src/pawns.cpp index f06d9f12..f34095b9 100644 --- a/src/pawns.cpp +++ b/src/pawns.cpp @@ -144,7 +144,7 @@ PawnInfoTable::PawnInfoTable(unsigned numOfEntries) { { std::cerr << "Failed to allocate " << (numOfEntries * sizeof(PawnInfo)) << " bytes for pawn hash table." << std::endl; - exit(EXIT_FAILURE); + Application::exit_with_failure(); } clear(); } diff --git a/src/search.cpp b/src/search.cpp index 64863be1..6250f02f 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -359,10 +359,11 @@ void SearchStack::initKillers() { //// /// think() is the external interface to Stockfish's search, and is called when -/// the program receives the UCI 'go' command. It initializes various -/// search-related global variables, and calls root_search() +/// the program receives the UCI 'go' command. It initializes various +/// search-related global variables, and calls root_search(). It returns false +/// when a quit command is received during the search. -void think(const Position &pos, bool infinite, bool ponder, int side_to_move, +bool think(const Position &pos, bool infinite, bool ponder, int side_to_move, int time[], int increment[], int movesToGo, int maxDepth, int maxNodes, int maxTime, Move searchMoves[]) { @@ -377,7 +378,7 @@ void think(const Position &pos, bool infinite, bool ponder, int side_to_move, if (bookMove != MOVE_NONE) { std::cout << "bestmove " << bookMove << std::endl; - return; + return true; } } @@ -541,13 +542,8 @@ void think(const Position &pos, bool infinite, bool ponder, int side_to_move, if (UseLogFile) LogFile.close(); - if (Quit) - { - stop_threads(); - quit_eval(); - exit(0); - } Idle = true; + return !Quit; } @@ -2454,6 +2450,7 @@ namespace { AbortSearch = true; PonderSearch = false; Quit = true; + return; } else if(command == "stop") { @@ -2551,19 +2548,21 @@ namespace { // after which the bestmove and pondermove will be printed (in id_loop()). void wait_for_stop_or_ponderhit() { + std::string command; - while(true) { - if(!std::getline(std::cin, command)) - command = "quit"; + while (true) + { + if (!std::getline(std::cin, command)) + command = "quit"; - if(command == "quit") { - stop_threads(); - quit_eval(); - exit(0); - } - else if(command == "ponderhit" || command == "stop") - break; + if (command == "quit") + { + Quit = true; + break; + } + else if(command == "ponderhit" || command == "stop") + break; } } diff --git a/src/search.h b/src/search.h index 0323d47e..bef8bf2e 100644 --- a/src/search.h +++ b/src/search.h @@ -84,7 +84,7 @@ extern History H; extern void init_threads(); extern void stop_threads(); -extern void think(const Position &pos, bool infinite, bool ponder, int side_to_move, +extern bool think(const Position &pos, bool infinite, bool ponder, int side_to_move, int time[], int increment[], int movesToGo, int maxDepth, int maxNodes, int maxTime, Move searchMoves[]); extern int64_t nodes_searched(); diff --git a/src/tt.cpp b/src/tt.cpp index 83eaf800..a0cc7922 100644 --- a/src/tt.cpp +++ b/src/tt.cpp @@ -69,7 +69,7 @@ void TranspositionTable::set_size(unsigned mbSize) { { std::cerr << "Failed to allocate " << mbSize << " MB for transposition table." << std::endl; - exit(EXIT_FAILURE); + Application::exit_with_failure(); } clear(); } diff --git a/src/uci.cpp b/src/uci.cpp index 9c13eff7..4c9d90e5 100644 --- a/src/uci.cpp +++ b/src/uci.cpp @@ -56,11 +56,10 @@ namespace { Position RootPosition; // Local functions - void get_command(); - void handle_command(const std::string &command); - void set_option(UCIInputParser &uip); - void set_position(UCIInputParser &uip); - void go(UCIInputParser &uip); + bool handle_command(const std::string& command); + void set_option(UCIInputParser& uip); + void set_position(UCIInputParser& uip); + bool go(UCIInputParser& uip); } @@ -68,17 +67,25 @@ namespace { //// Functions //// -/// uci_main_loop() is the only global function in this file. It is +/// uci_main_loop() is the only global function in this file. It is /// called immediately after the program has finished initializing. /// The program remains in this loop until it receives the "quit" UCI -/// command. +/// command. It waits for a command from the user, and passes this +/// command to handle_command and also intercepts EOF from stdin, +/// by translating EOF to the "quit" command. This ensures that Stockfish +/// exits gracefully if the GUI dies unexpectedly. void uci_main_loop() { RootPosition.from_fen(StartPosition); + std::string command; - while (1) - get_command(); + do { + // Wait for a command from stdin + if (!std::getline(std::cin, command)) + command = "quit"; + + } while (handle_command(command)); } @@ -88,29 +95,12 @@ void uci_main_loop() { namespace { - // get_command() waits for a command from the user, and passes - // this command to handle_command. get_command also intercepts - // EOF from stdin, by translating EOF to the "quit" command. This - // ensures that Stockfish exits gracefully if the GUI dies - // unexpectedly. - - void get_command() { - - std::string command; - - if (!std::getline(std::cin, command)) - command = "quit"; - - handle_command(command); - } - - // handle_command() takes a text string as input, uses a // UCIInputParser object to parse this text string as a UCI command, - // and calls the appropriate functions. In addition to the UCI + // and calls the appropriate functions. In addition to the UCI // commands, the function also supports a few debug commands. - void handle_command(const std::string &command) { + bool handle_command(const std::string& command) { UCIInputParser uip(command); std::string token; @@ -118,12 +108,12 @@ namespace { uip >> token; // operator >> skips any whitespace if (token == "quit") - { - stop_threads(); - quit_eval(); - exit(0); - } - else if (token == "uci") + return false; + + if (token == "go") + return go(uip); + + if (token == "uci") { std::cout << "id name " << engine_name() << std::endl << "id author Tord Romstad, Marco Costalba" @@ -143,8 +133,6 @@ namespace { set_position(uip); else if (token == "setoption") set_option(uip); - else if (token == "go") - go(uip); // The remaining commands are for debugging purposes only. // Perhaps they should be removed later in order to reduce the @@ -183,6 +171,7 @@ namespace { std::cout << token << std::endl; } } + return true; } @@ -192,7 +181,7 @@ namespace { // ("position"), and is ready to read the second token ("startpos" // or "fen", if the input is well-formed). - void set_position(UCIInputParser &uip) { + void set_position(UCIInputParser& uip) { std::string token; @@ -242,7 +231,7 @@ namespace { // ("setoption"), and is ready to read the second token ("name", if // the input is well-formed). - void set_option(UCIInputParser &uip) { + void set_option(UCIInputParser& uip) { std::string token, name; @@ -269,14 +258,15 @@ namespace { // go() is called when Stockfish receives the "go" UCI command. The - // input parameter is a UCIInputParser. It is assumed that this + // input parameter is a UCIInputParser. It is assumed that this // parser has consumed the first token of the UCI command ("go"), - // and is ready to read the second token. The function sets the + // and is ready to read the second token. The function sets the // thinking time and other parameters from the input string, and // calls think() (defined in search.cpp) with the appropriate - // parameters. + // parameters. Returns false if a quit command is received while + // thinking, returns true otherwise. - void go(UCIInputParser &uip) { + bool go(UCIInputParser& uip) { std::string token; @@ -328,7 +318,7 @@ namespace { assert(RootPosition.is_ok()); - think(RootPosition, infinite, ponder, RootPosition.side_to_move(), time, - inc, movesToGo, depth, nodes, moveTime, searchMoves); + return think(RootPosition, infinite, ponder, RootPosition.side_to_move(), time, + inc, movesToGo, depth, nodes, moveTime, searchMoves); } } -- 2.39.2