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