Home | History | Annotate | Download | only in Python
      1 /*
      2  * Implementation of the Global Interpreter Lock (GIL).
      3  */
      4 
      5 #include <stdlib.h>
      6 #include <errno.h>
      7 
      8 
      9 /* First some general settings */
     10 
     11 /* microseconds (the Python API uses seconds, though) */
     12 #define DEFAULT_INTERVAL 5000
     13 static unsigned long gil_interval = DEFAULT_INTERVAL;
     14 #define INTERVAL (gil_interval >= 1 ? gil_interval : 1)
     15 
     16 /* Enable if you want to force the switching of threads at least every `gil_interval` */
     17 #undef FORCE_SWITCHING
     18 #define FORCE_SWITCHING
     19 
     20 
     21 /*
     22    Notes about the implementation:
     23 
     24    - The GIL is just a boolean variable (gil_locked) whose access is protected
     25      by a mutex (gil_mutex), and whose changes are signalled by a condition
     26      variable (gil_cond). gil_mutex is taken for short periods of time,
     27      and therefore mostly uncontended.
     28 
     29    - In the GIL-holding thread, the main loop (PyEval_EvalFrameEx) must be
     30      able to release the GIL on demand by another thread. A volatile boolean
     31      variable (gil_drop_request) is used for that purpose, which is checked
     32      at every turn of the eval loop. That variable is set after a wait of
     33      `interval` microseconds on `gil_cond` has timed out.
     34 
     35       [Actually, another volatile boolean variable (eval_breaker) is used
     36        which ORs several conditions into one. Volatile booleans are
     37        sufficient as inter-thread signalling means since Python is run
     38        on cache-coherent architectures only.]
     39 
     40    - A thread wanting to take the GIL will first let pass a given amount of
     41      time (`interval` microseconds) before setting gil_drop_request. This
     42      encourages a defined switching period, but doesn't enforce it since
     43      opcodes can take an arbitrary time to execute.
     44 
     45      The `interval` value is available for the user to read and modify
     46      using the Python API `sys.{get,set}switchinterval()`.
     47 
     48    - When a thread releases the GIL and gil_drop_request is set, that thread
     49      ensures that another GIL-awaiting thread gets scheduled.
     50      It does so by waiting on a condition variable (switch_cond) until
     51      the value of gil_last_holder is changed to something else than its
     52      own thread state pointer, indicating that another thread was able to
     53      take the GIL.
     54 
     55      This is meant to prohibit the latency-adverse behaviour on multi-core
     56      machines where one thread would speculatively release the GIL, but still
     57      run and end up being the first to re-acquire it, making the "timeslices"
     58      much longer than expected.
     59      (Note: this mechanism is enabled with FORCE_SWITCHING above)
     60 */
     61 
     62 #include "condvar.h"
     63 #ifndef Py_HAVE_CONDVAR
     64 #error You need either a POSIX-compatible or a Windows system!
     65 #endif
     66 
     67 #define MUTEX_T PyMUTEX_T
     68 #define MUTEX_INIT(mut) \
     69     if (PyMUTEX_INIT(&(mut))) { \
     70         Py_FatalError("PyMUTEX_INIT(" #mut ") failed"); };
     71 #define MUTEX_FINI(mut) \
     72     if (PyMUTEX_FINI(&(mut))) { \
     73         Py_FatalError("PyMUTEX_FINI(" #mut ") failed"); };
     74 #define MUTEX_LOCK(mut) \
     75     if (PyMUTEX_LOCK(&(mut))) { \
     76         Py_FatalError("PyMUTEX_LOCK(" #mut ") failed"); };
     77 #define MUTEX_UNLOCK(mut) \
     78     if (PyMUTEX_UNLOCK(&(mut))) { \
     79         Py_FatalError("PyMUTEX_UNLOCK(" #mut ") failed"); };
     80 
     81 #define COND_T PyCOND_T
     82 #define COND_INIT(cond) \
     83     if (PyCOND_INIT(&(cond))) { \
     84         Py_FatalError("PyCOND_INIT(" #cond ") failed"); };
     85 #define COND_FINI(cond) \
     86     if (PyCOND_FINI(&(cond))) { \
     87         Py_FatalError("PyCOND_FINI(" #cond ") failed"); };
     88 #define COND_SIGNAL(cond) \
     89     if (PyCOND_SIGNAL(&(cond))) { \
     90         Py_FatalError("PyCOND_SIGNAL(" #cond ") failed"); };
     91 #define COND_WAIT(cond, mut) \
     92     if (PyCOND_WAIT(&(cond), &(mut))) { \
     93         Py_FatalError("PyCOND_WAIT(" #cond ") failed"); };
     94 #define COND_TIMED_WAIT(cond, mut, microseconds, timeout_result) \
     95     { \
     96         int r = PyCOND_TIMEDWAIT(&(cond), &(mut), (microseconds)); \
     97         if (r < 0) \
     98             Py_FatalError("PyCOND_WAIT(" #cond ") failed"); \
     99         if (r) /* 1 == timeout, 2 == impl. can't say, so assume timeout */ \
    100             timeout_result = 1; \
    101         else \
    102             timeout_result = 0; \
    103     } \
    104 
    105 
    106 
    107 /* Whether the GIL is already taken (-1 if uninitialized). This is atomic
    108    because it can be read without any lock taken in ceval.c. */
    109 static _Py_atomic_int gil_locked = {-1};
    110 /* Number of GIL switches since the beginning. */
    111 static unsigned long gil_switch_number = 0;
    112 /* Last PyThreadState holding / having held the GIL. This helps us know
    113    whether anyone else was scheduled after we dropped the GIL. */
    114 static _Py_atomic_address gil_last_holder = {0};
    115 
    116 /* This condition variable allows one or several threads to wait until
    117    the GIL is released. In addition, the mutex also protects the above
    118    variables. */
    119 static COND_T gil_cond;
    120 static MUTEX_T gil_mutex;
    121 
    122 #ifdef FORCE_SWITCHING
    123 /* This condition variable helps the GIL-releasing thread wait for
    124    a GIL-awaiting thread to be scheduled and take the GIL. */
    125 static COND_T switch_cond;
    126 static MUTEX_T switch_mutex;
    127 #endif
    128 
    129 
    130 static int gil_created(void)
    131 {
    132     return _Py_atomic_load_explicit(&gil_locked, _Py_memory_order_acquire) >= 0;
    133 }
    134 
    135 static void create_gil(void)
    136 {
    137     MUTEX_INIT(gil_mutex);
    138 #ifdef FORCE_SWITCHING
    139     MUTEX_INIT(switch_mutex);
    140 #endif
    141     COND_INIT(gil_cond);
    142 #ifdef FORCE_SWITCHING
    143     COND_INIT(switch_cond);
    144 #endif
    145     _Py_atomic_store_relaxed(&gil_last_holder, 0);
    146     _Py_ANNOTATE_RWLOCK_CREATE(&gil_locked);
    147     _Py_atomic_store_explicit(&gil_locked, 0, _Py_memory_order_release);
    148 }
    149 
    150 static void destroy_gil(void)
    151 {
    152     /* some pthread-like implementations tie the mutex to the cond
    153      * and must have the cond destroyed first.
    154      */
    155     COND_FINI(gil_cond);
    156     MUTEX_FINI(gil_mutex);
    157 #ifdef FORCE_SWITCHING
    158     COND_FINI(switch_cond);
    159     MUTEX_FINI(switch_mutex);
    160 #endif
    161     _Py_atomic_store_explicit(&gil_locked, -1, _Py_memory_order_release);
    162     _Py_ANNOTATE_RWLOCK_DESTROY(&gil_locked);
    163 }
    164 
    165 static void recreate_gil(void)
    166 {
    167     _Py_ANNOTATE_RWLOCK_DESTROY(&gil_locked);
    168     /* XXX should we destroy the old OS resources here? */
    169     create_gil();
    170 }
    171 
    172 static void drop_gil(PyThreadState *tstate)
    173 {
    174     if (!_Py_atomic_load_relaxed(&gil_locked))
    175         Py_FatalError("drop_gil: GIL is not locked");
    176     /* tstate is allowed to be NULL (early interpreter init) */
    177     if (tstate != NULL) {
    178         /* Sub-interpreter support: threads might have been switched
    179            under our feet using PyThreadState_Swap(). Fix the GIL last
    180            holder variable so that our heuristics work. */
    181         _Py_atomic_store_relaxed(&gil_last_holder, (uintptr_t)tstate);
    182     }
    183 
    184     MUTEX_LOCK(gil_mutex);
    185     _Py_ANNOTATE_RWLOCK_RELEASED(&gil_locked, /*is_write=*/1);
    186     _Py_atomic_store_relaxed(&gil_locked, 0);
    187     COND_SIGNAL(gil_cond);
    188     MUTEX_UNLOCK(gil_mutex);
    189 
    190 #ifdef FORCE_SWITCHING
    191     if (_Py_atomic_load_relaxed(&gil_drop_request) && tstate != NULL) {
    192         MUTEX_LOCK(switch_mutex);
    193         /* Not switched yet => wait */
    194         if ((PyThreadState*)_Py_atomic_load_relaxed(&gil_last_holder) == tstate) {
    195         RESET_GIL_DROP_REQUEST();
    196             /* NOTE: if COND_WAIT does not atomically start waiting when
    197                releasing the mutex, another thread can run through, take
    198                the GIL and drop it again, and reset the condition
    199                before we even had a chance to wait for it. */
    200             COND_WAIT(switch_cond, switch_mutex);
    201     }
    202         MUTEX_UNLOCK(switch_mutex);
    203     }
    204 #endif
    205 }
    206 
    207 static void take_gil(PyThreadState *tstate)
    208 {
    209     int err;
    210     if (tstate == NULL)
    211         Py_FatalError("take_gil: NULL tstate");
    212 
    213     err = errno;
    214     MUTEX_LOCK(gil_mutex);
    215 
    216     if (!_Py_atomic_load_relaxed(&gil_locked))
    217         goto _ready;
    218 
    219     while (_Py_atomic_load_relaxed(&gil_locked)) {
    220         int timed_out = 0;
    221         unsigned long saved_switchnum;
    222 
    223         saved_switchnum = gil_switch_number;
    224         COND_TIMED_WAIT(gil_cond, gil_mutex, INTERVAL, timed_out);
    225         /* If we timed out and no switch occurred in the meantime, it is time
    226            to ask the GIL-holding thread to drop it. */
    227         if (timed_out &&
    228             _Py_atomic_load_relaxed(&gil_locked) &&
    229             gil_switch_number == saved_switchnum) {
    230             SET_GIL_DROP_REQUEST();
    231         }
    232     }
    233 _ready:
    234 #ifdef FORCE_SWITCHING
    235     /* This mutex must be taken before modifying gil_last_holder (see drop_gil()). */
    236     MUTEX_LOCK(switch_mutex);
    237 #endif
    238     /* We now hold the GIL */
    239     _Py_atomic_store_relaxed(&gil_locked, 1);
    240     _Py_ANNOTATE_RWLOCK_ACQUIRED(&gil_locked, /*is_write=*/1);
    241 
    242     if (tstate != (PyThreadState*)_Py_atomic_load_relaxed(&gil_last_holder)) {
    243         _Py_atomic_store_relaxed(&gil_last_holder, (uintptr_t)tstate);
    244         ++gil_switch_number;
    245     }
    246 
    247 #ifdef FORCE_SWITCHING
    248     COND_SIGNAL(switch_cond);
    249     MUTEX_UNLOCK(switch_mutex);
    250 #endif
    251     if (_Py_atomic_load_relaxed(&gil_drop_request)) {
    252         RESET_GIL_DROP_REQUEST();
    253     }
    254     if (tstate->async_exc != NULL) {
    255         _PyEval_SignalAsyncExc();
    256     }
    257 
    258     MUTEX_UNLOCK(gil_mutex);
    259     errno = err;
    260 }
    261 
    262 void _PyEval_SetSwitchInterval(unsigned long microseconds)
    263 {
    264     gil_interval = microseconds;
    265 }
    266 
    267 unsigned long _PyEval_GetSwitchInterval()
    268 {
    269     return gil_interval;
    270 }
    271