Index: Python/ceval.c =================================================================== --- Python/ceval.c (revision 67120) +++ Python/ceval.c (working copy) @@ -213,6 +213,7 @@ #include "pythread.h" static PyThread_type_lock interpreter_lock = 0; /* This is the GIL */ +static PyThread_type_lock pendinglock = 0; /* for pending calls */ static long main_thread = 0; int @@ -227,6 +228,7 @@ if (interpreter_lock) return; interpreter_lock = PyThread_allocate_lock(); + pendinglock = PyThread_allocate_lock(); PyThread_acquire_lock(interpreter_lock, 1); main_thread = PyThread_get_thread_ident(); } @@ -357,7 +359,101 @@ Any thread can schedule pending calls, but only the main thread will execute them. #endif +*/ +#ifdef WITH_THREAD + +/* The WITH_THREAD implementation is thread-safe. It allows + scheduling to be made from any thread, and even from an executing + callback. + */ + +#define NPENDINGCALLS 32 +static struct { + int (*func)(void *); + void *arg; +} pendingcalls[NPENDINGCALLS]; +static int pendingfirst = 0; +static int pendinglast = 0; +static int things_to_do = 0; + +int +Py_AddPendingCall(int (*func)(void *), void *arg) +{ + int i, j, result=0; + + if (main_thread && PyThread_get_thread_ident() == main_thread) { + /* try a few times for the lock. Since this mechanism is used + * for signal handling (on the main thread), there is a (slim) + * change chat a signal is delivered on the same thread while we + * hold the lock during the Py_MakePendingCalls() function. + * This avoids a deadlock in that case. + */ + for (i = 0; i<100; i++) { + if (PyThread_acquire_lock(pendinglock, NOWAIT_LOCK)) + break; + } + if (i == 100) + return -1; + } else { + /* signals are only handled by the main thread, so re-acquiring + * the lock is not a danger on any other thread. + */ + PyThread_acquire_lock(pendinglock, WAIT_LOCK); + } + + i = pendinglast; + j = (i + 1) % NPENDINGCALLS; + if (j == pendingfirst) { + result = -1; /* Queue full */ + } else { + pendingcalls[i].func = func; + pendingcalls[i].arg = arg; + pendinglast = j; + } + /* signal main loop */ + _Py_Ticker = 0; + things_to_do = 1; + PyThread_release_lock(pendinglock); + return result; +} + +int +Py_MakePendingCalls(void) +{ + int i; + if (main_thread && PyThread_get_thread_ident() != main_thread) + return 0; + /* perform a bounded number of calls, in case of recursion */ + for (i=0; i