"""Cross-platform PID-liveness probe shared by the crash-recoverable locks.Both the bucket lockfile(:mod:`adapters.persistence.storage.bucket`) and the auth-acquisition lock(:mod:`application.auth`) reclaim a stale lock by asking the OS whether therecorded holder PID is still a live process. This module is the single homefor that probe so the two lock subsystems cannot diverge on Windowsfailure-mode handling: a probe failure other than "PID does not exist" MUSTbe treated as alive, never as grounds to reclaim a lock this process cannotactually inspect."""from__future__importannotationsimportosimportsysfrom.errors.hierarchyimportInternalInvariantErrorfrom.loggingimportget_logger_log=get_logger(__name__)
[docs]defpid_is_alive(pid:int)->bool:"""Return whether ``pid`` is a live process, per a best-effort OS probe. Returns ``True`` when the OS reports ``pid`` as a live process and ``False`` when the OS reports it as gone (or ``pid`` is not a valid process id). A permission error probing the PID (it exists but belongs to another user, or this process lacks the query right) is treated as alive: from the caller's perspective a lock held by that PID cannot be safely reclaimed. On Windows the probe distinguishes ``ERROR_INVALID_PARAMETER`` (87, returned by ``OpenProcess`` for a PID that no longer exists) from every other ``OpenProcess`` failure (access-denied against a foreign or protected process, a PID recycled onto a process this call cannot query, etc.). Only the former is treated as dead; every other failure is treated as alive so a lock is never wrongly reclaimed just because this process could not inspect its holder. """ifpid<=0:returnFalseifsys.platform=="win32":return_pid_is_alive_windows(pid)try:os.kill(pid,0)exceptProcessLookupError:returnFalseexceptPermissionError:_log.debug("pid liveness probe denied for pid=%s; treating as alive",pid)returnTruereturnTrue
def_pid_is_alive_windows(pid:int)->bool:"""Windows ``OpenProcess`` + ``GetExitCodeProcess`` liveness probe. ``os.kill(pid, 0)`` on Windows reports terminated-but-cached PIDs as alive until the kernel reclaims the PID, so the probe goes through ``OpenProcess`` + ``GetExitCodeProcess`` instead: a process whose exit code is not ``STILL_ACTIVE`` (259) is dead even if its PID is still allocated. The ``sys.platform == "win32"`` block, rather than an early raise off Windows, is what establishes the platform for the ``ctypes`` Windows API below: it is the only guard shape every checker this project runs narrows on, so those references resolve when the tree is analysed for a platform that does not ship them. """ifsys.platform=="win32":importctypesfromctypesimportwintypesprocess_query_limited_information=0x1000still_active=259error_invalid_parameter=87kernel32=ctypes.WinDLL("kernel32",use_last_error=True)handle=kernel32.OpenProcess(process_query_limited_information,False,pid)ifnothandle:last_error=ctypes.get_last_error()missing=last_error==error_invalid_parameterifnotmissing:_log.debug("pid liveness probe unavailable for pid=%s (last_error=%s); treating as alive",pid,last_error,)returnnotmissingtry:code=wintypes.DWORD()ok=kernel32.GetExitCodeProcess(handle,ctypes.byref(code))ifnotok:returnTruereturncode.value==still_activefinally:kernel32.CloseHandle(handle)raiseInternalInvariantError(# pragma: no cover - dispatch never routes here"the Windows liveness probe is not available on this platform")__all__=["pid_is_alive"]