engine.cpp (3325B)
1 #include "engine.hpp" 2 #include "logger.hpp" 3 4 #include <sstream> 5 6 #include <sys/wait.h> 7 #include <unistd.h> 8 9 Engine::Pipes::Pipes() { 10 if (pipe(fd) < 0 || pipe(fd) < 0) { 11 logger::error("pipe"); 12 throw std::runtime_error("pipe failed"); 13 } 14 } 15 16 void Engine::Pipes::close() { 17 if (::close(fd[0]) < 0 || ::close(fd[1])) { 18 logger::error("close"); 19 throw std::runtime_error("close failed"); 20 } 21 closed = true; 22 } 23 24 uint16_t Engine::id_t = 0; 25 Engine::Engine(const char *file) : file(file) { 26 if ((engine_pid = fork()) == 0) { 27 start_engine(); 28 } else if (engine_pid < 0) { 29 logger::error("fork"); 30 throw std::runtime_error("fork failed"); 31 } 32 33 send("uci"); 34 35 logger::log(std::format("Engine {}: waiting for uciok from {}", id, file)); 36 while (true) { 37 std::string tmp, response = receive(); 38 if (response == "uciok") break; 39 std::stringstream ss(response); 40 ss >> tmp >> tmp; 41 ss.ignore(1); 42 if (tmp == "name") getline(ss, name); 43 if (tmp == "author") getline(ss, author); 44 } 45 logger::log(std::format("Engine {}: {} is {} by {}", id, file, name, author)); 46 logger::log(std::format("Engine {}: created", id), logger::Debug); 47 } 48 49 Engine::~Engine() { 50 send("quit"); 51 waitpid(engine_pid, nullptr, 0); 52 // kill(engine_pid, SIGKILL); 53 54 pipeFrom.close(); 55 pripeTo.close(); 56 logger::log("Engine: destroyed", logger::Debug); 57 } 58 59 void Engine::send(std::string &&command) { 60 command.push_back('\n'); 61 const char *buffer = command.data(); 62 size_t to_write = command.size(); 63 while (to_write) { 64 ssize_t size = write(pripeTo[1], buffer, to_write); 65 if (size == -1) { 66 logger::error("write"); 67 throw std::runtime_error("write failed"); 68 } 69 buffer += size, to_write -= size; 70 } 71 command.pop_back(); 72 logger::log(std::format("Engine {}: TO {}: {}", id, name.size() ? name : file, command), logger::Info); 73 } 74 75 std::string Engine::receive() { 76 int size = 0; 77 78 while (true) { 79 if (!q.empty()) { 80 std::string response = q.front(); 81 logger::log(std::format("Engine {}: FROM {}: {}", id, name.size() ? name : file, response), 82 logger::Info); 83 q.pop(); 84 return response; 85 } 86 87 if ((size = read(pipeFrom[0], rb + rb_size, sizeof(rb) - rb_size)) == -1) { 88 logger::error("read"); 89 throw std::runtime_error("read failed"); 90 } 91 92 int last = 0; 93 for (int i = rb_size; i < rb_size + size; i++) { 94 if (rb[i] == '\n') { 95 q.push(q_buffer); 96 q_buffer.clear(); 97 last = i; 98 continue; 99 } 100 q_buffer += rb[i]; 101 } 102 103 rb_size += size; 104 if (last) { 105 rb_size -= last + 1; 106 memcpy(rb, rb + last + 1, rb_size); 107 } 108 } 109 } 110 111 [[noreturn]] void Engine::start_engine() { 112 if (dup2(pripeTo[0], 0) < 0 || dup2(pipeFrom[1], 1) < 0) { 113 logger::error("dup2"); 114 throw std::runtime_error("dup2 failed"); 115 } 116 117 pipeFrom.close(); 118 pripeTo.close(); 119 120 execl(file, file, (char *)nullptr); 121 logger::error("execl"); 122 throw std::runtime_error("execl failed"); 123 }