blob: d4d6fdde6e5990110de2bfede7be5b6dce8f8f97 [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"); };
Antoine Pitroua1d23322009-11-12 22:56:02 +0000109#define COND_RESET(cond)
Antoine Pitrou074e5ed2009-11-10 19:50:40 +0000110#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"); };
Antoine Pitroua1d23322009-11-12 22:56:02 +0000175#define COND_RESET(cond) \
Antoine Pitrou074e5ed2009-11-10 19:50:40 +0000176 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);
Antoine Pitrou074e5ed2009-11-10 19:50:40 +0000268 MUTEX_UNLOCK(gil_mutex);
269
270#ifdef FORCE_SWITCHING
Antoine Pitroua1d23322009-11-12 22:56:02 +0000271 if (gil_drop_request && tstate != NULL) {
Antoine Pitrou074e5ed2009-11-10 19:50:40 +0000272 MUTEX_LOCK(switch_mutex);
273 /* Not switched yet => wait */
Antoine Pitroua1d23322009-11-12 22:56:02 +0000274 if (gil_last_holder == tstate) {
275 RESET_GIL_DROP_REQUEST();
Antoine Pitroucf4cabb2009-11-11 18:11:36 +0000276 /* NOTE: if COND_WAIT does not atomically start waiting when
277 releasing the mutex, another thread can run through, take
278 the GIL and drop it again, and reset the condition
Antoine Pitroua1d23322009-11-12 22:56:02 +0000279 before we even had a chance to wait for it. */
Antoine Pitrou074e5ed2009-11-10 19:50:40 +0000280 COND_WAIT(switch_cond, switch_mutex);
Antoine Pitroua1d23322009-11-12 22:56:02 +0000281 COND_RESET(switch_cond);
282 }
Antoine Pitrou074e5ed2009-11-10 19:50:40 +0000283 MUTEX_UNLOCK(switch_mutex);
284 }
285#endif
286}
287
288static void take_gil(PyThreadState *tstate)
289{
290 int err;
291 if (tstate == NULL)
292 Py_FatalError("take_gil: NULL tstate");
293
294 err = errno;
295 MUTEX_LOCK(gil_mutex);
296
297 if (!gil_locked)
298 goto _ready;
299
Antoine Pitroua1d23322009-11-12 22:56:02 +0000300 COND_RESET(gil_cond);
Antoine Pitrou074e5ed2009-11-10 19:50:40 +0000301 while (gil_locked) {
302 int timed_out = 0;
303 unsigned long saved_switchnum;
304
305 saved_switchnum = gil_switch_number;
306 COND_TIMED_WAIT(gil_cond, gil_mutex, INTERVAL, timed_out);
307 /* If we timed out and no switch occurred in the meantime, it is time
308 to ask the GIL-holding thread to drop it. */
309 if (timed_out && gil_locked && gil_switch_number == saved_switchnum) {
310 SET_GIL_DROP_REQUEST();
311 }
312 }
313_ready:
314#ifdef FORCE_SWITCHING
315 /* This mutex must be taken before modifying gil_last_holder (see drop_gil()). */
316 MUTEX_LOCK(switch_mutex);
317#endif
318 /* We now hold the GIL */
319 gil_locked = 1;
320
321 if (tstate != gil_last_holder) {
322 gil_last_holder = tstate;
323 ++gil_switch_number;
324 }
325#ifdef FORCE_SWITCHING
326 COND_SIGNAL(switch_cond);
327 MUTEX_UNLOCK(switch_mutex);
328#endif
329 if (gil_drop_request) {
330 RESET_GIL_DROP_REQUEST();
331 }
332 if (tstate->async_exc != NULL) {
333 _PyEval_SignalAsyncExc();
334 }
335
336 MUTEX_UNLOCK(gil_mutex);
337 errno = err;
338}
339
340void _PyEval_SetSwitchInterval(unsigned long microseconds)
341{
342 gil_interval = microseconds;
343}
344
345unsigned long _PyEval_GetSwitchInterval()
346{
347 return gil_interval;
348}