Mercurial > hg
view tests/killdaemons.py @ 51925:3a90a6fd710d
dirstate: subclass the new dirstate Protocol class
Behold the chaos that ensues. We'll use the generated *.pyi files to apply type
annotations to the interface, and see how much agrees with the documentation.
Since the CamelCase name was used to try to work around pytype issues with zope
interfaces and is a new innovation this cycle (see c1d7ac70980b), drop the
CamelCase name. I think the Protocol classes *should* be CamelCase, but that
can be done later in one pass. For now, the CamelCase alias is extra noise in
the *.pyi files.
author | Matt Harbison <matt_harbison@yahoo.com> |
---|---|
date | Thu, 26 Sep 2024 18:52:46 -0400 |
parents | ca7bde5dbafb |
children | 23cc79e04c28 |
line wrap: on
line source
#!/usr/bin/env python3 import os import signal import sys import time if os.name == 'nt': import ctypes _BOOL = ctypes.c_long _DWORD = ctypes.c_ulong _UINT = ctypes.c_uint _HANDLE = ctypes.c_void_p ctypes.windll.kernel32.CloseHandle.argtypes = [_HANDLE] ctypes.windll.kernel32.CloseHandle.restype = _BOOL ctypes.windll.kernel32.GetLastError.argtypes = [] ctypes.windll.kernel32.GetLastError.restype = _DWORD ctypes.windll.kernel32.OpenProcess.argtypes = [_DWORD, _BOOL, _DWORD] ctypes.windll.kernel32.OpenProcess.restype = _HANDLE ctypes.windll.kernel32.TerminateProcess.argtypes = [_HANDLE, _UINT] ctypes.windll.kernel32.TerminateProcess.restype = _BOOL ctypes.windll.kernel32.WaitForSingleObject.argtypes = [_HANDLE, _DWORD] ctypes.windll.kernel32.WaitForSingleObject.restype = _DWORD def _check(ret, expectederr=None): if ret == 0: winerrno = ctypes.GetLastError() if winerrno == expectederr: return True raise ctypes.WinError(winerrno) def kill(pid, logfn, tryhard=True): logfn('# Killing daemon process %d' % pid) PROCESS_TERMINATE = 1 PROCESS_QUERY_INFORMATION = 0x400 SYNCHRONIZE = 0x00100000 WAIT_OBJECT_0 = 0 WAIT_TIMEOUT = 258 WAIT_FAILED = _DWORD(0xFFFFFFFF).value handle = ctypes.windll.kernel32.OpenProcess( PROCESS_TERMINATE | SYNCHRONIZE | PROCESS_QUERY_INFORMATION, False, pid, ) if handle is None: _check(0, 87) # err 87 when process not found return # process not found, already finished try: r = ctypes.windll.kernel32.WaitForSingleObject(handle, 100) if r == WAIT_OBJECT_0: pass # terminated, but process handle still available elif r == WAIT_TIMEOUT: _check(ctypes.windll.kernel32.TerminateProcess(handle, -1)) elif r == WAIT_FAILED: _check(0) # err stored in GetLastError() # TODO?: forcefully kill when timeout # and ?shorter waiting time? when tryhard==True r = ctypes.windll.kernel32.WaitForSingleObject(handle, 100) # timeout = 100 ms if r == WAIT_OBJECT_0: pass # process is terminated elif r == WAIT_TIMEOUT: logfn('# Daemon process %d is stuck') elif r == WAIT_FAILED: _check(0) # err stored in GetLastError() except: # re-raises ctypes.windll.kernel32.CloseHandle(handle) # no _check, keep error raise _check(ctypes.windll.kernel32.CloseHandle(handle)) else: def kill(pid, logfn, tryhard=True): try: os.kill(pid, 0) logfn('# Killing daemon process %d' % pid) os.kill(pid, signal.SIGTERM) if tryhard: for i in range(10): time.sleep(0.05) os.kill(pid, 0) else: time.sleep(0.1) os.kill(pid, 0) logfn('# Daemon process %d is stuck - really killing it' % pid) os.kill(pid, signal.SIGKILL) except ProcessLookupError: pass def killdaemons(pidfile, tryhard=True, remove=False, logfn=None): if not logfn: logfn = lambda s: s # Kill off any leftover daemon processes try: pids = [] with open(pidfile) as fp: for line in fp: try: pid = int(line) if pid <= 0: raise ValueError except ValueError: logfn( '# Not killing daemon process %s - invalid pid' % line.rstrip() ) continue pids.append(pid) for pid in pids: kill(pid, logfn, tryhard) if remove: os.unlink(pidfile) except IOError: pass if __name__ == '__main__': if len(sys.argv) > 1: (path,) = sys.argv[1:] else: path = os.environ["DAEMON_PIDS"] killdaemons(path, remove=True)