diff -r f402197954fc Include/ceval.h --- a/Include/ceval.h Mon Feb 22 16:59:01 2010 +0100 +++ b/Include/ceval.h Mon Feb 22 22:39:48 2010 +0100 @@ -159,6 +159,7 @@ PyAPI_FUNC(PyThreadState *) PyEval_SaveThread(void); PyAPI_FUNC(void) PyEval_RestoreThread(PyThreadState *); +PyAPI_FUNC(void) PyEval_RestoreThreadPrio(PyThreadState *, int prio); #ifdef WITH_THREAD @@ -180,6 +181,8 @@ #define Py_UNBLOCK_THREADS _save = PyEval_SaveThread(); #define Py_END_ALLOW_THREADS PyEval_RestoreThread(_save); \ } +#define Py_END_ALLOW_THREADS_PRIO(x) PyEval_RestoreThreadPrio(_save, (x)); \ + } #else /* !WITH_THREAD */ @@ -187,6 +190,7 @@ #define Py_BLOCK_THREADS #define Py_UNBLOCK_THREADS #define Py_END_ALLOW_THREADS } +#define Py_END_ALLOW_THREADS_PRIO(x) } #endif /* !WITH_THREAD */ diff -r f402197954fc Modules/socketmodule.c --- a/Modules/socketmodule.c Mon Feb 22 16:59:01 2010 +0100 +++ b/Modules/socketmodule.c Mon Feb 22 22:39:48 2010 +0100 @@ -2159,7 +2159,7 @@ timeout = internal_select(s, 0); if (!timeout) outlen = recv(s->sock_fd, cbuf, len, flags); - Py_END_ALLOW_THREADS + Py_END_ALLOW_THREADS_PRIO(outlen >= 0) if (timeout == 1) { PyErr_SetString(socket_timeout, "timed out"); @@ -2374,7 +2374,7 @@ SAS2SA(&addrbuf), &addrlen); #endif } - Py_END_ALLOW_THREADS + Py_END_ALLOW_THREADS_PRIO(n >= 0) if (timeout == 1) { PyErr_SetString(socket_timeout, "timed out"); @@ -2525,7 +2525,7 @@ #else n = send(s->sock_fd, buf, len, flags); #endif - Py_END_ALLOW_THREADS + Py_END_ALLOW_THREADS_PRIO(n >= 0) PyBuffer_Release(&pbuf); @@ -2594,7 +2594,7 @@ buf += n; len -= n; } while (len > 0); - Py_END_ALLOW_THREADS + Py_END_ALLOW_THREADS_PRIO(n >= 0) PyBuffer_Release(&pbuf); if (timeout == 1) { @@ -2653,7 +2653,7 @@ timeout = internal_select(s, 1); if (!timeout) n = sendto(s->sock_fd, buf, len, flags, SAS2SA(&addrbuf), addrlen); - Py_END_ALLOW_THREADS + Py_END_ALLOW_THREADS_PRIO(n >= 0) PyBuffer_Release(&pbuf); if (timeout == 1) { diff -r f402197954fc Python/ceval.c --- a/Python/ceval.c Mon Feb 22 16:59:01 2010 +0100 +++ b/Python/ceval.c Mon Feb 22 22:39:48 2010 +0100 @@ -392,20 +392,29 @@ } void -PyEval_RestoreThread(PyThreadState *tstate) +PyEval_RestoreThreadPrio(PyThreadState *tstate, int prio) { if (tstate == NULL) Py_FatalError("PyEval_RestoreThread: NULL tstate"); #ifdef WITH_THREAD if (gil_created()) { int err = errno; - take_gil(tstate); + if (prio) + take_gil_prio(tstate); + else + take_gil(tstate); errno = err; } #endif PyThreadState_Swap(tstate); } +void +PyEval_RestoreThread(PyThreadState *tstate) +{ + PyEval_RestoreThreadPrio(tstate, 0); +} + /* Mechanism whereby asynchronously executing callbacks (e.g. UNIX signal handlers or Mac I/O completion routines) can schedule calls diff -r f402197954fc Python/ceval_gil.h --- a/Python/ceval_gil.h Mon Feb 22 16:59:01 2010 +0100 +++ b/Python/ceval_gil.h Mon Feb 22 22:39:48 2010 +0100 @@ -17,6 +17,9 @@ #undef FORCE_SWITCHING #define FORCE_SWITCHING +#undef TRACE_PRIO +/* #define TRACE_PRIO */ + /* Notes about the implementation: @@ -222,6 +225,13 @@ static COND_T gil_cond; static MUTEX_T gil_mutex; +/* This mutex is taken when a priority request is made, and released when + it is finally honoured. + Other threads can sleep by trying to lock the mutex. */ +static MUTEX_T prio_mutex; +/* The thread making the prio request, or NULL. */ +static volatile PyThreadState *prio_request = NULL; + #ifdef FORCE_SWITCHING /* This condition variable helps the GIL-releasing thread wait for a GIL-awaiting thread to be scheduled and take the GIL. */ @@ -229,6 +239,14 @@ static MUTEX_T switch_mutex; #endif +#define YIELD_IF_PRIO_REQUEST() \ +do { \ + if (prio_request) { \ + MUTEX_LOCK(prio_mutex); \ + MUTEX_UNLOCK(prio_mutex); \ + } \ +} while (0) + static int gil_created(void) { @@ -241,12 +259,14 @@ #ifdef FORCE_SWITCHING MUTEX_INIT(switch_mutex); #endif + MUTEX_INIT(prio_mutex); COND_INIT(gil_cond); #ifdef FORCE_SWITCHING COND_INIT(switch_cond); #endif gil_locked = 0; gil_last_holder = NULL; + prio_request = NULL; } static void recreate_gil(void) @@ -285,7 +305,7 @@ #endif } -static void take_gil(PyThreadState *tstate) +static void _take_gil(PyThreadState *tstate, int prio) { int err; if (tstate == NULL) @@ -297,11 +317,39 @@ if (!gil_locked) goto _ready; + if (prio) { +#ifdef TRACE_PRIO + struct timeval tv; + GETTIMEOFDAY(&tv); + printf("trying to take gil with prio: %.3f <--\n", + tv.tv_sec + tv.tv_usec / 1000000.0); +#endif + if (!prio_request) { + MUTEX_LOCK(prio_mutex); + prio_request = tstate; + } + else + prio = 0; + } + COND_RESET(gil_cond); - while (gil_locked) { + while (gil_locked || (prio_request && !prio)) { int timed_out = 0; unsigned long saved_switchnum; + if (prio_request) { + /* Tell the eval loop the GIL must be dropped as soon as possible */ + SET_GIL_DROP_REQUEST(); + if (!prio) { + /* If another thread is making the prio_request, give it a + chance to run and take the mutex. */ + COND_SIGNAL(gil_cond); + MUTEX_UNLOCK(gil_mutex); + YIELD_IF_PRIO_REQUEST(); + MUTEX_LOCK(gil_mutex); + } + } + saved_switchnum = gil_switch_number; COND_TIMED_WAIT(gil_cond, gil_mutex, INTERVAL, timed_out); /* If we timed out and no switch occurred in the meantime, it is time @@ -321,12 +369,26 @@ if (tstate != gil_last_holder) { gil_last_holder = tstate; ++gil_switch_number; +#ifdef TRACE_PRIO + if (prio) { + struct timeval tv; + GETTIMEOFDAY(&tv); + printf("gil taken with prio: %.3f\n", + tv.tv_sec + tv.tv_usec / 1000000.0); + } +#endif } #ifdef FORCE_SWITCHING COND_SIGNAL(switch_cond); MUTEX_UNLOCK(switch_mutex); #endif - if (gil_drop_request) { + if (prio) { + /* The prio request was granted. */ + prio_request = NULL; + MUTEX_UNLOCK(prio_mutex); + } + if (gil_drop_request && !prio_request) { + /* No prio_request pending. */ RESET_GIL_DROP_REQUEST(); } if (tstate->async_exc != NULL) { @@ -337,6 +399,16 @@ errno = err; } +static void take_gil(PyThreadState *tstate) +{ + _take_gil(tstate, 0); +} + +static void take_gil_prio(PyThreadState *tstate) +{ + _take_gil(tstate, 1); +} + void _PyEval_SetSwitchInterval(unsigned long microseconds) { gil_interval = microseconds;