diff options
-rw-r--r-- | main.cpp | 117 | ||||
-rw-r--r-- | multilog.cpp | 10 | ||||
-rw-r--r-- | multilog.h | 3 | ||||
-rw-r--r-- | params.h | 16 | ||||
-rw-r--r-- | referee.cpp | 18 | ||||
-rw-r--r-- | referee.h | 4 | ||||
-rw-r--r-- | scheduler.cpp | 26 | ||||
-rw-r--r-- | scheduler.h | 6 |
8 files changed, 127 insertions, 73 deletions
@@ -23,13 +23,13 @@ #include "error.h" #include "multilog.h" #include "referee.h" -#include "params.h" using namespace std; -// TODO no globals -static MultiLog gMultiLog; +const char *const matchcachedir = "comp_cache"; +const char *const playerlogdir = "comp_playerlogs"; +const char *const gamelogdir = "comp_gamelogs"; static char hexchar(int n) { @@ -158,7 +158,10 @@ MatchResult MatchResult::inverted() const { } struct Params { + bool referee_verbose = false; string refereePath; + int num_matches = 5; + size_t timeout_msec = 10000; }; static string gameCodeName(const Player &p1, const Player &p2, int index) { @@ -226,19 +229,18 @@ static void recordResult(Player &p1, Player &p2, const MatchResult &result) { p2.results.push_back(result.inverted()); } -static void playMatch(Player &p1, Player &p2, int index, const Params ¶ms) { - int logId = gMultiLog.add(p1.fname + " - " + p2.fname + ": "); +static void playMatch(MultiLog &multiLog, Player &p1, Player &p2, int index, const Params ¶ms) { + int logId = multiLog.add(p1.fname + " - " + p2.fname + ": "); if (optional<MatchResult> optres = readMatchCache(p1, p2, index)) { - gMultiLog.append(logId, optres->describe(p1, p2) + " (cached)"); - gMultiLog.complete(logId); + multiLog.append(logId, optres->describe(p1, p2) + " (cached)"); + multiLog.complete(logId); recordResult(p1, p2, *optres); return; } MatchResult mres; - string gamelog_path = gameLogPath(p1, p2, index); ofstream gamelog(gamelog_path); if (!gamelog) { @@ -246,7 +248,7 @@ static void playMatch(Player &p1, Player &p2, int index, const Params ¶ms) { throw StopCompetitionError(); } - Referee referee(params.refereePath, {p1.fname, p2.fname}); + Referee referee(params.referee_verbose, params.refereePath, {p1.fname, p2.fname}); gamelog << "Player 1: " << p1.fname << "\n" << "Player 2: " << p2.fname << "\n\n" << flush; @@ -279,7 +281,7 @@ static void playMatch(Player &p1, Player &p2, int index, const Params ¶ms) { readEvent->callback(*oline); - if (mres.ms1 / 1000 > timeout_msec || mres.ms2 / 1000 > timeout_msec) { + if (mres.ms1 / 1000 > params.timeout_msec || mres.ms2 / 1000 > params.timeout_msec) { mres.status = MatchResult::Status::timeout; mres.sc1 = mres.sc2 = 0; referee.terminate(); @@ -326,8 +328,8 @@ static void playMatch(Player &p1, Player &p2, int index, const Params ¶ms) { } } - gMultiLog.append(logId, mres.describe(p1, p2)); - gMultiLog.complete(logId); + multiLog.append(logId, mres.describe(p1, p2)); + multiLog.complete(logId); gamelog << "\nResult: " << mres.describe(p1, p2) << "\n" << "P1 took " << mres.ms1 / 1000000.0 << " seconds\n" @@ -337,35 +339,92 @@ static void playMatch(Player &p1, Player &p2, int index, const Params ¶ms) { recordResult(p1, p2, mres); } -static void playerPit(Scheduler &scheduler, Player &p1, Player &p2, const Params ¶ms) { - for (int i = 0; i < num_matches; i++) { - scheduler.submit([&p1, &p2, i, params]() { playMatch(p1, p2, i + 1, params); }); +static void playerPit(Scheduler &scheduler, MultiLog &multiLog, Player &p1, Player &p2, const Params ¶ms) { + for (int i = 0; i < params.num_matches; i++) { + scheduler.submit([i, &p1, &p2, ¶ms, &multiLog]() { + playMatch(multiLog, p1, p2, i + 1, params); + }); } } -static void fullCompetition(Scheduler &scheduler, vector<Player> &players, const Params ¶ms) { +static void fullCompetition(Scheduler &scheduler, MultiLog &multiLog, vector<Player> &players, const Params ¶ms) { for (size_t p1i = 0; p1i < players.size(); p1i++) { for (size_t p2i = p1i + 1; p2i < players.size(); p2i++) { - playerPit(scheduler, players[p1i], players[p2i], params); - playerPit(scheduler, players[p2i], players[p1i], params); + playerPit(scheduler, multiLog, players[p1i], players[p2i], params); + playerPit(scheduler, multiLog, players[p2i], players[p1i], params); } } } -int main(int argc, char **argv) { - if (argc < 3) { - cerr << "Usage: " << argv[0] << " <referee> <players...>" << endl; - return 1; - } +static void usage(const char *argv0) { + cerr << "Usage: " << argv0 << " [OPTIONS] <referee> <players...>\n" + " --noansi Don't use fancy back-updating log output\n" + " --vs Verbose scheduler output (use with --noansi)\n" + " --vr Verbose referee-handling output (use with --noansi)\n" + " -j <NUM> Set number of parallel games (default ncores/2)\n" + " -n <NUM> Set number of matches per player pairing (default " + << Params{}.num_matches << ")\n" + " -T <MSEC> Set player timeout in milliseconds (default " + << Params{}.timeout_msec << ")\n"; +} +int main(int argc, char **argv) { Params params; - params.refereePath = argv[1]; - vector<Player> players; - for (int i = 2; i < argc; i++) { - players.emplace_back(argv[i]); + bool multilog_fancy = true; + bool scheduler_verbose = false; + int num_threads = std::thread::hardware_concurrency() / 2; + + for (int i = 1; i < argc; i++) { + if (strcmp(argv[i], "-h") == 0 || strcmp(argv[i], "--help") == 0) { + usage(argv[0]); + return 0; + } else if (strcmp(argv[i], "--noansi") == 0) { + multilog_fancy = false; + } else if (strcmp(argv[i], "--vs") == 0) { + scheduler_verbose = true; + } else if (strcmp(argv[i], "--vr") == 0) { + params.referee_verbose = true; + } else if (strcmp(argv[i], "-j") == 0 && i + 1 < argc) { + char *endp; + num_threads = strtol(argv[i+1], &endp, 10); + if (!argv[i][0] || *endp || num_threads <= 0) { + cerr << "Invalid number to -j" << endl; + return 1; + } + i++; + } else if (strcmp(argv[i], "-n") == 0 && i + 1 < argc) { + char *endp; + params.num_matches = strtol(argv[i+1], &endp, 10); + if (!argv[i][0] || *endp || params.num_matches <= 0) { + cerr << "Invalid number to -n" << endl; + return 1; + } + i++; + } else if (strcmp(argv[i], "-T") == 0 && i + 1 < argc) { + char *endp; + params.timeout_msec = strtoull(argv[i+1], &endp, 10); + if (!argv[i][0] || *endp || params.timeout_msec <= 0) { + cerr << "Invalid number to -T" << endl; + return 1; + } + i++; + } else if (argv[i][0] == '-') { + cerr << "Unknown option/flag '" << argv[i] << "'" << endl; + return 1; + } else if (params.refereePath.empty()) { + params.refereePath = argv[i]; + } else { + players.emplace_back(argv[i]); + } + } + + if (players.empty()) { + usage(argv[0]); + return 1; } + MultiLog multiLog{multilog_fancy}; mkdirp(matchcachedir); mkdirp(playerlogdir); @@ -373,8 +432,8 @@ int main(int argc, char **argv) { signal(SIGPIPE, [](int){}); - Scheduler scheduler(num_threads); - fullCompetition(scheduler, players, params); + Scheduler scheduler(scheduler_verbose, num_threads); + fullCompetition(scheduler, multiLog, players, params); scheduler.finish(); vector<pair<string, int>> scores; diff --git a/multilog.cpp b/multilog.cpp index d2dce10..2d9f3d6 100644 --- a/multilog.cpp +++ b/multilog.cpp @@ -2,7 +2,6 @@ #include <string> #include <cassert> #include "multilog.h" -#include "params.h" static int uniqid() { @@ -12,7 +11,10 @@ static int uniqid() { MultiLog::Item::Item() - : id(uniqid()) {} + : id{uniqid()} {} + +MultiLog::MultiLog(bool fancy) + : fancy{fancy} {} int MultiLog::add(const string_view prefix) { lock_guard guard(mut); @@ -21,7 +23,7 @@ int MultiLog::add(const string_view prefix) { items.emplace_back(); items.back().line = prefix; - if (multilog_fancy) { + if (fancy) { cout << prefix << endl; } else { cout << "[" << items.back().id << "] " << prefix << endl; @@ -36,7 +38,7 @@ void MultiLog::append(int id, const string_view text) { size_t idx = findId(id); items[idx].line += text; - if (multilog_fancy) { + if (fancy) { redrawLine(idx); } else { cout << "[" << items[idx].id << "]... " << text << endl; @@ -19,12 +19,15 @@ class MultiLog { mutex mut; vector<Item> items; + const bool fancy; // Methods assume mutex is taken. size_t findId(int id); void redrawLine(size_t idx); public: + MultiLog(bool fancy); + // Returns id of new item int add(const string_view prefix); void append(int id, const string_view text); diff --git a/params.h b/params.h deleted file mode 100644 index 3caff86..0000000 --- a/params.h +++ /dev/null @@ -1,16 +0,0 @@ -#pragma once - - -// Actual variable needs to be const as well to enable C++ default-internal linkage -const char *const matchcachedir = "comp_cache"; -const char *const playerlogdir = "comp_playerlogs"; -const char *const gamelogdir = "comp_gamelogs"; - -const int num_threads = 2; - -const int num_matches = 5; -const int timeout_msec = 5000; - -const bool multilog_fancy = true; -const bool referee_verbose = false; -const bool scheduler_verbose = false; diff --git a/referee.cpp b/referee.cpp index d215c3d..08770a7 100644 --- a/referee.cpp +++ b/referee.cpp @@ -2,7 +2,6 @@ #include <tuple> #include <cstdlib> #include "referee.h" -#include "params.h" // Timeout in milliseconds for the feature negotiation at the start of the // protocol. If the referee hasn't sent anything after this timeout, we assume @@ -10,14 +9,17 @@ #define FEATURE_TIMEOUT 200 -Referee::Referee(const string_view execname, const vector<string> &players) - : proc(execname), refereeExecname(execname), numPlayers(players.size()) { +Referee::Referee(bool verbose, const string_view execname, const vector<string> &players) + : verbose(verbose) + , proc(execname) + , refereeExecname(execname) + , numPlayers(players.size()) { proc.run(); reftag = "REF(" + to_string(proc.getPid()) + ") "; - if (referee_verbose) { + if (verbose) { cout << reftag << "starting for:"; for (const string &p : players) cout << " " << p; cout << endl; @@ -37,7 +39,7 @@ Referee::Referee(const string_view execname, const vector<string> &players) } Referee::~Referee() { - if (referee_verbose) { + if (verbose) { cout << reftag << "stopping" << endl; } proc.wait(); @@ -53,7 +55,7 @@ Referee::Event Referee::nextEvent() { string &command = *ocommand; - if (referee_verbose) { + if (verbose) { cout << reftag << "read <" << command << ">" << endl; } @@ -82,7 +84,7 @@ Referee::Event Referee::nextEvent() { return ReadEvent{ player, [this](const string &line) { - if (referee_verbose) { + if (verbose) { cout << reftag << "write <" << line << ">" << endl; } @@ -190,7 +192,7 @@ void Referee::queryFeatures() { exit(1); } - if (referee_verbose) { + if (verbose) { cout << reftag << "read <" << *line << ">" << endl; } @@ -13,6 +13,8 @@ using namespace std; class Referee { + const bool verbose; + Process proc; string refereeExecname; int numPlayers; @@ -51,7 +53,7 @@ public: }; using Event = variant<ReadEvent, WriteEvent, GamelogEvent, EndEvent, ErrorEvent>; - Referee(const string_view execname, const vector<string> &players); + Referee(bool verbose, const string_view execname, const vector<string> &players); ~Referee(); Event nextEvent(); diff --git a/scheduler.cpp b/scheduler.cpp index 6ed66ac..740eb3a 100644 --- a/scheduler.cpp +++ b/scheduler.cpp @@ -2,9 +2,8 @@ #include <chrono> #include <cassert> #include "scheduler.h" -#include "params.h" -void Scheduler::workerEntry() { +void Scheduler::workerEntry(bool verbose) { while (true) { Job *job = nullptr; @@ -17,20 +16,21 @@ void Scheduler::workerEntry() { } if (job) { - if (scheduler_verbose) { + if (verbose) { cout << "SCHED(" << this_thread::get_id() << ") running job" << endl; } job->callback(); - if (scheduler_verbose) { + delete job; + if (verbose) { cout << "SCHED(" << this_thread::get_id() << ") finished job" << endl; } } else if (finishFlag) { - if (scheduler_verbose) { + if (verbose) { cout << "SCHED(" << this_thread::get_id() << ") spotted finishFlag" << endl; } break; } else { - if (scheduler_verbose) { + if (verbose) { cout << "SCHED(" << this_thread::get_id() << ") no job, waiting" << endl; } this_thread::sleep_for(chrono::milliseconds(100)); @@ -38,17 +38,17 @@ void Scheduler::workerEntry() { } } -Scheduler::Scheduler(int nthreads) - : nthreads(nthreads) { +Scheduler::Scheduler(bool verbose, int nthreads) + : verbose(verbose), nthreads(nthreads) { assert(nthreads > 0); workers.reserve(nthreads); for (int i = 0; i < nthreads; i++) { - workers.emplace_back([this]() { workerEntry(); }); + workers.emplace_back([this]() { workerEntry(this->verbose); }); } - if (scheduler_verbose) { + if (verbose) { cout << "SCHED " << nthreads << " workers spawned" << endl; } } @@ -66,7 +66,7 @@ void Scheduler::submit(const function<void()> &func) { void Scheduler::finish() { if (hasJoined) return; - if (scheduler_verbose) { + if (verbose) { cout << "SCHED finish()" << endl; } @@ -76,13 +76,13 @@ void Scheduler::finish() { } for (int i = 0; i < nthreads; i++) { - if (scheduler_verbose) { + if (verbose) { cout << "SCHED Joining worker " << i << "..." << endl; } workers[i].join(); } - if (scheduler_verbose) { + if (verbose) { cout << "SCHED All workers joined" << endl; } diff --git a/scheduler.h b/scheduler.h index 95da7c7..5018a65 100644 --- a/scheduler.h +++ b/scheduler.h @@ -17,6 +17,8 @@ class Scheduler { : callback(callback) {} }; + bool verbose; + queue<Job*> jobs; bool finishFlag = false; mutex commMut; @@ -25,12 +27,12 @@ class Scheduler { vector<thread> workers; - void workerEntry(); + void workerEntry(bool verbose); public: const int nthreads; - Scheduler(int nthreads); + Scheduler(bool verbose, int nthreads); ~Scheduler(); // func is run in child thread |