Extract versioned stage contracts, model shared CPU/iGPU/NPU memory constraints, and add explicit graph routes, reservations, output acknowledgements, and worker retirement. Fix native Windows shared-buffer lifetime and host paths. Document qualified hardware paths and remaining rollout gates.
71 lines
2.4 KiB
Python
71 lines
2.4 KiB
Python
# SPDX-License-Identifier: Apache-2.0
|
|
"""Standalone native-Windows process identity and bounded tree retirement.
|
|
|
|
This file is executed by the worker interpreter from WSL; it imports no engine
|
|
or third-party packages. A launcher PID is never treated as a Windows PID.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import ctypes
|
|
import json
|
|
import os
|
|
import subprocess
|
|
import sys
|
|
from ctypes import wintypes
|
|
|
|
|
|
def _api():
|
|
kernel = ctypes.WinDLL("kernel32", use_last_error=True)
|
|
kernel.OpenProcess.argtypes = [wintypes.DWORD, wintypes.BOOL, wintypes.DWORD]
|
|
kernel.OpenProcess.restype = wintypes.HANDLE
|
|
kernel.CloseHandle.argtypes = [wintypes.HANDLE]
|
|
kernel.GetProcessTimes.argtypes = [wintypes.HANDLE] + [ctypes.POINTER(wintypes.FILETIME)] * 4
|
|
kernel.WaitForSingleObject.argtypes = [wintypes.HANDLE, wintypes.DWORD]
|
|
return kernel
|
|
|
|
|
|
def _created(kernel, handle) -> int:
|
|
times = [wintypes.FILETIME() for _ in range(4)]
|
|
if not kernel.GetProcessTimes(handle, *(ctypes.byref(t) for t in times)):
|
|
raise ctypes.WinError(ctypes.get_last_error())
|
|
return (times[0].dwHighDateTime << 32) | times[0].dwLowDateTime
|
|
|
|
|
|
def identity() -> dict:
|
|
kernel = _api()
|
|
handle = kernel.OpenProcess(0x1000, False, os.getpid()) # QUERY_LIMITED_INFORMATION
|
|
if not handle:
|
|
raise ctypes.WinError(ctypes.get_last_error())
|
|
try:
|
|
return {"pid": os.getpid(), "created_filetime": _created(kernel, handle)}
|
|
finally:
|
|
kernel.CloseHandle(handle)
|
|
|
|
|
|
def retire(pid: int, created_filetime: int) -> bool:
|
|
kernel = _api()
|
|
handle = kernel.OpenProcess(0x1000 | 0x100000, False, pid) # query + synchronize
|
|
if not handle:
|
|
return False # parent absence alone says nothing about its descendants
|
|
try:
|
|
if _created(kernel, handle) != created_filetime:
|
|
return False # stale identity must not kill a reused PID
|
|
result = subprocess.run(
|
|
["taskkill.exe", "/PID", str(pid), "/T", "/F"],
|
|
capture_output=True,
|
|
timeout=10,
|
|
creationflags=subprocess.CREATE_NO_WINDOW,
|
|
)
|
|
return result.returncode == 0 and kernel.WaitForSingleObject(handle, 5000) == 0
|
|
finally:
|
|
kernel.CloseHandle(handle)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
try:
|
|
print(json.dumps({"drained": retire(int(sys.argv[1]), int(sys.argv[2]))}))
|
|
except Exception as exc:
|
|
print(json.dumps({"drained": False, "error": str(exc)}))
|
|
sys.exit(1)
|