]> git.sesse.net Git - stockfish/commitdiff
Rewrite the way application exits
authorMarco Costalba <mcostalba@gmail.com>
Thu, 7 May 2009 10:45:46 +0000 (12:45 +0200)
committerMarco Costalba <mcostalba@gmail.com>
Thu, 7 May 2009 10:59:19 +0000 (12:59 +0200)
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 <mcostalba@gmail.com>
13 files changed:
src/application.cpp [new file with mode: 0644]
src/application.h [new file with mode: 0644]
src/benchmark.cpp
src/book.cpp
src/evaluate.cpp
src/main.cpp
src/material.cpp
src/misc.h
src/pawns.cpp
src/search.cpp
src/search.h
src/tt.cpp
src/uci.cpp

diff --git a/src/application.cpp b/src/application.cpp
new file mode 100644 (file)
index 0000000..b7ebbbb
--- /dev/null
@@ -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 <http://www.gnu.org/licenses/>.
+*/
+
+
+////
+//// 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 (file)
index 0000000..f714938
--- /dev/null
@@ -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 <http://www.gnu.org/licenses/>.
+*/
+
+
+#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)
index 9b1b40091d48e2d623d617e989ee777059fcd0a4..6e8357f29d0d5dc36ecb44eaceabd7d3c1d30a3e 100644 (file)
@@ -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
index c69eab739b7dfcae8284db5c417225ad466a831a..c6e1e436daa2f192e8c83afc7d03b2ca876bb524 100644 (file)
@@ -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();
   }
 }
 
index c29fd5e651a46bd5a46af414b256f2923f6a029d..d4fdac21604ef92c7d11df8f4947cb09cbfffb06 100644 (file)
@@ -505,6 +505,8 @@ void quit_eval() {
   {
       delete PawnTable[i];
       delete MaterialTable[i];
+      PawnTable[i] = NULL;
+      MaterialTable[i] = NULL;
   }
 }
 
index 01fb47860b092d43d837099234d56786f3c2164f..36534ae3d98b805e2bb3e34c957dfea3e029dbea 100644 (file)
 // To profile with callgrind uncomment following line
 //#define USE_CALLGRIND
 
+
 ////
 //// Includes
 ////
 
 #include <iostream>
+#include <string>
 
 #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 <valgrind/callgrind.h>
 #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 <hash size> <threads> "
+               << "[time = 60s] [fen positions file = default] "
+               << "[time, depth or node limited = time]" << endl;
+      else
       {
-        std::cout << "Usage: stockfish bench <hash size> <threads> "
-                  << "[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;
 }
index 442dab56089729aaa72c4cdfcf9a76883253d163..b5f85de1513af30ffd416810b7baa62208228e2d 100644 (file)
@@ -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();
 }
index aff7cf3e1e0aca0703f95e3d8d5ec97b5f0d450e..8c0a1f054859795d40ab34e3675c5a755f0e2410 100644 (file)
@@ -29,6 +29,7 @@
 #include <fstream>
 #include <string>
 
+#include "application.h"
 
 ////
 //// Macros
@@ -47,6 +48,7 @@ extern int get_system_time();
 extern int cpu_count();
 extern int Bioskey();
 
+
 ////
 //// Debug
 ////
index f06d9f12a1d2a39525bc05184fd511e737c68aa1..f34095b9da37c3c4146e6a1971870c35ccd941dd 100644 (file)
@@ -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();
 }
index 64863be166b07f84f53571c10ee79e392f3d210d..6250f02fa4254718935c4fe48f72da7f4f71ab74 100644 (file)
@@ -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;
     }
   }
 
index 0323d47e3127b45edd42935041a9cced3e5a1fe8..bef8bf2e6b980ebab35f7ddf2d89c598a2429582 100644 (file)
@@ -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();
index 83eaf800ac480aabd64856c5d02ac3b27d8cffa5..a0cc7922395cb2a86bba11a1adbd29eacd78f305 100644 (file)
@@ -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();
   }
index 9c13eff7abfdf2ced889d5273c3e1e3651160284..4c9d90e5854f9b75438e4c96b85f0372c8d425b5 100644 (file)
@@ -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(UCIInputParseruip) {
 
     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(UCIInputParseruip) {
 
     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);
   }
 }