class SandboxProcessServer: def __init__(self, *, sandbox_dir, executable): self.executable = executable self.boundary = PipeBoundary(sandbox_dir) self.boundary.create_channel(SANDBOX_PROCESS_CHANNEL) self.boundary.create_queue(SANDBOX_REQUEST_QUEUE) self.done = False self.process = None self.process_exit_stack = ExitStack() def run(self): logger.debug("starting process...") with self.boundary.open_channel(SANDBOX_PROCESS_CHANNEL, PipeBoundarySide.SERVER) as pipes: connection = SandboxProcessConnection(**pipes) self.process = self.process_exit_stack.enter_context( self.executable.run(connection)) logger.debug("process started") while not self.done: logger.debug("handling requests...") self.boundary.handle_request( SANDBOX_REQUEST_QUEUE, self.handle_request, ) def handle_request(self, *, wait): assert not self.done assert wait in ("0", "1") time_usage = self.executable.get_time_usage(self.process) memory_usage = self.executable.get_memory_usage(self.process) message = stacktrace = "" if wait == "1": self.done = True self.process = None try: self.process_exit_stack.close() except AlgorithmRuntimeError as e: message, stacktrace = e.args return { "error": message, "stacktrace": stacktrace, "time_usage": str(time_usage), "memory_usage": str(memory_usage), }
class SandboxProcessClient: def __init__(self, directory): self.boundary = PipeBoundary(directory) def get_info(self, *, wait=False): response = self.boundary.send_request( SANDBOX_REQUEST_QUEUE, wait=str(int(bool(wait))), ) response["time_usage"] = float(response["time_usage"]) response["memory_usage"] = int(response["memory_usage"]) return SandboxProcessInfo(**response) @contextmanager def connect(self): logger.debug("connecting to process...") with self.boundary.open_channel(SANDBOX_PROCESS_CHANNEL, PipeBoundarySide.CLIENT) as pipes: yield SandboxProcessConnection(**pipes) def wait(self): return self.get_info(wait=True)