示例#1
0
    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
示例#2
0
    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()
示例#3
0
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"]
示例#4
0
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"]
示例#5
0
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)
示例#6
0
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),
        }
示例#7
0
 def __init__(self, directory):
     self.boundary = PipeBoundary(directory)
     self.boundary.create_queue(self.get_queue_descriptor())
示例#8
0
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())
示例#9
0
 def __init__(self, driver_process_dir):
     self.boundary = PipeBoundary(driver_process_dir)
     self.proxy = InterfaceProxy(self)
示例#10
0
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)
示例#11
0
 def __init__(self, driver_dir):
     self.boundary = PipeBoundary(driver_dir)
示例#12
0
 def __init__(self, directory):
     self.boundary = PipeBoundary(directory)
示例#13
0
 def __init__(self, sandbox_dir):
     self.boundary = PipeBoundary(sandbox_dir)
示例#14
0
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