Add PyThreadState_SetAsyncExc(long, PyObject *). · python/cpython@b8b6d0c (original) (raw)
3 files changed
lines changed
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -74,6 +74,9 @@ typedef struct _ts { | ||
74 | 74 | int tick_counter; |
75 | 75 | int gilstate_counter; |
76 | 76 | |
77 | +PyObject *async_exc; /* Asynchronous exception to raise */ | |
78 | +long thread_id; /* Thread id where this tstate was created */ | |
79 | + | |
77 | 80 | /* XXX signal handlers should also be here */ |
78 | 81 | |
79 | 82 | } PyThreadState; |
@@ -93,6 +96,7 @@ PyAPI_FUNC(void) PyThreadState_DeleteCurrent(void); | ||
93 | 96 | PyAPI_FUNC(PyThreadState *) PyThreadState_Get(void); |
94 | 97 | PyAPI_FUNC(PyThreadState *) PyThreadState_Swap(PyThreadState *); |
95 | 98 | PyAPI_FUNC(PyObject *) PyThreadState_GetDict(void); |
99 | +PyAPI_FUNC(int) PyThreadState_SetAsyncExc(long, PyObject *); | |
96 | 100 | |
97 | 101 | |
98 | 102 | /* Variable and macro for in-line access to current thread state */ |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -290,7 +290,7 @@ static PyTypeObject gentype = { | ||
290 | 290 | |
291 | 291 | extern int _PyThread_Started; /* Flag for Py_Exit */ |
292 | 292 | |
293 | -static PyThread_type_lock interpreter_lock = 0; | |
293 | +static PyThread_type_lock interpreter_lock = 0; /* This is the GIL */ | |
294 | 294 | static long main_thread = 0; |
295 | 295 | |
296 | 296 | void |
@@ -773,6 +773,11 @@ eval_frame(PyFrameObject *f) | ||
773 | 773 | Py_MakePendingCalls() above. */ |
774 | 774 | |
775 | 775 | if (--_Py_Ticker < 0) { |
776 | +if (*next_instr == SETUP_FINALLY) { | |
777 | +/* Make the last opcode before | |
778 | + a try: finally: block uninterruptable. */ | |
779 | + goto fast_next_opcode; | |
780 | + } | |
776 | 781 | _Py_Ticker = _Py_CheckInterval; |
777 | 782 | tstate->tick_counter++; |
778 | 783 | if (things_to_do) { |
@@ -805,6 +810,17 @@ eval_frame(PyFrameObject *f) | ||
805 | 810 | PyThread_acquire_lock(interpreter_lock, 1); |
806 | 811 | if (PyThreadState_Swap(tstate) != NULL) |
807 | 812 | Py_FatalError("ceval: orphan tstate"); |
813 | + | |
814 | +/* Check for thread interrupts */ | |
815 | + | |
816 | +if (tstate->async_exc != NULL) { | |
817 | +x = tstate->async_exc; | |
818 | +tstate->async_exc = NULL; | |
819 | +PyErr_SetNone(x); | |
820 | +Py_DECREF(x); | |
821 | +why = WHY_EXCEPTION; | |
822 | + goto on_error; | |
823 | + } | |
808 | 824 | } |
809 | 825 | #endif |
810 | 826 | } |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -143,6 +143,8 @@ PyThreadState_New(PyInterpreterState *interp) | ||
143 | 143 | tstate->use_tracing = 0; |
144 | 144 | tstate->tick_counter = 0; |
145 | 145 | tstate->gilstate_counter = 0; |
146 | +tstate->async_exc = NULL; | |
147 | +tstate->thread_id = PyThread_get_thread_ident(); | |
146 | 148 | |
147 | 149 | tstate->dict = NULL; |
148 | 150 | |
@@ -179,6 +181,7 @@ PyThreadState_Clear(PyThreadState *tstate) | ||
179 | 181 | ZAP(tstate->frame); |
180 | 182 | |
181 | 183 | ZAP(tstate->dict); |
184 | +ZAP(tstate->async_exc); | |
182 | 185 | |
183 | 186 | ZAP(tstate->curexc_type); |
184 | 187 | ZAP(tstate->curexc_value); |
@@ -296,6 +299,32 @@ PyThreadState_GetDict(void) | ||
296 | 299 | } |
297 | 300 | |
298 | 301 | |
302 | +/* Asynchronously raise an exception in a thread. | |
303 | + Requested by Just van Rossum and Alex Martelli. | |
304 | + To prevent naive misuse, you must write your own exception | |
305 | + to call this. Must be called with the GIL held. | |
306 | + Returns the number of tstates modified; if it returns a number | |
307 | + greater than one, you're in trouble, and you should call it again | |
308 | + with exc=NULL to revert the effect. This raises no exceptions. */ | |
309 | + | |
310 | +int | |
311 | +PyThreadState_SetAsyncExc(long id, PyObject *exc) { | |
312 | +PyThreadState *tstate = PyThreadState_Get(); | |
313 | +PyInterpreterState *interp = tstate->interp; | |
314 | +PyThreadState *p; | |
315 | +int count = 0; | |
316 | +for (p = interp->tstate_head; p != NULL; p = p->next) { | |
317 | +if (p->thread_id != id) | |
318 | +continue; | |
319 | +ZAP(p->async_exc); | |
320 | +Py_XINCREF(exc); | |
321 | +p->async_exc = exc; | |
322 | +count += 1; | |
323 | + } | |
324 | +return count; | |
325 | +} | |
326 | + | |
327 | + | |
299 | 328 | /* Routines for advanced debuggers, requested by David Beazley. |
300 | 329 | Don't use unless you know what you are doing! */ |
301 | 330 | |
@@ -320,6 +349,7 @@ PyThreadState_Next(PyThreadState *tstate) { | ||
320 | 349 | return tstate->next; |
321 | 350 | } |
322 | 351 | |
352 | + | |
323 | 353 | /* Python "auto thread state" API. */ |
324 | 354 | #ifdef WITH_THREAD |
325 | 355 |