]> granicus.if.org Git - python/commitdiff
Add PyThreadState_SetAsyncExc(long, PyObject *).
authorGuido van Rossum <guido@python.org>
Sat, 28 Jun 2003 21:53:52 +0000 (21:53 +0000)
committerGuido van Rossum <guido@python.org>
Sat, 28 Jun 2003 21:53:52 +0000 (21:53 +0000)
A new API (only accessible from C) to interrupt a thread by sending it
an exception.  This is not always effective, but might help some people.
Requested by Just van Rossum and Alex Martelli.  It is intentional
that you have to write your own C extension to call it from Python.

Docs will have to wait.

Include/pystate.h
Python/ceval.c
Python/pystate.c

index c1182a67833ec72d69ddff27324a5348ca7dc5b1..63a5cb863dcbff31de316df079c313e5f6efbf52 100644 (file)
@@ -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 */
index 4e5b4722991c0e4374e4017765665025a802ce35..07862d0efed57c592fa1b3c063e98db363abb70b 100644 (file)
@@ -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
                }
index b083f8cb944b2c37535d48191ec65d815ebaa148..e8cb54715612bdaffb0166d1f1cedae7646d1631 100644 (file)
@@ -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