Home Assistant Unofficial Reference 2024.12.1
thread.py
Go to the documentation of this file.
1 """Threading util helpers."""
2 
3 import ctypes
4 import inspect
5 import logging
6 import threading
7 from typing import Any
8 
9 THREADING_SHUTDOWN_TIMEOUT = 10
10 
11 _LOGGER = logging.getLogger(__name__)
12 
13 
14 def deadlock_safe_shutdown() -> None:
15  """Shutdown that will not deadlock."""
16  # threading._shutdown can deadlock forever
17  # see https://github.com/justengel/continuous_threading#shutdown-update
18  # for additional detail
19  remaining_threads = [
20  thread
21  for thread in threading.enumerate()
22  if thread is not threading.main_thread()
23  and not thread.daemon
24  and thread.is_alive()
25  ]
26 
27  if not remaining_threads:
28  return
29 
30  timeout_per_thread = THREADING_SHUTDOWN_TIMEOUT / len(remaining_threads)
31  for thread in remaining_threads:
32  try:
33  thread.join(timeout_per_thread)
34  except Exception as err: # noqa: BLE001
35  _LOGGER.warning("Failed to join thread: %s", err)
36 
37 
38 def async_raise(tid: int, exctype: Any) -> None:
39  """Raise an exception in the threads with id tid."""
40  if not inspect.isclass(exctype):
41  raise TypeError("Only types can be raised (not instances)")
42 
43  c_tid = ctypes.c_ulong(tid) # changed in python 3.7+
44  res = ctypes.pythonapi.PyThreadState_SetAsyncExc(c_tid, ctypes.py_object(exctype))
45 
46  if res == 1:
47  return
48 
49  # "if it returns a number greater than one, you're in trouble,
50  # and you should call it again with exc=NULL to revert the effect"
51  ctypes.pythonapi.PyThreadState_SetAsyncExc(c_tid, None)
52  raise SystemError("PyThreadState_SetAsyncExc failed")
53 
54 
55 class ThreadWithException(threading.Thread):
56  """A thread class that supports raising exception in the thread from another thread.
57 
58  Based on
59  https://stackoverflow.com/questions/323972/is-there-any-way-to-kill-a-thread/49877671
60 
61  """
62 
63  def raise_exc(self, exctype: Any) -> None:
64  """Raise the given exception type in the context of this thread."""
65  assert self.ident
66  async_raise(self.ident, exctype)
None raise_exc(self, Any exctype)
Definition: thread.py:63
None deadlock_safe_shutdown()
Definition: thread.py:14
None async_raise(int tid, Any exctype)
Definition: thread.py:38