blob: 2687f9524da9be1486b46e7698e4384967992146 [file] [log] [blame]
Antoine Pitrou074e5ed2009-11-10 19:50:40 +00001/*
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
13static 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#ifndef _POSIX_THREADS
63/* This means pthreads are not implemented in libc headers, hence the macro
64 not present in unistd.h. But they still can be implemented as an external
65 library (e.g. gnu pth in pthread emulation) */
66# ifdef HAVE_PTHREAD_H
67# include <pthread.h> /* _POSIX_THREADS */
68# endif
69#endif
70
71
72#ifdef _POSIX_THREADS
73
74/*
75 * POSIX support
76 */
77
78#include <pthread.h>
79
80#define ADD_MICROSECONDS(tv, interval) \
81do { \
82 tv.tv_usec += (long) interval; \
83 tv.tv_sec += tv.tv_usec / 1000000; \
84 tv.tv_usec %= 1000000; \
85} while (0)
86
87/* We assume all modern POSIX systems have gettimeofday() */
88#ifdef GETTIMEOFDAY_NO_TZ
89#define GETTIMEOFDAY(ptv) gettimeofday(ptv)
90#else
91#define GETTIMEOFDAY(ptv) gettimeofday(ptv, (struct timezone *)NULL)
92#endif
93
94#define MUTEX_T pthread_mutex_t
95#define MUTEX_INIT(mut) \
96 if (pthread_mutex_init(&mut, NULL)) { \
97 Py_FatalError("pthread_mutex_init(" #mut ") failed"); };
98#define MUTEX_LOCK(mut) \
99 if (pthread_mutex_lock(&mut)) { \
100 Py_FatalError("pthread_mutex_lock(" #mut ") failed"); };
101#define MUTEX_UNLOCK(mut) \
102 if (pthread_mutex_unlock(&mut)) { \
103 Py_FatalError("pthread_mutex_unlock(" #mut ") failed"); };
104
105#define COND_T pthread_cond_t
106#define COND_INIT(cond) \
107 if (pthread_cond_init(&cond, NULL)) { \
108 Py_FatalError("pthread_cond_init(" #cond ") failed"); };
109#define COND_PREPARE(cond)
110#define COND_SIGNAL(cond) \
111 if (pthread_cond_signal(&cond)) { \
112 Py_FatalError("pthread_cond_signal(" #cond ") failed"); };
113#define COND_WAIT(cond, mut) \
114 if (pthread_cond_wait(&cond, &mut)) { \
115 Py_FatalError("pthread_cond_wait(" #cond ") failed"); };
116#define COND_TIMED_WAIT(cond, mut, microseconds, timeout_result) \
117 { \
118 int r; \
119 struct timespec ts; \
120 struct timeval deadline; \
121 \
122 GETTIMEOFDAY(&deadline); \
123 ADD_MICROSECONDS(deadline, microseconds); \
124 ts.tv_sec = deadline.tv_sec; \
125 ts.tv_nsec = deadline.tv_usec * 1000; \
126 \
127 r = pthread_cond_timedwait(&cond, &mut, &ts); \
128 if (r == ETIMEDOUT) \
129 timeout_result = 1; \
130 else if (r) \
131 Py_FatalError("pthread_cond_timedwait(" #cond ") failed"); \
132 else \
133 timeout_result = 0; \
134 } \
135
136#elif defined(NT_THREADS)
137
138/*
139 * Windows (2000 and later, as well as (hopefully) CE) support
140 */
141
142#include <windows.h>
143
144#define MUTEX_T HANDLE
145#define MUTEX_INIT(mut) \
146 if (!(mut = CreateMutex(NULL, FALSE, NULL))) { \
147 Py_FatalError("CreateMutex(" #mut ") failed"); };
148#define MUTEX_LOCK(mut) \
149 if (WaitForSingleObject(mut, INFINITE) != WAIT_OBJECT_0) { \
150 Py_FatalError("WaitForSingleObject(" #mut ") failed"); };
151#define MUTEX_UNLOCK(mut) \
152 if (!ReleaseMutex(mut)) { \
153 Py_FatalError("ReleaseMutex(" #mut ") failed"); };
154
155/* We emulate condition variables with events. It is sufficient here.
156 (WaitForMultipleObjects() allows the event to be caught and the mutex
157 to be taken atomically) */
158#define COND_T HANDLE
159#define COND_INIT(cond) \
160 /* auto-reset, non-signalled */ \
161 if (!(cond = CreateEvent(NULL, FALSE, FALSE, NULL))) { \
162 Py_FatalError("CreateMutex(" #cond ") failed"); };
163#define COND_PREPARE(cond) \
164 if (!ResetEvent(cond)) { \
165 Py_FatalError("ResetEvent(" #cond ") failed"); };
166#define COND_SIGNAL(cond) \
167 if (!SetEvent(cond)) { \
168 Py_FatalError("SetEvent(" #cond ") failed"); };
169#define COND_WAIT(cond, mut) \
170 { \
171 DWORD r; \
172 HANDLE objects[2] = { cond, mut }; \
173 MUTEX_UNLOCK(mut); \
174 r = WaitForMultipleObjects(2, objects, TRUE, INFINITE); \
175 if (r != WAIT_OBJECT_0) \
176 Py_FatalError("WaitForSingleObject(" #cond ") failed"); \
177 }
178#define COND_TIMED_WAIT(cond, mut, microseconds, timeout_result) \
179 { \
180 DWORD r; \
181 HANDLE objects[2] = { cond, mut }; \
182 MUTEX_UNLOCK(mut); \
183 r = WaitForMultipleObjects(2, objects, TRUE, microseconds / 1000); \
184 if (r == WAIT_TIMEOUT) { \
185 MUTEX_LOCK(mut); \
186 timeout_result = 1; \
187 } \
188 else if (r != WAIT_OBJECT_0) \
189 Py_FatalError("WaitForSingleObject(" #cond ") failed"); \
190 else \
191 timeout_result = 0; \
192 }
193
194#else
195
196#error You need either a POSIX-compatible or a Windows system!
197
198#endif /* _POSIX_THREADS, NT_THREADS */
199
200
201/* Whether the GIL is already taken (-1 if uninitialized). This is volatile
202 because it can be read without any lock taken in ceval.c. */
203static volatile int gil_locked = -1;
204/* Number of GIL switches since the beginning. */
205static unsigned long gil_switch_number = 0;
206/* Last thread holding / having held the GIL. This helps us know whether
207 anyone else was scheduled after we dropped the GIL. */
208static PyThreadState *gil_last_holder = NULL;
209
210/* This condition variable allows one or several threads to wait until
211 the GIL is released. In addition, the mutex also protects the above
212 variables. */
213static COND_T gil_cond;
214static MUTEX_T gil_mutex;
215
216#ifdef FORCE_SWITCHING
217/* This condition variable helps the GIL-releasing thread wait for
218 a GIL-awaiting thread to be scheduled and take the GIL. */
219static COND_T switch_cond;
220static MUTEX_T switch_mutex;
221#endif
222
223
224static int gil_created(void)
225{
226 return gil_locked >= 0;
227}
228
229static void create_gil(void)
230{
231 MUTEX_INIT(gil_mutex);
232#ifdef FORCE_SWITCHING
233 MUTEX_INIT(switch_mutex);
234#endif
235 COND_INIT(gil_cond);
236#ifdef FORCE_SWITCHING
237 COND_INIT(switch_cond);
238#endif
239 gil_locked = 0;
240 gil_last_holder = NULL;
241}
242
243static void recreate_gil(void)
244{
245 create_gil();
246}
247
248static void drop_gil(PyThreadState *tstate)
249{
250 /* NOTE: tstate is allowed to be NULL. */
251 if (!gil_locked)
252 Py_FatalError("drop_gil: GIL is not locked");
253 if (tstate != NULL && tstate != gil_last_holder)
254 Py_FatalError("drop_gil: wrong thread state");
255
256 MUTEX_LOCK(gil_mutex);
257 gil_locked = 0;
258 COND_SIGNAL(gil_cond);
259#ifdef FORCE_SWITCHING
260 COND_PREPARE(switch_cond);
261#endif
262 MUTEX_UNLOCK(gil_mutex);
263
264#ifdef FORCE_SWITCHING
265 if (gil_drop_request) {
266 MUTEX_LOCK(switch_mutex);
267 /* Not switched yet => wait */
268 if (gil_last_holder == tstate)
269 COND_WAIT(switch_cond, switch_mutex);
270 MUTEX_UNLOCK(switch_mutex);
271 }
272#endif
273}
274
275static void take_gil(PyThreadState *tstate)
276{
277 int err;
278 if (tstate == NULL)
279 Py_FatalError("take_gil: NULL tstate");
280
281 err = errno;
282 MUTEX_LOCK(gil_mutex);
283
284 if (!gil_locked)
285 goto _ready;
286
287 COND_PREPARE(gil_cond);
288 while (gil_locked) {
289 int timed_out = 0;
290 unsigned long saved_switchnum;
291
292 saved_switchnum = gil_switch_number;
293 COND_TIMED_WAIT(gil_cond, gil_mutex, INTERVAL, timed_out);
294 /* If we timed out and no switch occurred in the meantime, it is time
295 to ask the GIL-holding thread to drop it. */
296 if (timed_out && gil_locked && gil_switch_number == saved_switchnum) {
297 SET_GIL_DROP_REQUEST();
298 }
299 }
300_ready:
301#ifdef FORCE_SWITCHING
302 /* This mutex must be taken before modifying gil_last_holder (see drop_gil()). */
303 MUTEX_LOCK(switch_mutex);
304#endif
305 /* We now hold the GIL */
306 gil_locked = 1;
307
308 if (tstate != gil_last_holder) {
309 gil_last_holder = tstate;
310 ++gil_switch_number;
311 }
312#ifdef FORCE_SWITCHING
313 COND_SIGNAL(switch_cond);
314 MUTEX_UNLOCK(switch_mutex);
315#endif
316 if (gil_drop_request) {
317 RESET_GIL_DROP_REQUEST();
318 }
319 if (tstate->async_exc != NULL) {
320 _PyEval_SignalAsyncExc();
321 }
322
323 MUTEX_UNLOCK(gil_mutex);
324 errno = err;
325}
326
327void _PyEval_SetSwitchInterval(unsigned long microseconds)
328{
329 gil_interval = microseconds;
330}
331
332unsigned long _PyEval_GetSwitchInterval()
333{
334 return gil_interval;
335}