diff --git a/pyisolate/runtime/thread.py b/pyisolate/runtime/thread.py index 49ec62f..e7a44a2 100644 --- a/pyisolate/runtime/thread.py +++ b/pyisolate/runtime/thread.py @@ -513,6 +513,40 @@ def snapshot(self) -> dict: "child_work_max": self.child_work_max, } + def reset_config(self) -> dict[str, Any]: + """Return the runtime options consumed by ``reset`` for reconfiguration.""" + return { + "policy": self.policy, + "cpu_ms": self.cpu_quota_ms, + "mem_bytes": self.mem_quota_bytes, + "wall_time_ms": self.wall_time_ms, + "open_files_max": self.open_files_max, + "network_ops_max": self.network_ops_max, + "output_bytes_max": self.output_bytes_max, + "child_work_max": self.child_work_max, + "allowed_imports": sorted(self.allowed_imports) + if self.allowed_imports is not None + else None, + "numa_node": self.numa_node, + "capabilities": serialize_capabilities(self._capabilities), + } + + def apply_reset_config(self, config: dict[str, Any]) -> None: + """Apply a serialized runtime config produced by ``reset_config``.""" + self.policy = config.get("policy") + self.cpu_quota_ms = config.get("cpu_ms") + self.mem_quota_bytes = config.get("mem_bytes") + self.wall_time_ms = config.get("wall_time_ms") + self.open_files_max = config.get("open_files_max") + self.network_ops_max = config.get("network_ops_max") + self.output_bytes_max = config.get("output_bytes_max") + self.child_work_max = config.get("child_work_max") + self.numa_node = config.get("numa_node") + self.allowed_imports = self._merge_allowed_imports( + self.policy, config.get("allowed_imports") + ) + self._capabilities = deserialize_capabilities(config.get("capabilities")) + @staticmethod def _estimate_output_size(item: Any) -> int: if isinstance(item, bytes): diff --git a/pyisolate/supervisor.py b/pyisolate/supervisor.py index 1cadb4f..7830f2d 100644 --- a/pyisolate/supervisor.py +++ b/pyisolate/supervisor.py @@ -67,21 +67,12 @@ def quarantine(self, reason: str = "manual quarantine") -> None: self._supervisor.quarantine(self._thread.name, reason) def reset(self) -> None: + reset_config = self._thread.reset_config() + # Keep this reset path aligned with Supervisor.spawn warm-pool reuse. self._thread.reset( self._thread.name, - policy=self._thread.policy, - cpu_ms=self._thread.cpu_quota_ms, - mem_bytes=self._thread.mem_quota_bytes, - wall_time_ms=self._thread.wall_time_ms, - open_files_max=self._thread.open_files_max, - network_ops_max=self._thread.network_ops_max, - output_bytes_max=self._thread.output_bytes_max, - child_work_max=self._thread.child_work_max, - allowed_imports=sorted(self._thread.allowed_imports) - if self._thread.allowed_imports is not None - else None, - numa_node=self._thread.numa_node, cgroup_path=self._thread._cgroup_path, + **reset_config, ) def recycle(self) -> "Sandbox": @@ -254,42 +245,36 @@ def spawn( try: cg_path = cgroup.create(name, cpu_ms, mem_bytes) temp_dir = recovery.allocate_temp_dir(name) + reset_config = { + "policy": policy, + "cpu_ms": cpu_ms, + "mem_bytes": mem_bytes, + "wall_time_ms": wall_time_ms, + "open_files_max": open_files_max, + "network_ops_max": network_ops_max, + "output_bytes_max": output_bytes_max, + "child_work_max": child_work_max, + "allowed_imports": allowed_imports, + "numa_node": numa_node, + "capabilities": capabilities, + } if self._warm_pool: thread = self._warm_pool.pop() + # Intentionally mirrors Sandbox.reset by sharing reset_config. thread.reset( name, - policy=policy, - cpu_ms=cpu_ms, - mem_bytes=mem_bytes, - wall_time_ms=wall_time_ms, - open_files_max=open_files_max, - network_ops_max=network_ops_max, - output_bytes_max=output_bytes_max, - child_work_max=child_work_max, - allowed_imports=allowed_imports, - numa_node=numa_node, cgroup_path=cg_path, - capabilities=capabilities, + **reset_config, ) thread._on_violation = self._alerts.notify thread._tracer = self._tracer else: thread = SandboxThread( name=name, - policy=policy, - cpu_ms=cpu_ms, - mem_bytes=mem_bytes, - wall_time_ms=wall_time_ms, - open_files_max=open_files_max, - network_ops_max=network_ops_max, - output_bytes_max=output_bytes_max, - child_work_max=child_work_max, - allowed_imports=allowed_imports, + **reset_config, on_violation=self._alerts.notify, tracer=self._tracer, - numa_node=numa_node, cgroup_path=cg_path, - capabilities=capabilities, ) thread.start() thread._temp_dir = temp_dir