@@ -11,6 +11,7 @@ class ParallelProcessing: ...
1111
1212import sys
1313import time
14+ import ctypes
1415import signal
1516import threading
1617from functools import wraps
@@ -32,7 +33,7 @@ class ParallelProcessing: ...
3233 HookFunction ,
3334)
3435from typing_extensions import Generic , ParamSpec
35- from typing import List , Callable , Optional , Union , Mapping , Sequence , Tuple , Generator
36+ from typing import List , Optional , Union , Mapping , Sequence , Tuple , Generator
3637
3738
3839Threads : set ['Thread' ] = set ()
@@ -56,7 +57,6 @@ class Thread(threading.Thread, Generic[_Target_P, _Target_T]):
5657
5758 # threading.Thread stuff
5859 _initialized : bool
59- _run : Callable
6060
6161 def __init__ (
6262 self ,
@@ -116,23 +116,29 @@ def _wrap_target(
116116 def wrapper (
117117 * args : _Target_P .args , ** kwargs : _Target_P .kwargs
118118 ) -> Union [_Target_T , None ]:
119- self .status = 'Running'
119+ try :
120+ self .status = 'Running'
120121
121- global Threads
122- Threads .add (self )
122+ global Threads
123+ Threads .add (self )
123124
124- try :
125- self ._returned_value = target (* args , ** kwargs )
126- except Exception as e :
127- if not any (isinstance (e , ignore ) for ignore in self .ignore_errors ):
128- self .status = 'Errored'
129- self .errors .append (e )
130- return
125+ try :
126+ self ._returned_value = target (* args , ** kwargs )
127+ except Exception as e :
128+ if not any (isinstance (e , ignore ) for ignore in self .ignore_errors ):
129+ self .status = 'Errored'
130+ self .errors .append (e )
131+ return
132+
133+ self .status = 'Invoking hooks'
134+ self ._invoke_hooks ()
135+ Threads .remove (self )
136+ self .status = 'Completed'
131137
132- self . status = 'Invoking hooks'
133- self ._invoke_hooks ()
134- Threads . remove ( self )
135- self . status = 'Completed'
138+ except SystemExit :
139+ self .status = 'Killed'
140+ print ( 'KILLED ident: %s' % self . ident )
141+ return
136142
137143 return wrapper
138144
@@ -157,27 +163,6 @@ def _handle_exceptions(self) -> None:
157163 for e in self .errors :
158164 raise e
159165
160- def global_trace (self , frame , event : str , arg ) -> Optional [Callable ]:
161- if event == 'call' :
162- return self .local_trace
163-
164- def local_trace (self , frame , event : str , arg ):
165- if self .status == 'Kill Scheduled' and event == 'line' :
166- print ('KILLED ident: %s' % self .ident )
167- self .status = 'Killed'
168- raise SystemExit ()
169- return self .local_trace
170-
171- def _run_with_trace (self ) -> None :
172- """This will replace `threading.Thread`'s `run()` method"""
173- if not self ._run :
174- raise exceptions .ThreadNotInitializedError (
175- 'Running `_run_with_trace` may cause unintended behaviour, run `start` instead'
176- )
177-
178- sys .settrace (self .global_trace )
179- self ._run ()
180-
181166 @property
182167 def result (self ) -> _Target_T :
183168 """
@@ -274,17 +259,33 @@ def kill(self, yielding: bool = False, timeout: float = 5) -> bool:
274259
275260 Returns
276261 -------
277- :returns bool: False if the it exceeded the timeout
262+ :returns bool: False if the it exceeded the timeout without being killed
278263
279264 Raises
280265 ------
266+ ValueError: If the thread ident does not exist
281267 ThreadNotInitializedError: If the thread is not initialized
282268 ThreadNotRunningError: If the thread is not running
283269 """
284270 if not self .is_alive ():
285271 raise exceptions .ThreadNotRunningError ()
286272
287273 self .status = 'Kill Scheduled'
274+
275+ res : int = ctypes .pythonapi .PyThreadState_SetAsyncExc (
276+ ctypes .c_long (self .ident ), ctypes .py_object (SystemExit )
277+ )
278+
279+ if res == 0 :
280+ raise ValueError ('Thread IDENT does not exist' )
281+ elif res > 1 :
282+ # Unexpected behaviour, something seriously went wrong
283+ # https://docs.python.org/3/c-api/init.html#c.PyThreadState_SetAsyncExc
284+ ctypes .pythonapi .PyThreadState_SetAsyncExc (self .ident , None )
285+ raise SystemError (
286+ f'Killing thread with ident [{ self .ident } ] failed!\n PyThreadState_SetAsyncExc returned: { res } '
287+ )
288+
288289 if not yielding :
289290 return True
290291
@@ -308,8 +309,6 @@ def start(self) -> None:
308309 if self .is_alive ():
309310 raise exceptions .ThreadStillRunningError ()
310311
311- self ._run = self .run
312- self .run = self ._run_with_trace
313312 super ().start ()
314313
315314
0 commit comments