| 24 | |
| 25 | |
| 26 | class Process: |
| 27 | def __init__( |
| 28 | self, |
| 29 | config: Config, |
| 30 | target: Callable[[list[socket] | None], None], |
| 31 | sockets: list[socket], |
| 32 | ) -> None: |
| 33 | self.real_target = target |
| 34 | |
| 35 | self.parent_conn, self.child_conn = Pipe() |
| 36 | self.process = get_subprocess(config, self.target, sockets) |
| 37 | |
| 38 | def ping(self, timeout: float = 5) -> bool: |
| 39 | self.parent_conn.send(b"ping") |
| 40 | if self.parent_conn.poll(timeout): |
| 41 | self.parent_conn.recv() |
| 42 | return True |
| 43 | return False |
| 44 | |
| 45 | def pong(self) -> None: |
| 46 | self.child_conn.recv() |
| 47 | self.child_conn.send(b"pong") |
| 48 | |
| 49 | def always_pong(self) -> None: |
| 50 | while True: |
| 51 | self.pong() |
| 52 | |
| 53 | def target(self, sockets: list[socket] | None = None) -> Any: # pragma: no cover |
| 54 | if os.name == "nt": # pragma: py-not-win32 |
| 55 | # Windows doesn't support SIGTERM, so we use SIGBREAK instead. |
| 56 | # And then we raise SIGTERM when SIGBREAK is received. |
| 57 | # https://learn.microsoft.com/zh-cn/cpp/c-runtime-library/reference/signal?view=msvc-170 |
| 58 | signal.signal( |
| 59 | signal.SIGBREAK, # type: ignore[attr-defined] |
| 60 | lambda sig, frame: signal.raise_signal(signal.SIGTERM), |
| 61 | ) |
| 62 | |
| 63 | threading.Thread(target=self.always_pong, daemon=True).start() |
| 64 | return self.real_target(sockets) |
| 65 | |
| 66 | def is_alive(self, timeout: float = 5) -> bool: |
| 67 | if not self.process.is_alive(): |
| 68 | return False # pragma: full coverage |
| 69 | |
| 70 | return self.ping(timeout) |
| 71 | |
| 72 | def start(self) -> None: |
| 73 | self.process.start() |
| 74 | |
| 75 | def terminate(self) -> None: |
| 76 | if self.process.exitcode is None: # Process is still running |
| 77 | assert self.process.pid is not None |
| 78 | if os.name == "nt": # pragma: py-not-win32 |
| 79 | # Windows doesn't support SIGTERM. |
| 80 | # So send SIGBREAK, and then in process raise SIGTERM. |
| 81 | os.kill(self.process.pid, signal.CTRL_BREAK_EVENT) # type: ignore[attr-defined] |
| 82 | else: |
| 83 | os.kill(self.process.pid, signal.SIGTERM) |
no outgoing calls