def __init__(self, *, interface_text, sandbox_process_dir, driver_process_dir): self.sandbox_dir = sandbox_process_dir self.boundary = PipeBoundary(driver_process_dir) self.interface = InterfaceDefinition.compile(interface_text) self.boundary.create_queue(DRIVER_PROCESS_QUEUE) self.run_driver_iterator = None
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()
class SandboxClient: def __init__(self, sandbox_dir): self.boundary = PipeBoundary(sandbox_dir) @contextmanager def run(self, algorithm_dir): response = self.boundary.send_request( SANDBOX_QUEUE, algorithm_dir=algorithm_dir, ) yield response["sandbox_process_dir"]
class DriverClient: def __init__(self, driver_dir): self.boundary = PipeBoundary(driver_dir) @contextmanager def run(self, *, interface_text, sandbox_process_dir): response = self.boundary.send_request( DRIVER_QUEUE, interface_text=interface_text, sandbox_process_dir=sandbox_process_dir, ) yield response["driver_process_dir"]
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)
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), }
def __init__(self, directory): self.boundary = PipeBoundary(directory) self.boundary.create_queue(self.get_queue_descriptor())
class MetaServer: """ Implements a server that starts child servers. This class listens on a synchronous queue: it reads the parameters for the server to start, starts the child server in a new thread, listening in a temporary directory, writes the location of the temporary directory, and deletes the temporary directory when the child server stops. """ def __init__(self, directory): self.boundary = PipeBoundary(directory) self.boundary.create_queue(self.get_queue_descriptor()) @abstractmethod def get_queue_descriptor(self): pass @abstractmethod def create_child_server(self, child_server_dir, **request_payloads): pass @abstractmethod def run_child_server(self, child_server): pass @abstractmethod def create_response(self, child_server_dir): pass def run(self): while True: try: self.boundary.handle_request( self.get_queue_descriptor(), self.handle_request, ) except StopMetaServer: break def _run_child_server(self, child_server, child_server_dir): try: self.run_child_server(child_server) finally: child_server_dir.cleanup() def handle_request(self, **request_payloads): self.handle_stop_request(request_payloads) child_server_dir = TemporaryDirectory(dir="/tmp") child_server = self.create_child_server(child_server_dir.name, **request_payloads) Thread(target=lambda: self._run_child_server( child_server, child_server_dir)).start() return self.create_response(child_server_dir.name) def handle_stop_request(self, request_payloads): if any(request_payloads.values()): return raise StopMetaServer def stop(self): self.boundary.send_empty_request(self.get_queue_descriptor())
def __init__(self, driver_process_dir): self.boundary = PipeBoundary(driver_process_dir) self.proxy = InterfaceProxy(self)
class DriverProcessClient: def __init__(self, driver_process_dir): self.boundary = PipeBoundary(driver_process_dir) self.proxy = InterfaceProxy(self) def call(self, name, args, callbacks): callback_list = list(callbacks.items()) response = self.send_call(args, name, callback_list) while True: response_it = self.response_iterator(response) if next(response_it): # has callback index = next(response_it) args = list(response_it) name, f = callback_list[index] return_value = f(*args) response = self.send_callback_return(return_value) else: # no callbacks break if next(response_it): # has return value return next(response_it) else: return None def send_call(self, args, name, callback_list): return self.send_request( FunctionCall(function_name=name, parameters=args, accepted_callbacks={ name: f.__code__.co_argcount for name, f in callback_list })) def send_callback_return(self, return_value): return self.send_request(CallbackReturn(return_value=return_value)) def send_begin_main(self, global_variables): return self.send_request(MainBegin(global_variables=global_variables)) def send_end_main(self): return self.send_request(MainEnd()) def send_exit(self): return self.send_request(Exit()) def send_request(self, request): request_payload = "\n".join(str(l) for l in serialize_request(request)) payloads = self.boundary.send_request(DRIVER_PROCESS_QUEUE, request=request_payload) driver_error = payloads["driver_error"] if driver_error: raise InterfaceError(driver_error) sandbox_error = payloads["sandbox_error"] if sandbox_error: raise SandboxError(sandbox_error) return payloads["response"] def response_iterator(self, response): items = [int(line.strip()) for line in response.splitlines()] return iter(items)
def __init__(self, driver_dir): self.boundary = PipeBoundary(driver_dir)
def __init__(self, directory): self.boundary = PipeBoundary(directory)
def __init__(self, sandbox_dir): self.boundary = PipeBoundary(sandbox_dir)
class DriverProcessServer: def __init__(self, *, interface_text, sandbox_process_dir, driver_process_dir): self.sandbox_dir = sandbox_process_dir self.boundary = PipeBoundary(driver_process_dir) self.interface = InterfaceDefinition.compile(interface_text) self.boundary.create_queue(DRIVER_PROCESS_QUEUE) self.run_driver_iterator = None def run(self): with ExitStack() as stack: logger.debug("connecting to process...") sandbox_connection = stack.enter_context( SandboxProcessClient(self.sandbox_dir).connect()) self.run_driver_iterator = drive_interface( sandbox_connection=sandbox_connection, interface=self.interface) self.process_requests() def handle_request(self, request): current_request = self.deserialize(request) try: response = self.run_driver_iterator.send(current_request) except CommunicationBroken: logger.warning(f"communication with process broken") return { "sandbox_error": "communication broken", } assert all(isinstance(x, int) for x in response) return {"response": "\n".join(str(x) for x in response)} def deserialize(self, request): deserializer = deserialize_request() assert next(deserializer) is None lines = request.splitlines() lines_it = iter(lines) try: for line in lines_it: deserializer.send(line) except StopIteration as e: extra_lines = list(lines_it) if extra_lines: raise ValueError( f"extra lines '{extra_lines!s:.50}'") from None result = e.value else: raise ValueError(f"too few lines") return result def process_requests(self): assert next(self.run_driver_iterator) is None while True: self.boundary.handle_request(DRIVER_PROCESS_QUEUE, self.handle_request) try: assert next(self.run_driver_iterator) is None except StopIteration: break