diff -r 3ff40811aeed -r 0c747c744e88 Include/ceval.h --- a/Include/ceval.h Thu Aug 25 15:01:15 2011 +0200 +++ b/Include/ceval.h Tue Aug 30 16:02:06 2011 +0200 @@ -184,6 +184,15 @@ #define Py_END_ALLOW_THREADS PyEval_RestoreThread(_save); \ } +#ifndef Py_LIMITED_API +/* three internal functions that can be overridden by 3rd-party modules + that want to take more precise control over the GIL */ +typedef void (*_PyEval_GIL_func)(PyThreadState *); +PyAPI_FUNC(_PyEval_GIL_func) _PyEval_SetTakeGIL(_PyEval_GIL_func new); +PyAPI_FUNC(_PyEval_GIL_func) _PyEval_SetDropGIL(_PyEval_GIL_func new); +PyAPI_FUNC(_PyEval_GIL_func) _PyEval_SetBytecodeBarrier(_PyEval_GIL_func new); +#endif + #else /* !WITH_THREAD */ #define Py_BEGIN_ALLOW_THREADS { diff -r 3ff40811aeed -r 0c747c744e88 Python/ceval.c --- a/Python/ceval.c Thu Aug 25 15:01:15 2011 +0200 +++ b/Python/ceval.c Tue Aug 30 16:02:06 2011 +0200 @@ -231,7 +231,7 @@ &eval_breaker, \ GIL_REQUEST | \ _Py_atomic_load_relaxed(&pendingcalls_to_do) | \ - pending_async_exc) + pending_async_exc_or_bytecodebarrier) #ifdef WITH_THREAD @@ -264,12 +264,15 @@ #define SIGNAL_ASYNC_EXC() \ do { \ - pending_async_exc = 1; \ + pending_async_exc_or_bytecodebarrier |= 1; \ _Py_atomic_store_relaxed(&eval_breaker, 1); \ } while (0) #define UNSIGNAL_ASYNC_EXC() \ - do { pending_async_exc = 0; COMPUTE_EVAL_BREAKER(); } while (0) + do { \ + pending_async_exc_or_bytecodebarrier &= ~1; \ + COMPUTE_EVAL_BREAKER(); \ + } while (0) #ifdef WITH_THREAD @@ -288,12 +291,46 @@ 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 invoking the `bytecodebarrier_func` (bit 1). Guarded by the GIL. */ -static int pending_async_exc = 0; +static int pending_async_exc_or_bytecodebarrier = 0; #include "ceval_gil.h" +static _PyEval_GIL_func takegil_func = take_gil; +static _PyEval_GIL_func dropgil_func = drop_gil; +static _PyEval_GIL_func bytecodebarrier_func = NULL; + +_PyEval_GIL_func +_PyEval_SetTakeGIL(_PyEval_GIL_func new) { + _PyEval_GIL_func old = takegil_func; + takegil_func = new; + return old; +} +_PyEval_GIL_func +_PyEval_SetDropGIL(_PyEval_GIL_func new) { + _PyEval_GIL_func old = dropgil_func; + dropgil_func = new; + return old; +} +_PyEval_GIL_func +_PyEval_SetBytecodeBarrier(_PyEval_GIL_func new) { + /* NB. the bytecode barrier should be used with care: whenever we have + one, it will be invoked between *every* single bytecode. */ + _PyEval_GIL_func old = bytecodebarrier_func; + bytecodebarrier_func = new; + if (new != NULL) { + pending_async_exc_or_bytecodebarrier |= 2; + _Py_atomic_store_relaxed(&eval_breaker, 1); + } + else { + pending_async_exc_or_bytecodebarrier &= ~2; + COMPUTE_EVAL_BREAKER(); + } + return old; +} + int PyEval_ThreadsInitialized(void) { @@ -306,7 +343,7 @@ if (gil_created()) return; create_gil(); - take_gil(PyThreadState_GET()); + takegil_func(PyThreadState_GET()); main_thread = PyThread_get_thread_ident(); if (!pending_lock) pending_lock = PyThread_allocate_lock(); @@ -327,7 +364,7 @@ PyThreadState *tstate = PyThreadState_GET(); if (tstate == NULL) Py_FatalError("PyEval_AcquireLock: current thread state is NULL"); - take_gil(tstate); + takegil_func(tstate); } void @@ -337,7 +374,7 @@ We therefore avoid PyThreadState_GET() which dumps a fatal error in debug mode. */ - drop_gil((PyThreadState*)_Py_atomic_load_relaxed( + dropgil_func((PyThreadState*)_Py_atomic_load_relaxed( &_PyThreadState_Current)); } @@ -348,7 +385,7 @@ Py_FatalError("PyEval_AcquireThread: NULL new thread state"); /* Check someone has called PyEval_InitThreads() to create the lock */ assert(gil_created()); - take_gil(tstate); + takegil_func(tstate); if (PyThreadState_Swap(tstate) != NULL) Py_FatalError( "PyEval_AcquireThread: non-NULL old thread state"); @@ -361,7 +398,7 @@ Py_FatalError("PyEval_ReleaseThread: NULL thread state"); if (PyThreadState_Swap(NULL) != tstate) Py_FatalError("PyEval_ReleaseThread: wrong thread state"); - drop_gil(tstate); + dropgil_func(tstate); } /* This function is called from PyOS_AfterFork to ensure that newly @@ -379,7 +416,7 @@ return; recreate_gil(); pending_lock = PyThread_allocate_lock(); - take_gil(tstate); + takegil_func(tstate); main_thread = PyThread_get_thread_ident(); /* Update the threading module with the new state. @@ -402,7 +439,7 @@ #else static _Py_atomic_int eval_breaker = {0}; -static int pending_async_exc = 0; +static int pending_async_exc_or_bytecodebarrier = 0; #endif /* WITH_THREAD */ /* This function is used to signal that async exceptions are waiting to be @@ -426,7 +463,7 @@ Py_FatalError("PyEval_SaveThread: NULL tstate"); #ifdef WITH_THREAD if (gil_created()) - drop_gil(tstate); + dropgil_func(tstate); #endif return tstate; } @@ -439,10 +476,10 @@ #ifdef WITH_THREAD if (gil_created()) { int err = errno; - take_gil(tstate); + takegil_func(tstate); /* _Py_Finalizing is protected by the GIL */ if (_Py_Finalizing && tstate != _Py_Finalizing) { - drop_gil(tstate); + dropgil_func(tstate); PyThread_exit_thread(); assert(0); /* unreachable */ } @@ -1258,14 +1295,16 @@ /* Give another thread a chance */ if (PyThreadState_Swap(NULL) != tstate) Py_FatalError("ceval: tstate mix-up"); - drop_gil(tstate); + dropgil_func(tstate); /* Other threads may run now */ - take_gil(tstate); + takegil_func(tstate); if (PyThreadState_Swap(tstate) != NULL) Py_FatalError("ceval: orphan tstate"); } + if (bytecodebarrier_func != NULL) + bytecodebarrier_func(tstate); #endif /* Check for asynchronous exceptions. */ if (tstate->async_exc != NULL) {