• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Implementation of the Global Interpreter Lock (GIL).
3  */
4 
5 #include <stdlib.h>
6 #include <errno.h>
7 
8 #include "pycore_atomic.h"
9 
10 
11 /*
12    Notes about the implementation:
13 
14    - The GIL is just a boolean variable (locked) whose access is protected
15      by a mutex (gil_mutex), and whose changes are signalled by a condition
16      variable (gil_cond). gil_mutex is taken for short periods of time,
17      and therefore mostly uncontended.
18 
19    - In the GIL-holding thread, the main loop (PyEval_EvalFrameEx) must be
20      able to release the GIL on demand by another thread. A volatile boolean
21      variable (gil_drop_request) is used for that purpose, which is checked
22      at every turn of the eval loop. That variable is set after a wait of
23      `interval` microseconds on `gil_cond` has timed out.
24 
25       [Actually, another volatile boolean variable (eval_breaker) is used
26        which ORs several conditions into one. Volatile booleans are
27        sufficient as inter-thread signalling means since Python is run
28        on cache-coherent architectures only.]
29 
30    - A thread wanting to take the GIL will first let pass a given amount of
31      time (`interval` microseconds) before setting gil_drop_request. This
32      encourages a defined switching period, but doesn't enforce it since
33      opcodes can take an arbitrary time to execute.
34 
35      The `interval` value is available for the user to read and modify
36      using the Python API `sys.{get,set}switchinterval()`.
37 
38    - When a thread releases the GIL and gil_drop_request is set, that thread
39      ensures that another GIL-awaiting thread gets scheduled.
40      It does so by waiting on a condition variable (switch_cond) until
41      the value of last_holder is changed to something else than its
42      own thread state pointer, indicating that another thread was able to
43      take the GIL.
44 
45      This is meant to prohibit the latency-adverse behaviour on multi-core
46      machines where one thread would speculatively release the GIL, but still
47      run and end up being the first to re-acquire it, making the "timeslices"
48      much longer than expected.
49      (Note: this mechanism is enabled with FORCE_SWITCHING above)
50 */
51 
52 #include "condvar.h"
53 
54 #define MUTEX_INIT(mut) \
55     if (PyMUTEX_INIT(&(mut))) { \
56         Py_FatalError("PyMUTEX_INIT(" #mut ") failed"); };
57 #define MUTEX_FINI(mut) \
58     if (PyMUTEX_FINI(&(mut))) { \
59         Py_FatalError("PyMUTEX_FINI(" #mut ") failed"); };
60 #define MUTEX_LOCK(mut) \
61     if (PyMUTEX_LOCK(&(mut))) { \
62         Py_FatalError("PyMUTEX_LOCK(" #mut ") failed"); };
63 #define MUTEX_UNLOCK(mut) \
64     if (PyMUTEX_UNLOCK(&(mut))) { \
65         Py_FatalError("PyMUTEX_UNLOCK(" #mut ") failed"); };
66 
67 #define COND_INIT(cond) \
68     if (PyCOND_INIT(&(cond))) { \
69         Py_FatalError("PyCOND_INIT(" #cond ") failed"); };
70 #define COND_FINI(cond) \
71     if (PyCOND_FINI(&(cond))) { \
72         Py_FatalError("PyCOND_FINI(" #cond ") failed"); };
73 #define COND_SIGNAL(cond) \
74     if (PyCOND_SIGNAL(&(cond))) { \
75         Py_FatalError("PyCOND_SIGNAL(" #cond ") failed"); };
76 #define COND_WAIT(cond, mut) \
77     if (PyCOND_WAIT(&(cond), &(mut))) { \
78         Py_FatalError("PyCOND_WAIT(" #cond ") failed"); };
79 #define COND_TIMED_WAIT(cond, mut, microseconds, timeout_result) \
80     { \
81         int r = PyCOND_TIMEDWAIT(&(cond), &(mut), (microseconds)); \
82         if (r < 0) \
83             Py_FatalError("PyCOND_WAIT(" #cond ") failed"); \
84         if (r) /* 1 == timeout, 2 == impl. can't say, so assume timeout */ \
85             timeout_result = 1; \
86         else \
87             timeout_result = 0; \
88     } \
89 
90 
91 #define DEFAULT_INTERVAL 5000
92 
_gil_initialize(struct _gil_runtime_state * gil)93 static void _gil_initialize(struct _gil_runtime_state *gil)
94 {
95     _Py_atomic_int uninitialized = {-1};
96     gil->locked = uninitialized;
97     gil->interval = DEFAULT_INTERVAL;
98 }
99 
gil_created(struct _gil_runtime_state * gil)100 static int gil_created(struct _gil_runtime_state *gil)
101 {
102     return (_Py_atomic_load_explicit(&gil->locked, _Py_memory_order_acquire) >= 0);
103 }
104 
create_gil(struct _gil_runtime_state * gil)105 static void create_gil(struct _gil_runtime_state *gil)
106 {
107     MUTEX_INIT(gil->mutex);
108 #ifdef FORCE_SWITCHING
109     MUTEX_INIT(gil->switch_mutex);
110 #endif
111     COND_INIT(gil->cond);
112 #ifdef FORCE_SWITCHING
113     COND_INIT(gil->switch_cond);
114 #endif
115     _Py_atomic_store_relaxed(&gil->last_holder, 0);
116     _Py_ANNOTATE_RWLOCK_CREATE(&gil->locked);
117     _Py_atomic_store_explicit(&gil->locked, 0, _Py_memory_order_release);
118 }
119 
destroy_gil(struct _gil_runtime_state * gil)120 static void destroy_gil(struct _gil_runtime_state *gil)
121 {
122     /* some pthread-like implementations tie the mutex to the cond
123      * and must have the cond destroyed first.
124      */
125     COND_FINI(gil->cond);
126     MUTEX_FINI(gil->mutex);
127 #ifdef FORCE_SWITCHING
128     COND_FINI(gil->switch_cond);
129     MUTEX_FINI(gil->switch_mutex);
130 #endif
131     _Py_atomic_store_explicit(&gil->locked, -1,
132                               _Py_memory_order_release);
133     _Py_ANNOTATE_RWLOCK_DESTROY(&gil->locked);
134 }
135 
recreate_gil(struct _gil_runtime_state * gil)136 static void recreate_gil(struct _gil_runtime_state *gil)
137 {
138     _Py_ANNOTATE_RWLOCK_DESTROY(&gil->locked);
139     /* XXX should we destroy the old OS resources here? */
140     create_gil(gil);
141 }
142 
143 static void
drop_gil(struct _ceval_runtime_state * ceval,struct _ceval_state * ceval2,PyThreadState * tstate)144 drop_gil(struct _ceval_runtime_state *ceval, struct _ceval_state *ceval2,
145          PyThreadState *tstate)
146 {
147     struct _gil_runtime_state *gil = &ceval->gil;
148     if (!_Py_atomic_load_relaxed(&gil->locked)) {
149         Py_FatalError("drop_gil: GIL is not locked");
150     }
151 
152     /* tstate is allowed to be NULL (early interpreter init) */
153     if (tstate != NULL) {
154         /* Sub-interpreter support: threads might have been switched
155            under our feet using PyThreadState_Swap(). Fix the GIL last
156            holder variable so that our heuristics work. */
157         _Py_atomic_store_relaxed(&gil->last_holder, (uintptr_t)tstate);
158     }
159 
160     MUTEX_LOCK(gil->mutex);
161     _Py_ANNOTATE_RWLOCK_RELEASED(&gil->locked, /*is_write=*/1);
162     _Py_atomic_store_relaxed(&gil->locked, 0);
163     COND_SIGNAL(gil->cond);
164     MUTEX_UNLOCK(gil->mutex);
165 
166 #ifdef FORCE_SWITCHING
167     if (_Py_atomic_load_relaxed(&ceval2->gil_drop_request) && tstate != NULL) {
168         MUTEX_LOCK(gil->switch_mutex);
169         /* Not switched yet => wait */
170         if (((PyThreadState*)_Py_atomic_load_relaxed(&gil->last_holder)) == tstate)
171         {
172             assert(is_tstate_valid(tstate));
173             RESET_GIL_DROP_REQUEST(tstate->interp);
174             /* NOTE: if COND_WAIT does not atomically start waiting when
175                releasing the mutex, another thread can run through, take
176                the GIL and drop it again, and reset the condition
177                before we even had a chance to wait for it. */
178             COND_WAIT(gil->switch_cond, gil->switch_mutex);
179         }
180         MUTEX_UNLOCK(gil->switch_mutex);
181     }
182 #endif
183 }
184 
185 
186 /* Check if a Python thread must exit immediately, rather than taking the GIL
187    if Py_Finalize() has been called.
188 
189    When this function is called by a daemon thread after Py_Finalize() has been
190    called, the GIL does no longer exist.
191 
192    tstate must be non-NULL. */
193 static inline int
tstate_must_exit(PyThreadState * tstate)194 tstate_must_exit(PyThreadState *tstate)
195 {
196     /* bpo-39877: Access _PyRuntime directly rather than using
197        tstate->interp->runtime to support calls from Python daemon threads.
198        After Py_Finalize() has been called, tstate can be a dangling pointer:
199        point to PyThreadState freed memory. */
200     PyThreadState *finalizing = _PyRuntimeState_GetFinalizing(&_PyRuntime);
201     return (finalizing != NULL && finalizing != tstate);
202 }
203 
204 
205 /* Take the GIL.
206 
207    The function saves errno at entry and restores its value at exit.
208 
209    tstate must be non-NULL. */
210 static void
take_gil(PyThreadState * tstate)211 take_gil(PyThreadState *tstate)
212 {
213     int err = errno;
214 
215     assert(tstate != NULL);
216 
217     if (tstate_must_exit(tstate)) {
218         /* bpo-39877: If Py_Finalize() has been called and tstate is not the
219            thread which called Py_Finalize(), exit immediately the thread.
220 
221            This code path can be reached by a daemon thread after Py_Finalize()
222            completes. In this case, tstate is a dangling pointer: points to
223            PyThreadState freed memory. */
224         PyThread_exit_thread();
225     }
226 
227     assert(is_tstate_valid(tstate));
228     PyInterpreterState *interp = tstate->interp;
229     struct _ceval_runtime_state *ceval = &interp->runtime->ceval;
230     struct _ceval_state *ceval2 = &interp->ceval;
231     struct _gil_runtime_state *gil = &ceval->gil;
232 
233     /* Check that _PyEval_InitThreads() was called to create the lock */
234     assert(gil_created(gil));
235 
236     MUTEX_LOCK(gil->mutex);
237 
238     if (!_Py_atomic_load_relaxed(&gil->locked)) {
239         goto _ready;
240     }
241 
242     while (_Py_atomic_load_relaxed(&gil->locked)) {
243         unsigned long saved_switchnum = gil->switch_number;
244 
245         unsigned long interval = (gil->interval >= 1 ? gil->interval : 1);
246         int timed_out = 0;
247         COND_TIMED_WAIT(gil->cond, gil->mutex, interval, timed_out);
248 
249         /* If we timed out and no switch occurred in the meantime, it is time
250            to ask the GIL-holding thread to drop it. */
251         if (timed_out &&
252             _Py_atomic_load_relaxed(&gil->locked) &&
253             gil->switch_number == saved_switchnum)
254         {
255             if (tstate_must_exit(tstate)) {
256                 MUTEX_UNLOCK(gil->mutex);
257                 PyThread_exit_thread();
258             }
259             assert(is_tstate_valid(tstate));
260 
261             SET_GIL_DROP_REQUEST(interp);
262         }
263     }
264 
265 _ready:
266 #ifdef FORCE_SWITCHING
267     /* This mutex must be taken before modifying gil->last_holder:
268        see drop_gil(). */
269     MUTEX_LOCK(gil->switch_mutex);
270 #endif
271     /* We now hold the GIL */
272     _Py_atomic_store_relaxed(&gil->locked, 1);
273     _Py_ANNOTATE_RWLOCK_ACQUIRED(&gil->locked, /*is_write=*/1);
274 
275     if (tstate != (PyThreadState*)_Py_atomic_load_relaxed(&gil->last_holder)) {
276         _Py_atomic_store_relaxed(&gil->last_holder, (uintptr_t)tstate);
277         ++gil->switch_number;
278     }
279 
280 #ifdef FORCE_SWITCHING
281     COND_SIGNAL(gil->switch_cond);
282     MUTEX_UNLOCK(gil->switch_mutex);
283 #endif
284 
285     if (tstate_must_exit(tstate)) {
286         /* bpo-36475: If Py_Finalize() has been called and tstate is not
287            the thread which called Py_Finalize(), exit immediately the
288            thread.
289 
290            This code path can be reached by a daemon thread which was waiting
291            in take_gil() while the main thread called
292            wait_for_thread_shutdown() from Py_Finalize(). */
293         MUTEX_UNLOCK(gil->mutex);
294         drop_gil(ceval, ceval2, tstate);
295         PyThread_exit_thread();
296     }
297     assert(is_tstate_valid(tstate));
298 
299     if (_Py_atomic_load_relaxed(&ceval2->gil_drop_request)) {
300         RESET_GIL_DROP_REQUEST(interp);
301     }
302     else {
303         /* bpo-40010: eval_breaker should be recomputed to be set to 1 if there
304            is a pending signal: signal received by another thread which cannot
305            handle signals.
306 
307            Note: RESET_GIL_DROP_REQUEST() calls COMPUTE_EVAL_BREAKER(). */
308         COMPUTE_EVAL_BREAKER(interp, ceval, ceval2);
309     }
310 
311     /* Don't access tstate if the thread must exit */
312     if (tstate->async_exc != NULL) {
313         _PyEval_SignalAsyncExc(tstate);
314     }
315 
316     MUTEX_UNLOCK(gil->mutex);
317 
318     errno = err;
319 }
320 
_PyEval_SetSwitchInterval(unsigned long microseconds)321 void _PyEval_SetSwitchInterval(unsigned long microseconds)
322 {
323     struct _gil_runtime_state *gil = &_PyRuntime.ceval.gil;
324     gil->interval = microseconds;
325 }
326 
_PyEval_GetSwitchInterval()327 unsigned long _PyEval_GetSwitchInterval()
328 {
329     struct _gil_runtime_state *gil = &_PyRuntime.ceval.gil;
330     return gil->interval;
331 }
332