diff -r e1fe09bc54c9 Include/pystate.h --- a/Include/pystate.h Sat Mar 27 13:34:21 2010 +0100 +++ b/Include/pystate.h Sat Mar 27 14:25:17 2010 +0100 @@ -103,6 +103,7 @@ typedef struct _ts { PyObject *async_exc; /* Asynchronous exception to raise */ long thread_id; /* Thread id where this tstate was created */ + int interactiveness; /* How much interactive the thread is */ /* XXX signal handlers should also be here */ } PyThreadState; diff -r e1fe09bc54c9 Python/ceval_gil.h --- a/Python/ceval_gil.h Sat Mar 27 13:34:21 2010 +0100 +++ b/Python/ceval_gil.h Sat Mar 27 14:25:17 2010 +0100 @@ -17,6 +17,9 @@ static unsigned long gil_interval = DEFA #undef FORCE_SWITCHING #define FORCE_SWITCHING +#undef TRACE_PRIO +/* #define TRACE_PRIO */ + /* Notes about the implementation: @@ -222,6 +225,13 @@ static PyThreadState *gil_last_holder = 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,30 @@ static COND_T switch_cond; 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) + +/* PyThreadState->interactiveness is implemented as a saturating counter */ +#define MAX_INTERACTIVENESS 2 +#define MIN_INTERACTIVENESS -2 + +#define UP_INTERACTIVENESS(tstate) \ +do { \ + if (tstate->interactiveness < MAX_INTERACTIVENESS) \ + ++tstate->interactiveness; \ +} while (0) + +#define DOWN_INTERACTIVENESS(tstate) \ +do { \ + if (tstate->interactiveness > MIN_INTERACTIVENESS) \ + --tstate->interactiveness; \ +} while (0) + static int gil_created(void) { @@ -241,12 +275,14 @@ static void create_gil(void) #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) @@ -266,7 +302,15 @@ static void drop_gil(PyThreadState *tsta gil_locked = 0; COND_SIGNAL(gil_cond); MUTEX_UNLOCK(gil_mutex); - + + if (tstate != NULL) { + if (gil_drop_request) { + DOWN_INTERACTIVENESS(tstate); + } + else + UP_INTERACTIVENESS(tstate); + } + #ifdef FORCE_SWITCHING if (gil_drop_request && tstate != NULL) { MUTEX_LOCK(switch_mutex); @@ -287,7 +331,7 @@ static void drop_gil(PyThreadState *tsta static void take_gil(PyThreadState *tstate) { - int err; + int err, prio = 0; if (tstate == NULL) Py_FatalError("take_gil: NULL tstate"); @@ -297,11 +341,45 @@ static void take_gil(PyThreadState *tsta if (!gil_locked) goto _ready; + /* Prioritize interactive tasks vs. non-interactive ones */ + if (!prio_request + && (tstate->interactiveness == MAX_INTERACTIVENESS) + && (!gil_last_holder || gil_last_holder->interactiveness < MAX_INTERACTIVENESS)) + prio = 1; + + 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 +399,26 @@ _ready: 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) { diff -r e1fe09bc54c9 Python/pystate.c --- a/Python/pystate.c Sat Mar 27 13:34:21 2010 +0100 +++ b/Python/pystate.c Sat Mar 27 14:25:17 2010 +0100 @@ -198,6 +198,8 @@ new_threadstate(PyInterpreterState *inte tstate->c_profileobj = NULL; tstate->c_traceobj = NULL; + tstate->interactiveness = 0; + if (init) _PyThreadState_Init(tstate);