mirror of
https://github.com/kovidgoyal/calibre.git
synced 2026-05-27 09:12:34 -04:00
Make workaround_windows_shutdown_hang more robust against PID reuse
Use a process handle instead of PIDs Fixes #3054
This commit is contained in:
committed by
Kovid Goyal
parent
500958021e
commit
101f4c0e62
@@ -406,9 +406,26 @@ def workaround_windows_shutdown_hang(timeout: float=1.0):
|
||||
# So it is likely a hang in the Loader Lock. Or memory corruption during exit.
|
||||
# So we run a child process that will wait a second for the parent process
|
||||
# to exit and if it hasnt, will kill it.
|
||||
# We pass an inheritable process handle so the child does not accidentally
|
||||
# kill the wrong process due to PID reuse.
|
||||
import ctypes
|
||||
|
||||
from calibre.utils.ipc.simple_worker import start_pipe_worker
|
||||
start_pipe_worker(
|
||||
f'from calibre.utils import kill_parent_if_needed; kill_parent_if_needed({os.getpid()!r}, {timeout!r})')
|
||||
SYNCHRONIZE = 0x00100000
|
||||
PROCESS_TERMINATE = 0x0001
|
||||
kernel32 = ctypes.windll.kernel32
|
||||
# Use OpenProcess on our own PID with bInheritHandle=True to get a real,
|
||||
# inheritable handle. We set the restype to c_void_p so the 64-bit handle
|
||||
# value is not truncated on x64 systems.
|
||||
kernel32.OpenProcess.restype = ctypes.c_void_p
|
||||
handle = kernel32.OpenProcess(SYNCHRONIZE | PROCESS_TERMINATE, True, os.getpid())
|
||||
if handle:
|
||||
try:
|
||||
start_pipe_worker(
|
||||
f'from calibre.utils import kill_parent_if_needed; kill_parent_if_needed({handle!r}, {timeout!r})',
|
||||
pass_fds=(handle,))
|
||||
finally:
|
||||
kernel32.CloseHandle(handle)
|
||||
|
||||
|
||||
def run_gui_(opts, args, app, gui_debug=None):
|
||||
|
||||
@@ -56,23 +56,17 @@ def pickle_binary_string(data):
|
||||
return PROTO + b'\x02' + BINSTRING + struct.pack(b'<i', len(data)) + data + STOP
|
||||
|
||||
|
||||
def kill_parent_if_needed(parent_pid: int, timeout: float = 1.0) -> None:
|
||||
def kill_parent_if_needed(parent_process_handle: int, timeout: float = 1.0) -> None:
|
||||
import ctypes
|
||||
import os
|
||||
import signal
|
||||
SYNCHRONIZE = 0x00100000
|
||||
WAIT_OBJECT_0 = 0x00000000
|
||||
WAIT_TIMEOUT = 0x00000102
|
||||
|
||||
kernel32 = ctypes.windll.kernel32
|
||||
handle = kernel32.OpenProcess(SYNCHRONIZE, False, parent_pid)
|
||||
if not handle:
|
||||
return
|
||||
try:
|
||||
result = kernel32.WaitForSingleObject(handle, int(timeout * 1000))
|
||||
result = kernel32.WaitForSingleObject(parent_process_handle, int(timeout * 1000))
|
||||
if result == WAIT_OBJECT_0:
|
||||
return
|
||||
if result == WAIT_TIMEOUT:
|
||||
os.kill(parent_pid, signal.SIGTERM)
|
||||
kernel32.TerminateProcess(parent_process_handle, 1)
|
||||
finally:
|
||||
kernel32.CloseHandle(handle)
|
||||
kernel32.CloseHandle(parent_process_handle)
|
||||
|
||||
Reference in New Issue
Block a user