@@ -11,6 +11,7 @@ class ParallelProcessing: ...
1111
1212import sys
1313import time
14+ import ctypes
1415import signal
1516import threading
1617from functools import wraps
@@ -116,23 +117,29 @@ def _wrap_target(
116117 def wrapper (
117118 * args : _Target_P .args , ** kwargs : _Target_P .kwargs
118119 ) -> Union [_Target_T , None ]:
119- self .status = 'Running'
120+ try :
121+ self .status = 'Running'
120122
121- global Threads
122- Threads .add (self )
123+ global Threads
124+ Threads .add (self )
123125
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
126+ try :
127+ self ._returned_value = target (* args , ** kwargs )
128+ except Exception as e :
129+ if not any (isinstance (e , ignore ) for ignore in self .ignore_errors ):
130+ self .status = 'Errored'
131+ self .errors .append (e )
132+ return
133+
134+ self .status = 'Invoking hooks'
135+ self ._invoke_hooks ()
136+ Threads .remove (self )
137+ self .status = 'Completed'
131138
132- self . status = 'Invoking hooks'
133- self ._invoke_hooks ()
134- Threads . remove ( self )
135- self . status = 'Completed'
139+ except SystemExit :
140+ self .status = 'Killed'
141+ print ( 'KILLED ident: %s' % self . ident )
142+ return
136143
137144 return wrapper
138145
@@ -157,27 +164,6 @@ def _handle_exceptions(self) -> None:
157164 for e in self .errors :
158165 raise e
159166
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-
181167 @property
182168 def result (self ) -> _Target_T :
183169 """
@@ -285,6 +271,20 @@ def kill(self, yielding: bool = False, timeout: float = 5) -> bool:
285271 raise exceptions .ThreadNotRunningError ()
286272
287273 self .status = 'Kill Scheduled'
274+ res : int = ctypes .pythonapi .PyThreadState_SetAsyncExc (
275+ ctypes .c_long (self .ident ), ctypes .py_object (SystemExit )
276+ )
277+
278+ if res == 0 :
279+ raise ValueError ('Thread IDENT does not exist' )
280+ elif res > 1 :
281+ # Unexpected behaviour, something seriously went wrong
282+ # https://docs.python.org/3/c-api/init.html#c.PyThreadState_SetAsyncExc
283+ ctypes .pythonapi .PyThreadState_SetAsyncExc (self .ident , None )
284+ raise SystemError (
285+ f'Killing thread with ident [{ self .ident } ] failed!\n PyThreadState_SetAsyncExc returned: { res } '
286+ )
287+
288288 if not yielding :
289289 return True
290290
@@ -308,8 +308,6 @@ def start(self) -> None:
308308 if self .is_alive ():
309309 raise exceptions .ThreadStillRunningError ()
310310
311- self ._run = self .run
312- self .run = self ._run_with_trace
313311 super ().start ()
314312
315313
0 commit comments