Skip to content

Commit f436650

Browse files
committed
Round-robin gil
1 parent 8d21aa2 commit f436650

File tree

3 files changed

+179
-14
lines changed

3 files changed

+179
-14
lines changed

Include/pythread.h

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,20 @@ PyAPI_FUNC(void) PyThread_delete_key_value(int key);
3434
/* Cleanup after a fork */
3535
PyAPI_FUNC(void) PyThread_ReInitTLS(void);
3636

37+
#ifndef _POSIX_THREADS
38+
#error Requires POSIX threads
39+
#endif
40+
41+
PyAPI_FUNC(void *) PyThread_mutex_alloc(void);
42+
PyAPI_FUNC(void) PyThread_mutex_free(void *);
43+
PyAPI_FUNC(void) PyThread_mutex_lock(void *);
44+
PyAPI_FUNC(void) PyThread_mutex_unlock(void *);
45+
46+
PyAPI_FUNC(void *) PyThread_cond_alloc(void);
47+
PyAPI_FUNC(void) PyThread_cond_free(void *);
48+
PyAPI_FUNC(void) PyThread_cond_wait(void *, void *);
49+
PyAPI_FUNC(void) PyThread_cond_signal(void *);
50+
3751
#ifdef __cplusplus
3852
}
3953
#endif

Python/ceval.c

Lines changed: 115 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -235,7 +235,31 @@ PyEval_GetCallStats(PyObject *self)
235235
#endif
236236
#include "pythread.h"
237237

238-
static PyThread_type_lock interpreter_lock = 0; /* This is the GIL */
238+
typedef void *PySpecial_cond_type;
239+
240+
struct special_linkstruct {
241+
PySpecial_cond_type wait;
242+
struct special_linkstruct *queue_next, *free_next;
243+
int in_use;
244+
};
245+
246+
typedef void *PySpecial_lock_type;
247+
248+
typedef struct {
249+
PySpecial_lock_type the_lock;
250+
struct special_linkstruct *wait_queue, *wait_last, *free_queue;
251+
} PySpecialSemaphore;
252+
253+
void
254+
PySpecial_init(PySpecialSemaphore *s)
255+
{
256+
s->the_lock = PyThread_mutex_alloc();
257+
s->wait_queue = NULL;
258+
s->wait_last = NULL;
259+
s->free_queue = NULL;
260+
}
261+
262+
static PySpecialSemaphore *interpreter_lock = NULL; /* This is the GIL */
239263
static PyThread_type_lock pending_lock = 0; /* for pending calls */
240264
static long main_thread = 0;
241265

@@ -245,26 +269,100 @@ PyEval_ThreadsInitialized(void)
245269
return interpreter_lock != 0;
246270
}
247271

272+
static PySpecialSemaphore *allocate_special(void)
273+
{
274+
PySpecialSemaphore *s = malloc(sizeof(PySpecialSemaphore));
275+
PySpecial_init(s);
276+
return s;
277+
}
278+
279+
static struct special_linkstruct *allocate_special_linkstruct(void)
280+
{
281+
struct special_linkstruct *ls = malloc(sizeof(struct special_linkstruct));
282+
ls->wait = PyThread_cond_alloc();
283+
ls->queue_next = NULL;
284+
ls->free_next = NULL;
285+
ls->in_use = 0;
286+
return ls;
287+
}
288+
289+
static void PySpecial_Lock(PySpecialSemaphore *s)
290+
{
291+
struct special_linkstruct *ls;
292+
293+
PyThread_mutex_lock(s->the_lock);
294+
295+
if (!s->free_queue)
296+
s->free_queue = allocate_special_linkstruct();
297+
298+
ls = s->free_queue;
299+
s->free_queue = ls->free_next;
300+
301+
if (!s->wait_queue)
302+
{
303+
ls->in_use = 1;
304+
s->wait_queue = ls;
305+
s->wait_last = ls;
306+
PyThread_mutex_unlock(s->the_lock);
307+
return;
308+
}
309+
310+
assert(s->wait_queue != ls);
311+
assert(s->wait_last != ls);
312+
assert(s->wait_last->queue_next == NULL);
313+
assert(!ls->in_use);
314+
s->wait_last->queue_next = ls;
315+
s->wait_last = ls;
316+
ls->in_use = 1;
317+
318+
while (s->wait_queue != ls)
319+
PyThread_cond_wait(ls->wait, s->the_lock);
320+
321+
PyThread_mutex_unlock(s->the_lock);
322+
}
323+
324+
static void PySpecial_Unlock(PySpecialSemaphore *s)
325+
{
326+
struct special_linkstruct *ls;
327+
328+
PyThread_mutex_lock(s->the_lock);
329+
ls = s->wait_queue;
330+
assert(ls->in_use);
331+
332+
s->wait_queue = ls->queue_next;
333+
if (s->wait_queue)
334+
{
335+
ls->queue_next = NULL;
336+
PyThread_cond_signal(s->wait_queue->wait);
337+
}
338+
ls->in_use = 0;
339+
340+
ls->free_next = s->free_queue;
341+
s->free_queue = ls;
342+
343+
PyThread_mutex_unlock(s->the_lock);
344+
}
345+
248346
void
249347
PyEval_InitThreads(void)
250348
{
251349
if (interpreter_lock)
252350
return;
253-
interpreter_lock = PyThread_allocate_lock();
254-
PyThread_acquire_lock(interpreter_lock, 1);
351+
interpreter_lock = allocate_special();
352+
PySpecial_Lock(interpreter_lock);
255353
main_thread = PyThread_get_thread_ident();
256354
}
257355

258356
void
259357
PyEval_AcquireLock(void)
260358
{
261-
PyThread_acquire_lock(interpreter_lock, 1);
359+
PySpecial_Lock(interpreter_lock);
262360
}
263361

264362
void
265363
PyEval_ReleaseLock(void)
266364
{
267-
PyThread_release_lock(interpreter_lock);
365+
PySpecial_Unlock(interpreter_lock);
268366
}
269367

270368
void
@@ -274,7 +372,7 @@ PyEval_AcquireThread(PyThreadState *tstate)
274372
Py_FatalError("PyEval_AcquireThread: NULL new thread state");
275373
/* Check someone has called PyEval_InitThreads() to create the lock */
276374
assert(interpreter_lock);
277-
PyThread_acquire_lock(interpreter_lock, 1);
375+
PySpecial_Lock(interpreter_lock);
278376
if (PyThreadState_Swap(tstate) != NULL)
279377
Py_FatalError(
280378
"PyEval_AcquireThread: non-NULL old thread state");
@@ -287,7 +385,7 @@ PyEval_ReleaseThread(PyThreadState *tstate)
287385
Py_FatalError("PyEval_ReleaseThread: NULL thread state");
288386
if (PyThreadState_Swap(NULL) != tstate)
289387
Py_FatalError("PyEval_ReleaseThread: wrong thread state");
290-
PyThread_release_lock(interpreter_lock);
388+
PySpecial_Unlock(interpreter_lock);
291389
}
292390

293391
/* This function is called from PyOS_AfterFork to ensure that newly
@@ -307,9 +405,9 @@ PyEval_ReInitThreads(void)
307405
much error-checking. Doing this cleanly would require
308406
adding a new function to each thread_*.h. Instead, just
309407
create a new lock and waste a little bit of memory */
310-
interpreter_lock = PyThread_allocate_lock();
408+
interpreter_lock = allocate_special();
311409
pending_lock = PyThread_allocate_lock();
312-
PyThread_acquire_lock(interpreter_lock, 1);
410+
PySpecial_Lock(interpreter_lock);
313411
main_thread = PyThread_get_thread_ident();
314412

315413
/* Update the threading module with the new state.
@@ -343,7 +441,7 @@ PyEval_SaveThread(void)
343441
Py_FatalError("PyEval_SaveThread: NULL tstate");
344442
#ifdef WITH_THREAD
345443
if (interpreter_lock)
346-
PyThread_release_lock(interpreter_lock);
444+
PySpecial_Unlock(interpreter_lock);
347445
#endif
348446
return tstate;
349447
}
@@ -356,7 +454,7 @@ PyEval_RestoreThread(PyThreadState *tstate)
356454
#ifdef WITH_THREAD
357455
if (interpreter_lock) {
358456
int err = errno;
359-
PyThread_acquire_lock(interpreter_lock, 1);
457+
PySpecial_Lock(interpreter_lock);
360458
errno = err;
361459
}
362460
#endif
@@ -1119,20 +1217,23 @@ PyEval_EvalFrameEx(PyFrameObject *f, int throwflag)
11191217
_Py_Ticker = 0;
11201218
}
11211219
#ifdef WITH_THREAD
1122-
if (interpreter_lock) {
1220+
if (interpreter_lock && interpreter_lock->wait_queue) {
11231221
/* Give another thread a chance */
11241222

11251223
if (PyThreadState_Swap(NULL) != tstate)
11261224
Py_FatalError("ceval: tstate mix-up");
1127-
PyThread_release_lock(interpreter_lock);
1225+
PySpecial_Unlock(interpreter_lock);
11281226

11291227
/* Other threads may run now */
11301228

1131-
PyThread_acquire_lock(interpreter_lock, 1);
1229+
PySpecial_Lock(interpreter_lock);
11321230

11331231
if (PyThreadState_Swap(tstate) != NULL)
11341232
Py_FatalError("ceval: orphan tstate");
11351233

1234+
}
1235+
1236+
if (interpreter_lock) {
11361237
/* Check for thread interrupts */
11371238

11381239
if (tstate->async_exc != NULL) {

Python/thread.c

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -151,6 +151,56 @@ static size_t _pythread_stacksize = 0;
151151
#endif
152152
*/
153153

154+
void *PyThread_mutex_alloc(void)
155+
{
156+
pthread_mutex_t *m = malloc(sizeof(pthread_mutex_t));
157+
if (pthread_mutex_init(m, NULL))
158+
Py_FatalError("PyThread_mutex_alloc: pthread_mutex_init failed");
159+
return m;
160+
}
161+
162+
void PyThread_mutex_free(void *m)
163+
{
164+
if (pthread_mutex_destroy(m))
165+
Py_FatalError("PyThread_mutex_free: pthread_mutex_destroy failed");
166+
free(m);
167+
}
168+
169+
void PyThread_mutex_lock(void *m)
170+
{
171+
pthread_mutex_lock(m);
172+
}
173+
174+
void PyThread_mutex_unlock(void *m)
175+
{
176+
pthread_mutex_unlock(m);
177+
}
178+
179+
void *PyThread_cond_alloc(void)
180+
{
181+
pthread_cond_t *c = malloc(sizeof(pthread_cond_t));
182+
if (pthread_cond_init(c, NULL))
183+
Py_FatalError("PyThread_cond_alloc: pthread_cond_init failed");
184+
return c;
185+
}
186+
187+
void PyThread_cond_free(void *c)
188+
{
189+
if (pthread_cond_destroy(c))
190+
Py_FatalError("PyThread_cond_free: pthread_cond_destroy failed");
191+
free(c);
192+
}
193+
194+
void PyThread_cond_wait(void *c, void *m)
195+
{
196+
pthread_cond_wait(c, m);
197+
}
198+
199+
void PyThread_cond_signal(void *c)
200+
{
201+
pthread_cond_signal(c);
202+
}
203+
154204
/* return the current thread stack size */
155205
size_t
156206
PyThread_get_stacksize(void)

0 commit comments

Comments
 (0)