From 4a5204a9677406205d21b1a4e2375dac689653c0 Mon Sep 17 00:00:00 2001 From: Alex Vandiver Date: Tue, 5 Apr 2022 11:34:56 -0700 Subject: [PATCH] timeout: Warn if the thread did not exit. As noted in the docstring for this function, the timeout is best-effort only -- if the thread is blocked in a syscall, it will not service the exception until it returns. It can also choose to catch and ignore the TimeoutExpired; in either case it will still be running even after the `timeout()` function returns. Raising a vare TimeoutExpired it still somewhat accurate, but obscures that the backend thread may still be running along merrily. Notice such cases, and log a warning about them. (cherry picked from commit 3af2c8d9a3aa8f0887c91ffe2b86ffb6a011819a) --- zerver/lib/timeout.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/zerver/lib/timeout.py b/zerver/lib/timeout.py index c85d73cc1f..46ef82c083 100644 --- a/zerver/lib/timeout.py +++ b/zerver/lib/timeout.py @@ -1,4 +1,5 @@ import ctypes +import logging import sys import threading import time @@ -82,6 +83,12 @@ def timeout(timeout: float, func: Callable[[], ResultT]) -> ResultT: # Re-raise the exception we sent, if possible, so the # stacktrace originates in the slow code raise thread.exc_info[1].with_traceback(thread.exc_info[2]) + # If we don't have that for some reason (e.g. we failed to + # kill it), just raise from here; the thread _may still be + # running_ because it failed to see any of our exceptions, and + # we just ignore it. + if thread.is_alive(): + logging.warning("Failed to time out backend thread") raise TimeoutExpired if thread.exc_info[1] is not None: