blob: bcc4fb9f6c343e68fe29821e577f323a73c8ae96 [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.
Antoine Pitroucf4cabb2009-11-11 18:11:36 +0000156 WaitForMultipleObjects() allows the event to be caught and the mutex
157 to be taken atomically.
158 As for SignalObjectAndWait(), its semantics are unfortunately a bit
159 more foggy. Many sources on the Web define it as atomically releasing
160 the first object while starting to wait on the second, but MSDN states
161 it is *not* atomic...
162
163 In any case, the emulation here is tailored for our particular use case.
164 For example, we don't care how many threads are woken up when a condition
165 gets signalled. Generic emulations of the pthread_cond_* API using
166 Win32 functions can be found on the Web.
167 The following read can be edificating (or not):
168 http://www.cse.wustl.edu/~schmidt/win32-cv-1.html
169*/
Antoine Pitrou074e5ed2009-11-10 19:50:40 +0000170#define COND_T HANDLE
171#define COND_INIT(cond) \
172 /* auto-reset, non-signalled */ \
173 if (!(cond = CreateEvent(NULL, FALSE, FALSE, NULL))) { \
174 Py_FatalError("CreateMutex(" #cond ") failed"); };
175#define COND_PREPARE(cond) \
176 if (!ResetEvent(cond)) { \
177 Py_FatalError("ResetEvent(" #cond ") failed"); };
178#define COND_SIGNAL(cond) \
179 if (!SetEvent(cond)) { \
180 Py_FatalError("SetEvent(" #cond ") failed"); };
181#define COND_WAIT(cond, mut) \
182 { \
Antoine Pitroucf4cabb2009-11-11 18:11:36 +0000183 if (SignalObjectAndWait(mut, cond, INFINITE, FALSE) != WAIT_OBJECT_0) \
184 Py_FatalError("SignalObjectAndWait(" #mut ", " #cond") failed"); \
185 MUTEX_LOCK(mut); \
Antoine Pitrou074e5ed2009-11-10 19:50:40 +0000186 }
187#define COND_TIMED_WAIT(cond, mut, microseconds, timeout_result) \
188 { \
189 DWORD r; \
190 HANDLE objects[2] = { cond, mut }; \
191 MUTEX_UNLOCK(mut); \
192 r = WaitForMultipleObjects(2, objects, TRUE, microseconds / 1000); \
193 if (r == WAIT_TIMEOUT) { \
194 MUTEX_LOCK(mut); \
195 timeout_result = 1; \
196 } \
197 else if (r != WAIT_OBJECT_0) \
198 Py_FatalError("WaitForSingleObject(" #cond ") failed"); \
199 else \
200 timeout_result = 0; \
201 }
202
203#else
204
205#error You need either a POSIX-compatible or a Windows system!
206
207#endif /* _POSIX_THREADS, NT_THREADS */
208
209
210/* Whether the GIL is already taken (-1 if uninitialized). This is volatile
211 because it can be read without any lock taken in ceval.c. */
212static volatile int gil_locked = -1;
213/* Number of GIL switches since the beginning. */
214static unsigned long gil_switch_number = 0;
215/* Last thread holding / having held the GIL. This helps us know whether
216 anyone else was scheduled after we dropped the GIL. */
217static PyThreadState *gil_last_holder = NULL;
218
219/* This condition variable allows one or several threads to wait until
220 the GIL is released. In addition, the mutex also protects the above
221 variables. */
222static COND_T gil_cond;
223static MUTEX_T gil_mutex;
224
225#ifdef FORCE_SWITCHING
226/* This condition variable helps the GIL-releasing thread wait for
227 a GIL-awaiting thread to be scheduled and take the GIL. */
228static COND_T switch_cond;
229static MUTEX_T switch_mutex;
230#endif
231
232
233static int gil_created(void)
234{
235 return gil_locked >= 0;
236}
237
238static void create_gil(void)
239{
240 MUTEX_INIT(gil_mutex);
241#ifdef FORCE_SWITCHING
242 MUTEX_INIT(switch_mutex);
243#endif
244 COND_INIT(gil_cond);
245#ifdef FORCE_SWITCHING
246 COND_INIT(switch_cond);
247#endif
248 gil_locked = 0;
249 gil_last_holder = NULL;
250}
251
252static void recreate_gil(void)
253{
254 create_gil();
255}
256
257static void drop_gil(PyThreadState *tstate)
258{
259 /* NOTE: tstate is allowed to be NULL. */
260 if (!gil_locked)
261 Py_FatalError("drop_gil: GIL is not locked");
262 if (tstate != NULL && tstate != gil_last_holder)
263 Py_FatalError("drop_gil: wrong thread state");
264
265 MUTEX_LOCK(gil_mutex);
266 gil_locked = 0;
267 COND_SIGNAL(gil_cond);
268#ifdef FORCE_SWITCHING
Antoine Pitroucf4cabb2009-11-11 18:11:36 +0000269 if (gil_drop_request)
270 COND_PREPARE(switch_cond);
Antoine Pitrou074e5ed2009-11-10 19:50:40 +0000271#endif
272 MUTEX_UNLOCK(gil_mutex);
273
274#ifdef FORCE_SWITCHING
275 if (gil_drop_request) {
276 MUTEX_LOCK(switch_mutex);
277 /* Not switched yet => wait */
278 if (gil_last_holder == tstate)
Antoine Pitroucf4cabb2009-11-11 18:11:36 +0000279 /* NOTE: if COND_WAIT does not atomically start waiting when
280 releasing the mutex, another thread can run through, take
281 the GIL and drop it again, and reset the condition
282 (COND_PREPARE above) before we even had a chance to wait
283 for it. */
Antoine Pitrou074e5ed2009-11-10 19:50:40 +0000284 COND_WAIT(switch_cond, switch_mutex);
285 MUTEX_UNLOCK(switch_mutex);
286 }
287#endif
288}
289
290static void take_gil(PyThreadState *tstate)
291{
292 int err;
293 if (tstate == NULL)
294 Py_FatalError("take_gil: NULL tstate");
295
296 err = errno;
297 MUTEX_LOCK(gil_mutex);
298
299 if (!gil_locked)
300 goto _ready;
301
302 COND_PREPARE(gil_cond);
303 while (gil_locked) {
304 int timed_out = 0;
305 unsigned long saved_switchnum;
306
307 saved_switchnum = gil_switch_number;
308 COND_TIMED_WAIT(gil_cond, gil_mutex, INTERVAL, timed_out);
309 /* If we timed out and no switch occurred in the meantime, it is time
310 to ask the GIL-holding thread to drop it. */
311 if (timed_out && gil_locked && gil_switch_number == saved_switchnum) {
312 SET_GIL_DROP_REQUEST();
313 }
314 }
315_ready:
316#ifdef FORCE_SWITCHING
317 /* This mutex must be taken before modifying gil_last_holder (see drop_gil()). */
318 MUTEX_LOCK(switch_mutex);
319#endif
320 /* We now hold the GIL */
321 gil_locked = 1;
322
323 if (tstate != gil_last_holder) {
324 gil_last_holder = tstate;
325 ++gil_switch_number;
326 }
327#ifdef FORCE_SWITCHING
328 COND_SIGNAL(switch_cond);
329 MUTEX_UNLOCK(switch_mutex);
330#endif
331 if (gil_drop_request) {
332 RESET_GIL_DROP_REQUEST();
333 }
334 if (tstate->async_exc != NULL) {
335 _PyEval_SignalAsyncExc();
336 }
337
338 MUTEX_UNLOCK(gil_mutex);
339 errno = err;
340}
341
342void _PyEval_SetSwitchInterval(unsigned long microseconds)
343{
344 gil_interval = microseconds;
345}
346
347unsigned long _PyEval_GetSwitchInterval()
348{
349 return gil_interval;
350}