CoolFace
Apppublic

moebiusT7/book-ocr-studio

sourceHugging Faceagpl-3.0updated 2d agoView on Hugging Face
0likes
owned_process.py93 linesDownload Raw Back to root
1"""Linux subprocess supervisor: release owned descendants when the caller exits.2 3A private pipe, not a PID/name scan, defines ownership. Closing the caller's4write end (including SIGKILL) asks the independent supervisor to stop its group.5Normal child exit and SIGTERM also clean up remaining group members.6"""7import json8import os9import select10import signal11import subprocess12import sys13import time14import weakref15from pathlib import Path16 17 18def _close(fd):19    try: os.close(fd)20    except OSError: pass21 22 23class OwnedPopen(subprocess.Popen):24    def __init__(self, args, **kwargs):25        if os.name != 'posix' or not sys.platform.startswith('linux'):26            raise RuntimeError('Owned process supervision requires Linux')27        if kwargs.pop('shell', False) or kwargs.get('preexec_fn') or kwargs.get('pass_fds'):28            raise ValueError('OwnedPopen requires direct argv without preexec_fn/pass_fds')29        reader, writer = os.pipe2(os.O_CLOEXEC)30        self._pipe_finalizer = None31        try:32            kwargs['start_new_session'] = True33            kwargs['pass_fds'] = (reader,)34            super().__init__([sys.executable, str(Path(__file__).resolve()), '--watch',35                              str(reader), json.dumps([os.fspath(a) for a in args])], **kwargs)36            self._pipe_finalizer = weakref.finalize(self, _close, writer)37        except BaseException:38            _close(writer)39            raise40        finally:41            _close(reader)42 43    def wait(self, timeout=None):44        result = super().wait(timeout=timeout)45        finalizer = getattr(self, '_pipe_finalizer', None)46        if finalizer: finalizer()47        return result48 49 50def _watch(reader, args):51    stopping = False52    def stop(signum, frame):53        nonlocal stopping54        stopping = True55    for sig in (signal.SIGTERM, signal.SIGINT, signal.SIGHUP):56        signal.signal(sig, stop)57    child = None58    result = 159    try:60        # Avoid starting a model if the caller already vanished during spawn.61        if select.select([reader], [], [], 0)[0] and not os.read(reader, 1):62            return 12563        child = subprocess.Popen(args, start_new_session=True, close_fds=True)64        while not stopping:65            code = child.poll()66            if code is not None:67                result = code if code >= 0 else 128-code68                break69            if select.select([reader], [], [], .1)[0] and not os.read(reader, 1):70                stopping = True71        if stopping: result = 14372    finally:73        if child:74            # Only the process group created above is signalled. External API75            # servers and other applications are never selected by name.76            try: os.killpg(child.pid, signal.SIGTERM)77            except ProcessLookupError: pass78            try: child.wait(timeout=2)79            except subprocess.TimeoutExpired: pass80            # The leader may exit before a model runner/grandchild does.81            time.sleep(.1)82            try: os.killpg(child.pid, signal.SIGKILL)83            except ProcessLookupError: pass84            child.wait()85        _close(reader)86    return result87 88 89if __name__ == '__main__':90    if len(sys.argv) != 4 or sys.argv[1] != '--watch':91        raise SystemExit('Internal owned-process supervisor')92    raise SystemExit(_watch(int(sys.argv[2]), json.loads(sys.argv[3])))93