initial commit
This commit is contained in:
commit
61aa14398a
15 changed files with 4628 additions and 0 deletions
257
mcp_shared.cc
Normal file
257
mcp_shared.cc
Normal file
|
|
@ -0,0 +1,257 @@
|
|||
#pragma once
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdio.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdarg.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/wait.h>
|
||||
#include <sys/time.h>
|
||||
#include <sys/resource.h>
|
||||
#include <signal.h>
|
||||
#include <time.h>
|
||||
|
||||
#include "board.h"
|
||||
#include "mcp.h"
|
||||
|
||||
enum exit_reason {
|
||||
/* Invalid moves disqualify the player. */
|
||||
INVALID_MOVE_0 = 1,
|
||||
INVALID_MOVE_1,
|
||||
/* Crashes do, too. */
|
||||
CRASH_0,
|
||||
CRASH_1,
|
||||
/* Couldn't execute a player. This is a configuration error. */
|
||||
EXEC_FAILED
|
||||
};
|
||||
|
||||
/* Time given to players after softlimit has been exhausted. */
|
||||
//#define CLEANUP_TIME 1 /* seconds */
|
||||
|
||||
static struct rlimit cpu_limit = { RLIM_INFINITY, RLIM_INFINITY };
|
||||
static struct rlimit mem_limit = { RLIM_INFINITY, RLIM_INFINITY };
|
||||
static bool debug = false;
|
||||
|
||||
static struct player {
|
||||
volatile pid_t pid;
|
||||
int pipe_from_player;
|
||||
int pipe_to_player;
|
||||
|
||||
volatile bool soft_timeout;
|
||||
volatile bool hard_timeout;
|
||||
} player[2];
|
||||
|
||||
static struct player * volatile current_player = NULL;
|
||||
|
||||
/* Specify seconds = RLIM_INFINITY to disarm timer. */
|
||||
static void
|
||||
arm_timer(time_t seconds)
|
||||
{
|
||||
struct itimerval t;
|
||||
|
||||
t.it_interval.tv_sec = 0;
|
||||
t.it_interval.tv_usec = 0;
|
||||
t.it_value.tv_sec = (seconds == (int)RLIM_INFINITY) ? 0 : seconds;
|
||||
t.it_value.tv_usec = 0;
|
||||
|
||||
if (setitimer(ITIMER_REAL, &t, NULL) < 0)
|
||||
abort();
|
||||
}
|
||||
|
||||
static void
|
||||
alarm_handler(int signum, siginfo_t *, void *)
|
||||
{
|
||||
assert(signum == SIGALRM);
|
||||
|
||||
struct player *p = current_player;
|
||||
assert(p);
|
||||
|
||||
if (!p->soft_timeout) {
|
||||
p->soft_timeout = true;
|
||||
kill(p->pid, SIGXCPU);
|
||||
arm_timer(static_cast<time_t>(cpu_limit.rlim_max - cpu_limit.rlim_cur));
|
||||
} else {
|
||||
p->hard_timeout = true;
|
||||
kill(p->pid, SIGKILL);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static char const *
|
||||
si_code_str(int si_code)
|
||||
{
|
||||
switch(si_code) {
|
||||
case CLD_EXITED: return "exited";
|
||||
case CLD_KILLED: return "killed";
|
||||
case CLD_DUMPED: return "coredumped";
|
||||
case CLD_TRAPPED: return "trapped";
|
||||
default: break;
|
||||
}
|
||||
return "unknown";
|
||||
}
|
||||
|
||||
static char const *
|
||||
signal_str(int sig)
|
||||
{
|
||||
switch(sig)
|
||||
{
|
||||
#define CASE(x) case x: return #x;
|
||||
CASE(SIGHUP); CASE(SIGINT); CASE(SIGILL); CASE(SIGABRT);
|
||||
CASE(SIGSEGV); CASE(SIGFPE); CASE(SIGPIPE); CASE(SIGKILL);
|
||||
CASE(SIGTERM); CASE(SIGALRM); CASE(SIGUSR1); CASE(SIGUSR2);
|
||||
CASE(SIGBUS); CASE(SIGCHLD);
|
||||
#undef CASE
|
||||
}
|
||||
return "unknown";
|
||||
}
|
||||
|
||||
static void
|
||||
child_handler(int signum, siginfo_t *si, void *)
|
||||
{
|
||||
/* For any SIGCHLD code other than STOP/CONT, we print additional info:
|
||||
* si_code contains what happened to the child. If the child was terminated
|
||||
* with a signal (most common case), then si_status additionally contains
|
||||
* the signal that caused termination.
|
||||
*/
|
||||
if ((si->si_code != CLD_STOPPED) &&
|
||||
(si->si_code != CLD_CONTINUED)) {
|
||||
// si_status contains the signal that caused the child to terminate
|
||||
switch(si->si_status) {
|
||||
case SIGKILL:
|
||||
case SIGSEGV:
|
||||
printf("\n\033[31mCHILD signal: %d ", si->si_status);
|
||||
printf("(%s,%s)\033[0m\n", si_code_str(si->si_code), signal_str(si->si_status));
|
||||
printf("killing the other one\n");
|
||||
for (int i = 0; i < 2; i++) {
|
||||
int status;
|
||||
kill(player[i].pid, SIGTERM);
|
||||
kill(player[i].pid, SIGCONT);
|
||||
waitpid(player[i].pid, &status, 0);
|
||||
}
|
||||
printf("MCP exiting\n");
|
||||
exit(1);
|
||||
break;
|
||||
case SIGTERM:
|
||||
printf("\n\033[32mCHILD terminated successfully\033[0m\n");
|
||||
break;
|
||||
default:
|
||||
printf(" HANDLEME!\n");
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void __attribute__((noreturn))
|
||||
exit_msg(enum exit_reason r, const char *msg, ...)
|
||||
{
|
||||
va_list ap;
|
||||
va_start(ap, msg);
|
||||
vfprintf(stderr, msg, ap);
|
||||
exit(r);
|
||||
va_end(ap);
|
||||
}
|
||||
|
||||
static bool
|
||||
player_move(struct player *the_player,
|
||||
const game_state *state,
|
||||
game_move *move)
|
||||
{
|
||||
bool succ;
|
||||
|
||||
the_player->hard_timeout = false;
|
||||
the_player->soft_timeout = false;
|
||||
|
||||
/* Arm timer with softlimit */
|
||||
current_player = the_player;
|
||||
arm_timer(static_cast<time_t>(cpu_limit.rlim_cur));
|
||||
|
||||
if (kill(the_player->pid, SIGCONT) < 0) return false;
|
||||
|
||||
ssize_t written = serialize_state(the_player->pipe_to_player, state);
|
||||
if (the_player->hard_timeout) goto timeout;
|
||||
if (written < 0)
|
||||
return false;
|
||||
|
||||
succ = deserialize_move(the_player->pipe_from_player, move);
|
||||
if (the_player->hard_timeout) goto timeout;
|
||||
if (!succ) return false;
|
||||
|
||||
/* Stop player and disarm timer. */
|
||||
if (kill(the_player->pid, SIGSTOP) < 0) return false;
|
||||
arm_timer(static_cast<time_t>(RLIM_INFINITY));
|
||||
current_player = NULL;
|
||||
|
||||
return true;
|
||||
|
||||
timeout:
|
||||
fprintf(stderr, "Player timeout!\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
static void
|
||||
print_usage()
|
||||
{
|
||||
fprintf(stderr, "Usage: mcp [-s state_string] [-t soft-player-time] [-m soft-player-mem]\n"
|
||||
" [-T hard-player-time] [-M hard-player-mem]\n"
|
||||
" [-d]\n"
|
||||
" player1 player2\n"
|
||||
" state_string - 65 character string representing an initial field state\n"
|
||||
" (this is the string the MCP prints before every move.)\n"
|
||||
" player-time - CPU time per turn in seconds\n"
|
||||
" player-mem - Memory limit per player in megabytes\n"
|
||||
" -d - turn on debugging, program won't exit on invalid move\n");
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
setup_signal_handlers()
|
||||
{
|
||||
/* Ignore SIGPIPE. Accessing dead pipes now returns EPIPE. */
|
||||
struct sigaction pact;
|
||||
pact.sa_handler = SIG_IGN;
|
||||
sigemptyset(&pact.sa_mask);
|
||||
pact.sa_restorer = 0;
|
||||
pact.sa_sigaction = 0;
|
||||
pact.sa_flags = 0;
|
||||
if (sigaction(SIGPIPE, &pact, NULL) != 0)
|
||||
abort();
|
||||
|
||||
/* Handle SIGALRM. */
|
||||
struct sigaction aact;
|
||||
aact.sa_handler = 0;
|
||||
sigemptyset(&aact.sa_mask);
|
||||
aact.sa_restorer = 0;
|
||||
aact.sa_sigaction = alarm_handler;
|
||||
aact.sa_flags = SA_SIGINFO | SA_RESTART;
|
||||
if (sigaction(SIGALRM, &aact, NULL) != 0)
|
||||
abort();
|
||||
|
||||
// Catch SIGCHLD
|
||||
struct sigaction cact;
|
||||
cact.sa_handler = 0;
|
||||
sigemptyset(&cact.sa_mask);
|
||||
cact.sa_restorer = 0;
|
||||
cact.sa_sigaction = child_handler;
|
||||
cact.sa_flags = SA_SIGINFO | SA_RESTART /*| SA_NOCLDSTOP*/;
|
||||
if (sigaction(SIGCHLD, &cact, NULL) != 0)
|
||||
abort();
|
||||
}
|
||||
|
||||
|
||||
static void getTimeStr(char *timebuf, size_t len)
|
||||
{
|
||||
time_t rawtime;
|
||||
struct tm *timeinfo;
|
||||
time(&rawtime);
|
||||
if (rawtime == (time_t)-1) {
|
||||
perror("time()");
|
||||
}
|
||||
timeinfo = localtime(&rawtime);
|
||||
if (!timeinfo) {
|
||||
perror("localtime()");
|
||||
}
|
||||
strftime(timebuf, len, "%H:%M:%S", timeinfo);
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue