Commit 4805b98a authored by whooie's avatar whooie
Browse files

add thread termination upon receipt of a keyboard interrupt

parent e0aa5f23
from __future__ import annotations
import threading
import inspect
import ctypes
import sys
import time
def _async_raise(tid, exctype):
"""
Raises the exception, performs cleanup if needed.
"""
if not inspect.isclass(exctype):
raise TypeError("Only types can be raised (not instances)")
res = ctypes.pythonapi.PyThreadState_SetAsyncExc(
ctypes.c_long(tid), ctypes.py_object(exctype))
if res == 0:
raise ValueError("invalid thread id")
elif res != 1:
# if it returns a number greater than one, you're in trouble,
# and you should call it again with exc=NULL to revert the effect"""
ctypes.pythonapi.PyThreadState_SetAsyncExc(tid, 0)
raise SystemError("PyThreadState_SetAsyncExc failed")
class TerminableThread(threading.Thread):
def _get_my_tid(self):
"""
Determines this (self's) thread id.
"""
if not self.is_alive():
raise threading.ThreadError("the thread is not active")
# do we have it cached?
if hasattr(self, "_thread_id"):
return self._thread_id
# no, look for it in the _active dict
for tid, tobj in threading._active.items():
if tobj is self:
self._thread_id = tid
return tid
raise AssertionError("could not determine the thread's id")
def raise_exc(self, exctype):
"""
Raises the given exception type in the context of this thread.
"""
_async_raise(self._get_my_tid(), exctype)
def terminate(self):
"""
Raises SystemExit in the context of the given thread, which should
cause the thread to exit silently (unless caught).
"""
self.raise_exc(SystemExit)
class Controller:
def precmd(self, *args):
raise NotImplementedError
......@@ -51,19 +103,32 @@ class Controller:
except NotImplementedError:
print("[control] no pre-command defined")
threads = [
(name, threading.Thread(target=fn, args=args))
(name, TerminableThread(target=fn, args=args))
for name, fn in actions.items()
]
for name, thread in threads:
print(f"[control] action `{name}` started")
thread.start()
while len(threads) > 0:
try:
while len(threads) > 0:
for name, thread in threads:
if not thread.is_alive():
thread.join()
print(f"[control] action `{name}` finished")
threads.remove((name, thread))
time.sleep(1)
except KeyboardInterrupt:
print("[control] received keyboard interrupt; terminating all actions...")
for name, thread in threads:
if not thread.is_alive():
thread.join()
print(f"[control] action `{name}` finished")
threads.remove((name, thread))
time.sleep(1)
thread.terminate()
while len(threads) > 0:
for name, thread in threads:
if not thread.is_alive():
thread.join()
print(f"[control] action `{name}` terminated")
threads.remove((name, thread))
time.sleep(1)
return None
try:
self.postcmd(*args)
except NotImplementedError:
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment