summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Include/pystate.h4
-rw-r--r--Python/ceval.c18
-rw-r--r--Python/pystate.c30
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