00001 //---------------------------------------------------------------------------- 00002 /** @file GtpEngine.cpp 00003 See GtpEngine.h 00004 */ 00005 //---------------------------------------------------------------------------- 00006 00007 #include "GtpEngine.h" 00008 00009 #include <iomanip> 00010 #include <cassert> 00011 #include <cctype> 00012 #include <fstream> 00013 00014 #if GTPENGINE_PONDER || GTPENGINE_INTERRUPT 00015 #include <boost/thread/barrier.hpp> 00016 #include <boost/thread/condition.hpp> 00017 #include <boost/thread/mutex.hpp> 00018 #include <boost/thread/thread.hpp> 00019 #include <boost/thread/xtime.hpp> 00020 00021 using boost::barrier; 00022 using boost::condition; 00023 using boost::mutex; 00024 using boost::thread; 00025 using boost::xtime; 00026 using boost::xtime_get; 00027 #endif 00028 00029 using namespace std; 00030 00031 #if WIN32 00032 // Disable Visual C++ 2005 warning 4355 ('this' : used in base member 00033 // initializer list). The constructors of ReadThread and PonderThread store a 00034 // reference of 'this', which is allowed as long as 'this' is not used yet 00035 #pragma warning( disable : 4355 ) 00036 #endif 00037 00038 //---------------------------------------------------------------------------- 00039 00040 /** Utility functions. */ 00041 namespace { 00042 00043 void Trim(string& str); 00044 00045 /** Check, if line contains a command. 00046 @param line The line to check. 00047 @return True, if command does not contain only whitespaces and is not a 00048 comment line. 00049 */ 00050 bool IsCommandLine(const string& line) 00051 { 00052 string trimmedLine = line; 00053 Trim(trimmedLine); 00054 return (! trimmedLine.empty() && trimmedLine[0] != '#'); 00055 } 00056 00057 #if ! GTPENGINE_INTERRUPT 00058 00059 /** Read next command from stream. 00060 @param in The input stream. 00061 @param[out] cmd The command (reused for efficiency) 00062 @return @c false on end-of-stream or read error. 00063 */ 00064 bool ReadCommand(GtpCommand& cmd, GtpInputStream& in) 00065 { 00066 string line; 00067 while (in.GetLine(line) && ! IsCommandLine(line)) 00068 { 00069 } 00070 if (in.EndOfInput()) 00071 return false; 00072 Trim(line); 00073 cmd.Init(line); 00074 return true; 00075 } 00076 #endif 00077 00078 /** Replace empty lines in a multi-line string by lines containing a single 00079 space. 00080 @param text The input string. 00081 @return The input string with all occurrences of "\n\n" replaced by 00082 "\n \n". 00083 */ 00084 string ReplaceEmptyLines(const string& text) 00085 { 00086 if (text.find("\n\n") == string::npos) 00087 return text; 00088 istringstream in(text); 00089 ostringstream result; 00090 bool lastWasNewLine = false; 00091 char c; 00092 while (in.get(c)) 00093 { 00094 bool isNewLine = (c == '\n'); 00095 if (isNewLine && lastWasNewLine) 00096 result.put(' '); 00097 result.put(c); 00098 lastWasNewLine = isNewLine; 00099 } 00100 return result.str(); 00101 } 00102 00103 /** Remove leading and trailing whitespaces from a string. 00104 Whitespaces are tab, carriage return and space. 00105 @param str The input string. 00106 */ 00107 void Trim(string& str) 00108 { 00109 char const* whiteSpace = " \t\r"; 00110 size_t pos = str.find_first_not_of(whiteSpace); 00111 str.erase(0, pos); 00112 pos = str.find_last_not_of(whiteSpace); 00113 str.erase(pos + 1); 00114 } 00115 00116 } // namespace 00117 00118 //---------------------------------------------------------------------------- 00119 00120 #if GTPENGINE_PONDER || GTPENGINE_INTERRUPT 00121 00122 /** Utility functions for Boost.Thread. */ 00123 namespace { 00124 00125 void Notify(mutex& aMutex, condition& aCondition) 00126 { 00127 mutex::scoped_lock lock(aMutex); 00128 aCondition.notify_all(); 00129 } 00130 00131 } // namespace 00132 00133 #endif // GTPENGINE_PONDER || GTPENGINE_INTERRUPT 00134 00135 //---------------------------------------------------------------------------- 00136 00137 #if GTPENGINE_PONDER 00138 00139 namespace { 00140 00141 /** Ponder thread used by GtpEngine::MainLoop(). 00142 This thread calls GtpEngine::Ponder() while the engine is waiting for the 00143 next command. 00144 @see GtpEngine::Ponder() 00145 */ 00146 class PonderThread 00147 { 00148 public: 00149 PonderThread(GtpEngine& engine); 00150 00151 void StartPonder(); 00152 00153 void StopPonder(); 00154 00155 void Quit(); 00156 00157 private: 00158 class Function 00159 { 00160 public: 00161 Function(PonderThread& ponderThread); 00162 00163 void operator()(); 00164 00165 private: 00166 PonderThread& m_ponderThread; 00167 }; 00168 00169 friend class PonderThread::Function; 00170 00171 GtpEngine& m_engine; 00172 00173 barrier m_threadReady; 00174 00175 mutex m_startPonderMutex; 00176 00177 mutex m_ponderFinishedMutex; 00178 00179 condition m_startPonder; 00180 00181 condition m_ponderFinished; 00182 00183 mutex::scoped_lock m_ponderFinishedLock; 00184 00185 /** The thread to run the ponder function. 00186 Order dependency: must be constructed as the last member, because the 00187 constructor starts the thread. 00188 */ 00189 boost::thread m_thread; 00190 }; 00191 00192 PonderThread::Function::Function(PonderThread& ponderThread) 00193 : m_ponderThread(ponderThread) 00194 { 00195 } 00196 00197 void PonderThread::Function::operator()() 00198 { 00199 mutex::scoped_lock lock(m_ponderThread.m_startPonderMutex); 00200 m_ponderThread.m_threadReady.wait(); 00201 while (true) 00202 { 00203 m_ponderThread.m_startPonder.wait(lock); 00204 GtpEngine& engine = m_ponderThread.m_engine; 00205 if (engine.IsQuitSet()) 00206 return; 00207 engine.Ponder(); 00208 Notify(m_ponderThread.m_ponderFinishedMutex, 00209 m_ponderThread.m_ponderFinished); 00210 } 00211 } 00212 00213 PonderThread::PonderThread(GtpEngine& engine) 00214 : m_engine(engine), 00215 m_threadReady(2), 00216 m_ponderFinishedLock(m_ponderFinishedMutex), 00217 m_thread(Function(*this)) 00218 { 00219 m_threadReady.wait(); 00220 } 00221 00222 void PonderThread::StartPonder() 00223 { 00224 m_engine.InitPonder(); 00225 Notify(m_startPonderMutex, m_startPonder); 00226 } 00227 00228 void PonderThread::StopPonder() 00229 { 00230 m_engine.StopPonder(); 00231 m_ponderFinished.wait(m_ponderFinishedLock); 00232 } 00233 00234 void PonderThread::Quit() 00235 { 00236 Notify(m_startPonderMutex, m_startPonder); 00237 m_thread.join(); 00238 } 00239 00240 } // namespace 00241 00242 #endif // GTPENGINE_PONDER 00243 00244 //---------------------------------------------------------------------------- 00245 00246 #if GTPENGINE_INTERRUPT 00247 00248 namespace { 00249 00250 /** Thread for reading the next command line. 00251 This thread is used instead of the simple function 00252 ReadCommand(GtpCommand&), if GtpEngine is compiled with interrupt 00253 support. 00254 @see GtpEngine::Interrupt() 00255 */ 00256 class ReadThread 00257 { 00258 public: 00259 ReadThread(GtpInputStream& in, GtpEngine& engine); 00260 00261 bool ReadCommand(GtpCommand& cmd); 00262 00263 void JoinThread(); 00264 00265 private: 00266 class Function 00267 { 00268 public: 00269 Function(ReadThread& readThread); 00270 00271 void operator()(); 00272 00273 private: 00274 ReadThread& m_readThread; 00275 00276 void ExecuteSleepLine(const string& line); 00277 }; 00278 00279 friend class ReadThread::Function; 00280 00281 GtpInputStream& m_in; 00282 00283 GtpEngine& m_engine; 00284 00285 string m_line; 00286 00287 bool m_isStreamGood; 00288 00289 barrier m_threadReady; 00290 00291 mutex m_waitCommandMutex; 00292 00293 condition m_waitCommand; 00294 00295 mutex m_commandReceivedMutex; 00296 00297 condition m_commandReceived; 00298 00299 mutex::scoped_lock m_commandReceivedLock; 00300 00301 /** The thread to run the read command function. 00302 Order dependency: must be constructed as the last member, because the 00303 constructor starts the thread. 00304 */ 00305 boost::thread m_thread; 00306 }; 00307 00308 ReadThread::Function::Function(ReadThread& readThread) 00309 : m_readThread(readThread) 00310 { 00311 } 00312 00313 void ReadThread::Function::operator()() 00314 { 00315 mutex::scoped_lock lock(m_readThread.m_waitCommandMutex); 00316 m_readThread.m_threadReady.wait(); 00317 GtpEngine& engine = m_readThread.m_engine; 00318 GtpInputStream& in = m_readThread.m_in; 00319 string line; 00320 while (true) 00321 { 00322 while (in.GetLine(line)) 00323 { 00324 Trim(line); 00325 if (line == "# interrupt") 00326 engine.Interrupt(); 00327 else if (line.find("# gtpengine-sleep ") == 0) 00328 ExecuteSleepLine(line); 00329 else if (IsCommandLine(line)) 00330 break; 00331 } 00332 m_readThread.m_waitCommand.wait(lock); 00333 m_readThread.m_isStreamGood = ! in.EndOfInput(); 00334 m_readThread.m_line = line; 00335 Notify(m_readThread.m_commandReceivedMutex, 00336 m_readThread.m_commandReceived); 00337 if (in.EndOfInput()) 00338 return; 00339 // See comment at GtpEngine::SetQuit 00340 GtpCommand cmd(line); 00341 if (cmd.Name() == "quit") 00342 return; 00343 } 00344 } 00345 00346 void ReadThread::Function::ExecuteSleepLine(const string& line) 00347 { 00348 istringstream buffer(line); 00349 string s; 00350 buffer >> s; 00351 assert(s == "#"); 00352 buffer >> s; 00353 assert(s == "gtpengine-sleep"); 00354 int seconds; 00355 buffer >> seconds; 00356 if (seconds > 0) 00357 { 00358 cerr << "GtpEngine: sleep " << seconds << '\n'; 00359 xtime time; 00360 xtime_get(&time, boost::TIME_UTC); 00361 time.sec += seconds; 00362 thread::sleep(time); 00363 cerr << "GtpEngine: sleep done\n"; 00364 } 00365 } 00366 00367 void ReadThread::JoinThread() 00368 { 00369 m_thread.join(); 00370 } 00371 00372 ReadThread::ReadThread(GtpInputStream& in, GtpEngine& engine) 00373 : m_in(in), 00374 m_engine(engine), 00375 m_threadReady(2), 00376 m_commandReceivedLock(m_commandReceivedMutex), 00377 m_thread(Function(*this)) 00378 { 00379 m_threadReady.wait(); 00380 } 00381 00382 bool ReadThread::ReadCommand(GtpCommand& cmd) 00383 { 00384 Notify(m_waitCommandMutex, m_waitCommand); 00385 m_commandReceived.wait(m_commandReceivedLock); 00386 if (! m_isStreamGood) 00387 return false; 00388 cmd.Init(m_line); 00389 return true; 00390 } 00391 00392 } // namespace 00393 00394 #endif // GTPENGINE_INTERRUPT 00395 00396 //---------------------------------------------------------------------------- 00397 00398 GtpFailure::GtpFailure() 00399 { 00400 } 00401 00402 GtpFailure::GtpFailure(const GtpFailure& failure) 00403 { 00404 m_response << failure.Response(); 00405 m_response.copyfmt(failure.m_response); 00406 } 00407 00408 GtpFailure::GtpFailure(const string& response) 00409 { 00410 m_response << response; 00411 } 00412 00413 GtpFailure::~GtpFailure() throw() 00414 { 00415 } 00416 00417 //---------------------------------------------------------------------------- 00418 00419 GtpCommand::Argument::Argument(const string& value, std::size_t end) 00420 : m_value(value), 00421 m_end(end) 00422 { 00423 } 00424 00425 //---------------------------------------------------------------------------- 00426 00427 ostringstream GtpCommand::s_dummy; 00428 00429 const string& GtpCommand::Arg(std::size_t number) const 00430 { 00431 size_t index = number + 1; 00432 if (number >= NuArg()) 00433 throw GtpFailure() << "missing argument " << index; 00434 return m_arguments[index].m_value; 00435 } 00436 00437 const string& GtpCommand::Arg() const 00438 { 00439 CheckNuArg(1); 00440 return Arg(0); 00441 } 00442 00443 std::string GtpCommand::ArgLine() const 00444 { 00445 string result = m_line.substr(m_arguments[0].m_end); 00446 Trim(result); 00447 return result; 00448 } 00449 00450 string GtpCommand::ArgToLower(std::size_t number) const 00451 { 00452 string value = Arg(number); 00453 for (size_t i = 0; i < value.length(); ++i) 00454 value[i] = tolower(value[i]); 00455 return value; 00456 } 00457 00458 bool GtpCommand::BoolArg(std::size_t number) const 00459 { 00460 string value = Arg(number); 00461 if (value == "1") 00462 return true; 00463 if (value == "0") 00464 return false; 00465 throw GtpFailure() << "argument " << (number + 1) << " must be 0 or 1"; 00466 } 00467 00468 void GtpCommand::CheckNuArg(std::size_t number) const 00469 { 00470 if (NuArg() == number) 00471 return; 00472 if (number == 0) 00473 throw GtpFailure() << "no arguments allowed"; 00474 else if (number == 1) 00475 throw GtpFailure() << "command needs one argument"; 00476 else 00477 throw GtpFailure() << "command needs " << number << " arguments"; 00478 } 00479 00480 void GtpCommand::CheckNuArgLessEqual(std::size_t number) const 00481 { 00482 if (NuArg() <= number) 00483 return; 00484 if (number == 1) 00485 throw GtpFailure() << "command needs at most one argument"; 00486 else 00487 throw GtpFailure() << "command needs at most " << number << " arguments"; 00488 } 00489 00490 double GtpCommand::FloatArg(std::size_t number) const 00491 { 00492 istringstream in(Arg(number)); 00493 double result; 00494 in >> result; 00495 if (! in) 00496 throw GtpFailure() << "argument " << (number + 1) 00497 << " must be a float"; 00498 return result; 00499 } 00500 00501 int GtpCommand::IntArg(std::size_t number) const 00502 { 00503 istringstream in(Arg(number)); 00504 int result; 00505 in >> result; 00506 if (! in) 00507 throw GtpFailure() << "argument " << (number + 1) 00508 << " must be a number"; 00509 return result; 00510 } 00511 00512 int GtpCommand::IntArg(std::size_t number, int min) const 00513 { 00514 int result = IntArg(number); 00515 if (result < min) 00516 throw GtpFailure() << "argument " << (number + 1) 00517 << " must be greater or equal " << min; 00518 return result; 00519 } 00520 00521 int GtpCommand::IntArg(std::size_t number, int min, int max) const 00522 { 00523 int result = IntArg(number); 00524 if (result < min) 00525 throw GtpFailure() << "argument " << (number + 1) 00526 << " must be greater or equal " << min; 00527 if (result > max) 00528 throw GtpFailure() << "argument " << (number + 1) 00529 << " must be less or equal " << max; 00530 return result; 00531 } 00532 00533 void GtpCommand::Init(const string& line) 00534 { 00535 m_line = line; 00536 Trim(m_line); 00537 SplitLine(m_line); 00538 assert(m_arguments.size() > 0); 00539 ParseCommandId(); 00540 assert(m_arguments.size() > 0); 00541 m_response.str(""); 00542 m_response.copyfmt(s_dummy); 00543 } 00544 00545 void GtpCommand::ParseCommandId() 00546 { 00547 m_id = ""; 00548 if (m_arguments.size() < 2) 00549 return; 00550 istringstream in(m_arguments[0].m_value); 00551 int id; 00552 in >> id; 00553 if (in) 00554 { 00555 m_id = m_arguments[0].m_value; 00556 m_arguments.erase(m_arguments.begin()); 00557 } 00558 } 00559 00560 string GtpCommand::RemainingLine(std::size_t number) const 00561 { 00562 size_t index = number + 1; 00563 if (number >= NuArg()) 00564 throw GtpFailure() << "missing argument " << index; 00565 string result = m_line.substr(m_arguments[index].m_end); 00566 Trim(result); 00567 return result; 00568 } 00569 00570 void GtpCommand::SetResponse(const string& response) 00571 { 00572 m_response.str(response); 00573 } 00574 00575 void GtpCommand::SetResponseBool(bool value) 00576 { 00577 m_response.str(value ? "true" : "false"); 00578 } 00579 00580 std::size_t GtpCommand::SizeTypeArg(std::size_t number) const 00581 { 00582 string arg = Arg(number); 00583 // Workaround for bug in standard library of some GCC versions (e.g. GCC 00584 // 4.4.1 on Ubuntu 9.10): negative numbers are parsed without error as 00585 // size_t, which should be unsigned 00586 bool fail = (! arg.empty() && arg[0] == '-'); 00587 std::size_t result; 00588 if (! fail) 00589 { 00590 istringstream in(arg); 00591 in >> result; 00592 fail = ! in; 00593 } 00594 if (fail) 00595 throw GtpFailure() << "argument " << (number + 1) 00596 << " must be a number"; 00597 return result; 00598 } 00599 00600 std::size_t GtpCommand::SizeTypeArg(std::size_t number, std::size_t min) const 00601 { 00602 std::size_t result = SizeTypeArg(number); 00603 if (result < min) 00604 throw GtpFailure() << "argument " << (number + 1) 00605 << " must be greater or equal " << min; 00606 return result; 00607 } 00608 00609 /** Split line into arguments. 00610 Arguments are words separated by whitespaces. 00611 Arguments with whitespaces can be quoted with quotation marks ('"'). 00612 Characters can be escaped with a backslash ('\'). 00613 @param line The line to split. 00614 */ 00615 void GtpCommand::SplitLine(const string& line) 00616 { 00617 m_arguments.clear(); 00618 bool escape = false; 00619 bool inString = false; 00620 ostringstream element; 00621 for (size_t i = 0; i < line.size(); ++i) 00622 { 00623 char c = line[i]; 00624 if (c == '"' && ! escape) 00625 { 00626 if (inString) 00627 { 00628 m_arguments.push_back(Argument(element.str(), i + 1)); 00629 element.str(""); 00630 } 00631 inString = ! inString; 00632 } 00633 else if (isspace(c) && ! inString) 00634 { 00635 if (! element.str().empty()) 00636 { 00637 m_arguments.push_back(Argument(element.str(), i + 1)); 00638 element.str(""); 00639 } 00640 } 00641 else 00642 element << c; 00643 escape = (c == '\\' && ! escape); 00644 } 00645 if (! element.str().empty()) 00646 m_arguments.push_back(Argument(element.str(), line.size())); 00647 } 00648 00649 //---------------------------------------------------------------------------- 00650 00651 GtpCallbackBase::~GtpCallbackBase() throw() 00652 { 00653 } 00654 00655 //---------------------------------------------------------------------------- 00656 00657 GtpEngine::GtpEngine(GtpInputStream& in, GtpOutputStream& out) 00658 : m_quit(false), 00659 m_in(in), 00660 m_out(out) 00661 { 00662 Register("known_command", &GtpEngine::CmdKnownCommand, this); 00663 Register("list_commands", &GtpEngine::CmdListCommands, this); 00664 Register("name", &GtpEngine::CmdName, this); 00665 Register("protocol_version", &GtpEngine::CmdProtocolVersion, this); 00666 Register("quit", &GtpEngine::CmdQuit, this); 00667 Register("version", &GtpEngine::CmdVersion, this); 00668 } 00669 00670 GtpEngine::~GtpEngine() 00671 { 00672 typedef CallbackMap::iterator Iterator; 00673 for (Iterator i = m_callbacks.begin(); i != m_callbacks.end(); ++i) 00674 { 00675 delete i->second; 00676 #ifndef NDEBUG 00677 i->second = 0; 00678 #endif 00679 } 00680 } 00681 00682 void GtpEngine::BeforeHandleCommand() 00683 { 00684 // Default implementation does nothing 00685 } 00686 00687 void GtpEngine::BeforeWritingResponse() 00688 { 00689 // Default implementation does nothing 00690 } 00691 00692 /** Return @c true if command is known, @c false otherwise. */ 00693 void GtpEngine::CmdKnownCommand(GtpCommand& cmd) 00694 { 00695 cmd.CheckNuArg(1); 00696 cmd.SetResponseBool(IsRegistered(cmd.Arg(0))); 00697 } 00698 00699 /** List all known commands. */ 00700 void GtpEngine::CmdListCommands(GtpCommand& cmd) 00701 { 00702 cmd.CheckArgNone(); 00703 typedef CallbackMap::const_iterator Iterator; 00704 for (Iterator i = m_callbacks.begin(); i != m_callbacks.end(); ++i) 00705 cmd << i->first << '\n'; 00706 } 00707 00708 /** Return name. */ 00709 void GtpEngine::CmdName(GtpCommand& cmd) 00710 { 00711 cmd.CheckArgNone(); 00712 cmd << "Unknown"; 00713 } 00714 00715 /** Return protocol version. */ 00716 void GtpEngine::CmdProtocolVersion(GtpCommand& cmd) 00717 { 00718 cmd.CheckArgNone(); 00719 cmd << "2"; 00720 } 00721 00722 /** Quit command loop. */ 00723 void GtpEngine::CmdQuit(GtpCommand& cmd) 00724 { 00725 cmd.CheckArgNone(); 00726 SetQuit(); 00727 } 00728 00729 /** Return empty version string. 00730 The GTP standard says to return empty string, if no meaningful reponse 00731 is available. 00732 */ 00733 void GtpEngine::CmdVersion(GtpCommand& cmd) 00734 { 00735 cmd.CheckArgNone(); 00736 } 00737 00738 string GtpEngine::ExecuteCommand(const string& cmdline, ostream& log) 00739 { 00740 if (! IsCommandLine(cmdline)) 00741 throw GtpFailure() << "Bad command: " << cmdline; 00742 GtpCommand cmd; 00743 cmd.Init(cmdline); 00744 log << cmd.Line() << '\n'; 00745 GtpOutputStream gtpLog(log); 00746 bool status = HandleCommand(cmd, gtpLog); 00747 string response = cmd.Response(); 00748 if (! status) 00749 throw GtpFailure() << "Executing " << cmd.Line() << " failed"; 00750 return response; 00751 } 00752 00753 void GtpEngine::ExecuteFile(const string& name, ostream& log) 00754 { 00755 ifstream in(name.c_str()); 00756 if (! in) 00757 throw GtpFailure() << "Cannot read " << name; 00758 string line; 00759 GtpCommand cmd; 00760 GtpOutputStream gtpLog(log); 00761 while (getline(in, line)) 00762 { 00763 if (! IsCommandLine(line)) 00764 continue; 00765 cmd.Init(line); 00766 log << cmd.Line() << '\n'; 00767 00768 bool status = HandleCommand(cmd, gtpLog); 00769 if (! status) 00770 throw GtpFailure() << "Executing " << cmd.Line() << " failed"; 00771 } 00772 } 00773 00774 bool GtpEngine::HandleCommand(GtpCommand& cmd, GtpOutputStream& out) 00775 { 00776 BeforeHandleCommand(); 00777 bool status = true; 00778 string response; 00779 try 00780 { 00781 CallbackMap::const_iterator pos = m_callbacks.find(cmd.Name()); 00782 if (pos == m_callbacks.end()) 00783 { 00784 status = false; 00785 response = "unknown command: " + cmd.Name(); 00786 } 00787 else 00788 { 00789 GtpCallbackBase* callback = pos->second; 00790 (*callback)(cmd); 00791 response = cmd.Response(); 00792 } 00793 } 00794 catch (const GtpFailure& failure) 00795 { 00796 status = false; 00797 response = failure.Response(); 00798 } 00799 response = ReplaceEmptyLines(response); 00800 BeforeWritingResponse(); 00801 ostringstream ostr; 00802 ostr << (status ? '=' : '?') << cmd.ID() << ' ' << response; 00803 size_t size = response.size(); 00804 if (size == 0 || response[size - 1] != '\n') 00805 ostr << '\n'; 00806 ostr << '\n' << flush; 00807 out.Write(ostr.str()); 00808 out.Flush(); 00809 return status; 00810 } 00811 00812 bool GtpEngine::IsRegistered(const string& command) const 00813 { 00814 return (m_callbacks.find(command) != m_callbacks.end()); 00815 } 00816 00817 void GtpEngine::MainLoop() 00818 { 00819 m_quit = false; 00820 #if GTPENGINE_PONDER 00821 PonderThread ponderThread(*this); 00822 #endif 00823 #if GTPENGINE_INTERRUPT 00824 ReadThread readThread(m_in, *this); 00825 #endif 00826 GtpCommand cmd; 00827 while (true) 00828 { 00829 #if GTPENGINE_PONDER 00830 ponderThread.StartPonder(); 00831 #endif 00832 #if GTPENGINE_INTERRUPT 00833 bool isStreamGood = readThread.ReadCommand(cmd); 00834 #else 00835 bool isStreamGood = ReadCommand(cmd, m_in); 00836 #endif 00837 #if GTPENGINE_PONDER 00838 ponderThread.StopPonder(); 00839 #endif 00840 if (isStreamGood) 00841 HandleCommand(cmd, m_out); 00842 else 00843 SetQuit(); 00844 if (m_quit) 00845 { 00846 #if GTPENGINE_PONDER 00847 ponderThread.Quit(); 00848 #endif 00849 #if GTPENGINE_INTERRUPT 00850 readThread.JoinThread(); 00851 #endif 00852 break; 00853 } 00854 } 00855 } 00856 00857 void GtpEngine::Register(const string& command, GtpCallbackBase* callback) 00858 { 00859 CallbackMap::iterator pos = m_callbacks.find(command); 00860 if (pos != m_callbacks.end()) 00861 { 00862 delete pos->second; 00863 #ifndef NDEBUG 00864 pos->second = 0; 00865 #endif 00866 m_callbacks.erase(pos); 00867 } 00868 m_callbacks.insert(make_pair(command, callback)); 00869 } 00870 00871 void GtpEngine::SetQuit() 00872 { 00873 m_quit = true; 00874 } 00875 00876 bool GtpEngine::IsQuitSet() const 00877 { 00878 return m_quit; 00879 } 00880 00881 00882 #if GTPENGINE_PONDER 00883 00884 void GtpEngine::Ponder() 00885 { 00886 // Default implementation does nothing 00887 #ifdef GTPENGINE_TEST 00888 cerr << "GtpEngine::Ponder()\n"; 00889 #endif 00890 } 00891 00892 void GtpEngine::StopPonder() 00893 { 00894 // Default implementation does nothing 00895 #ifdef GTPENGINE_TEST 00896 cerr << "GtpEngine::StopPonder()\n"; 00897 #endif 00898 } 00899 00900 void GtpEngine::InitPonder() 00901 { 00902 // Default implementation does nothing 00903 #ifdef GTPENGINE_TEST 00904 cerr << "GtpEngine::InitPonder()\n"; 00905 #endif 00906 } 00907 00908 #endif // GTPENGINE_PONDER 00909 00910 00911 #if GTPENGINE_INTERRUPT 00912 00913 void GtpEngine::Interrupt() 00914 { 00915 // Default implementation does nothing 00916 #ifdef GTPENGINE_TEST 00917 cerr << "GtpEngine::Interrupt()\n"; 00918 #endif 00919 } 00920 00921 #endif // GTPENGINE_INTERRUPT 00922 00923 //---------------------------------------------------------------------------- 00924 00925 #ifdef GTPENGINE_MAIN 00926 00927 /** Main routine for testing and standalone compilation. */ 00928 int main() 00929 { 00930 try 00931 { 00932 GtpEngine engine(cin, cout); 00933 engine.MainLoop(); 00934 } 00935 catch (const exception& e) 00936 { 00937 cerr << e.what() << '\n'; 00938 return 1; 00939 } 00940 return 0; 00941 } 00942 00943 #endif // GTPENGINE_MAIN 00944 00945 //----------------------------------------------------------------------------