diff options
-rw-r--r-- | Include/pystate.h | 4 | ||||
-rw-r--r-- | Python/ceval.c | 18 | ||||
-rw-r--r-- | Python/pystate.c | 30 |
3 files changed, 51 insertions, 1 deletions
diff --git a/Include/pystate.h b/Include/pystate.h index c1182a6..63a5cb8 100644 --- a/Include/pystate.h +++ b/Include/pystate.h @@ -74,6 +74,9 @@ typedef struct _ts { int tick_counter; int gilstate_counter; + PyObject *async_exc; /* Asynchronous exception to raise */ + long thread_id; /* Thread id where this tstate was created */ + /* XXX signal handlers should also be here */ } PyThreadState; @@ -93,6 +96,7 @@ PyAPI_FUNC(void) PyThreadState_DeleteCurrent(void); PyAPI_FUNC(PyThreadState *) PyThreadState_Get(void); PyAPI_FUNC(PyThreadState *) PyThreadState_Swap(PyThreadState *); PyAPI_FUNC(PyObject *) PyThreadState_GetDict(void); +PyAPI_FUNC(int) PyThreadState_SetAsyncExc(long, PyObject *); /* Variable and macro for in-line access to current thread state */ diff --git a/Python/ceval.c b/Python/ceval.c index 4e5b472..07862d0 100644 --- a/Python/ceval.c +++ b/Python/ceval.c @@ -290,7 +290,7 @@ static PyTypeObject gentype = { extern int _PyThread_Started; /* Flag for Py_Exit */ -static PyThread_type_lock interpreter_lock = 0; +static PyThread_type_lock interpreter_lock = 0; /* This is the GIL */ static long main_thread = 0; void @@ -773,6 +773,11 @@ eval_frame(PyFrameObject *f) Py_MakePendingCalls() above. */ if (--_Py_Ticker < 0) { + if (*next_instr == SETUP_FINALLY) { + /* Make the last opcode before + a try: finally: block uninterruptable. */ + goto fast_next_opcode; + } _Py_Ticker = _Py_CheckInterval; tstate->tick_counter++; if (things_to_do) { @@ -805,6 +810,17 @@ eval_frame(PyFrameObject *f) PyThread_acquire_lock(interpreter_lock, 1); if (PyThreadState_Swap(tstate) != NULL) Py_FatalError("ceval: orphan tstate"); + + /* Check for thread interrupts */ + + if (tstate->async_exc != NULL) { + x = tstate->async_exc; + tstate->async_exc = NULL; + PyErr_SetNone(x); + Py_DECREF(x); + why = WHY_EXCEPTION; + goto on_error; + } } #endif } diff --git a/Python/pystate.c b/Python/pystate.c index b083f8c..e8cb547 100644 --- a/Python/pystate.c +++ b/Python/pystate.c @@ -143,6 +143,8 @@ PyThreadState_New(PyInterpreterState *interp) tstate->use_tracing = 0; tstate->tick_counter = 0; tstate->gilstate_counter = 0; + tstate->async_exc = NULL; + tstate->thread_id = PyThread_get_thread_ident(); tstate->dict = NULL; @@ -179,6 +181,7 @@ PyThreadState_Clear(PyThreadState *tstate) ZAP(tstate->frame); ZAP(tstate->dict); + ZAP(tstate->async_exc); ZAP(tstate->curexc_type); ZAP(tstate->curexc_value); @@ -296,6 +299,32 @@ PyThreadState_GetDict(void) } +/* Asynchronously raise an exception in a thread. + Requested by Just van Rossum and Alex Martelli. + To prevent naive misuse, you must write your own exception + to call this. Must be called with the GIL held. + Returns the number of tstates modified; if it returns a number + greater than one, you're in trouble, and you should call it again + with exc=NULL to revert the effect. This raises no exceptions. */ + +int +PyThreadState_SetAsyncExc(long id, PyObject *exc) { + PyThreadState *tstate = PyThreadState_Get(); + PyInterpreterState *interp = tstate->interp; + PyThreadState *p; + int count = 0; + for (p = interp->tstate_head; p != NULL; p = p->next) { + if (p->thread_id != id) + continue; + ZAP(p->async_exc); + Py_XINCREF(exc); + p->async_exc = exc; + count += 1; + } + return count; +} + + /* Routines for advanced debuggers, requested by David Beazley. Don't use unless you know what you are doing! */ @@ -320,6 +349,7 @@ PyThreadState_Next(PyThreadState *tstate) { return tstate->next; } + /* Python "auto thread state" API. */ #ifdef WITH_THREAD |