diff -r 3ff40811aeed Include/pythread.h --- a/Include/pythread.h Thu Aug 25 15:01:15 2011 +0200 +++ b/Include/pythread.h Fri Sep 02 08:17:42 2011 +0200 @@ -86,6 +86,14 @@ /* Cleanup after a fork */ PyAPI_FUNC(void) PyThread_ReInitTLS(void); +#ifndef Py_LIMITED_API +/* stops all threads different from 'tstate' at their next bytecode, + at which point they wait for the 'lock' to be available. Limited + use only; reserved for the 'transaction' module. */ +PyAPI_FUNC(void) _PyEval_SuspendOtherThreads(PyThreadState *, + PyThread_type_lock); +#endif + #ifdef __cplusplus } #endif diff -r 3ff40811aeed Python/ceval.c --- a/Python/ceval.c Thu Aug 25 15:01:15 2011 +0200 +++ b/Python/ceval.c Fri Sep 02 08:17:42 2011 +0200 @@ -231,7 +231,7 @@ &eval_breaker, \ GIL_REQUEST | \ _Py_atomic_load_relaxed(&pendingcalls_to_do) | \ - pending_async_exc) + pending_async_exc_or_threadstopper) #ifdef WITH_THREAD @@ -262,14 +262,17 @@ COMPUTE_EVAL_BREAKER(); \ } while (0) -#define SIGNAL_ASYNC_EXC() \ +#define SIGNAL_ASYNC_EXC(flag) \ do { \ - pending_async_exc = 1; \ + pending_async_exc_or_threadstopper |= (flag); \ _Py_atomic_store_relaxed(&eval_breaker, 1); \ } while (0) -#define UNSIGNAL_ASYNC_EXC() \ - do { pending_async_exc = 0; COMPUTE_EVAL_BREAKER(); } while (0) +#define UNSIGNAL_ASYNC_EXC(flag) \ + do { \ + pending_async_exc_or_threadstopper &= ~(flag); \ + COMPUTE_EVAL_BREAKER(); \ + } while (0) #ifdef WITH_THREAD @@ -288,9 +291,13 @@ static _Py_atomic_int gil_drop_request = {0}; /* Request for running pending calls. */ static _Py_atomic_int pendingcalls_to_do = {0}; -/* Request for looking at the `async_exc` field of the current thread state. +/* Request for looking at the `async_exc` field of the current thread state + (bit 0) or for acquiring the `threadstopper_lock` (bit 1). Guarded by the GIL. */ -static int pending_async_exc = 0; +static int pending_async_exc_or_threadstopper = 0; + +static PyThreadState *threadstopper = 0; +static PyThread_type_lock threadstopper_lock; #include "ceval_gil.h" @@ -400,9 +407,17 @@ Py_DECREF(threading); } +void +_PyEval_SuspendOtherThreads(PyThreadState *tstate, PyThread_type_lock lock) +{ + /* 'tstate' should be either the current thread's, or NULL */ + threadstopper = tstate; + threadstopper_lock = lock; +} + #else static _Py_atomic_int eval_breaker = {0}; -static int pending_async_exc = 0; +static int pending_async_exc_or_threadstopper = 0; #endif /* WITH_THREAD */ /* This function is used to signal that async exceptions are waiting to be @@ -411,7 +426,7 @@ void _PyEval_SignalAsyncExc(void) { - SIGNAL_ASYNC_EXC(); + SIGNAL_ASYNC_EXC(1); } /* Functions save_thread and restore_thread are always defined so @@ -1266,12 +1281,19 @@ if (PyThreadState_Swap(tstate) != NULL) Py_FatalError("ceval: orphan tstate"); } + while (pending_async_exc_or_threadstopper & 2) { + PyThread_type_lock lock = threadstopper_lock; + Py_BEGIN_ALLOW_THREADS + PyThread_acquire_lock(lock, 1); + PyThread_release_lock(lock); + Py_END_ALLOW_THREADS + } #endif /* Check for asynchronous exceptions. */ if (tstate->async_exc != NULL) { x = tstate->async_exc; tstate->async_exc = NULL; - UNSIGNAL_ASYNC_EXC(); + UNSIGNAL_ASYNC_EXC(1); PyErr_SetNone(x); Py_DECREF(x); why = WHY_EXCEPTION; diff -r 3ff40811aeed Python/ceval_gil.h --- a/Python/ceval_gil.h Thu Aug 25 15:01:15 2011 +0200 +++ b/Python/ceval_gil.h Fri Sep 02 08:17:42 2011 +0200 @@ -344,6 +344,10 @@ _Py_atomic_store_relaxed(&gil_last_holder, tstate); } + if (pending_async_exc_or_threadstopper & 2) { + UNSIGNAL_ASYNC_EXC(2); + } + MUTEX_LOCK(gil_mutex); _Py_ANNOTATE_RWLOCK_RELEASED(&gil_locked, /*is_write=*/1); _Py_atomic_store_relaxed(&gil_locked, 0); @@ -420,6 +424,10 @@ MUTEX_UNLOCK(gil_mutex); errno = err; + + if (threadstopper != NULL && threadstopper != tstate) { + SIGNAL_ASYNC_EXC(2); + } } void _PyEval_SetSwitchInterval(unsigned long microseconds)