Skip to content

Commit 4a1a4ff

Browse files
committed
Removed the GIL. Don't merge this!
Few programs work now.
1 parent 1c756e5 commit 4a1a4ff

25 files changed

Lines changed: 1285 additions & 190 deletions

Include/abstract.h

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -503,15 +503,16 @@ xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx*/
503503
void **buffer,
504504
Py_ssize_t *buffer_len);
505505

506-
/*
507-
Takes an arbitrary object which must support the (writable,
508-
single segment) buffer interface and returns a pointer to a
509-
writable memory location in buffer of size buffer_len.
506+
/*
507+
Lock or unlock an object. Locking and unlocking always succeeds.
510508
511-
0 is returned on success. buffer and buffer_len are only
512-
set in case no error occurs. Otherwise, -1 is returned and
513-
an exception set.
514-
*/
509+
If the object does not support locking no exception is set.
510+
*/
511+
512+
PyAPI_FUNC(void) PyObject_Lock(PyObject *obj);
513+
PyAPI_FUNC(void) PyObject_Unlock(PyObject *obj);
514+
PyAPI_FUNC(void) PyObject_Lock2(PyObject *o1, PyObject *o2);
515+
PyAPI_FUNC(void) PyObject_Unlock2(PyObject *o1, PyObject *o2);
515516

516517
/* new buffer API */
517518

Include/dictobject.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ typedef struct _dictkeysobject PyDictKeysObject;
2323
typedef struct {
2424
PyObject_HEAD
2525
Py_ssize_t ma_used;
26+
furtex_t ma_lock;
2627
PyDictKeysObject *ma_keys;
2728
PyObject **ma_values;
2829
} PyDictObject;

Include/listobject.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ typedef struct {
3737
* the list is not yet visible outside the function that builds it.
3838
*/
3939
Py_ssize_t allocated;
40+
furtex_t lock;
4041
} PyListObject;
4142
#endif
4243

Include/object.h

Lines changed: 99 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,99 @@ whose size is determined when the object is allocated.
8282
/* PyObject_HEAD defines the initial segment of every PyObject. */
8383
#define PyObject_HEAD PyObject ob_base;
8484

85+
86+
/*
87+
** futex
88+
**
89+
** linux-specific fast userspace lock
90+
*/
91+
92+
#include <errno.h>
93+
#include <linux/futex.h>
94+
#include <pthread.h>
95+
#include <stdio.h>
96+
#include <stdlib.h>
97+
#include <sys/mman.h>
98+
#include <sys/syscall.h>
99+
#include <sys/time.h>
100+
#include <sys/wait.h>
101+
#include <unistd.h>
102+
#include <pyport.h>
103+
104+
Py_LOCAL_INLINE(int) futex(int *uaddr, int futex_op, int val,
105+
const struct timespec *timeout, int *uaddr2, int val3) {
106+
return syscall(SYS_futex, uaddr, futex_op, val,
107+
timeout, uaddr, val3);
108+
}
109+
110+
#define futex_wait(i_ptr, value) \
111+
(futex(i_ptr, FUTEX_WAIT, value, NULL, NULL, 0))
112+
113+
#define futex_wake(i_ptr, value) \
114+
(futex(i_ptr, FUTEX_WAKE, value, NULL, NULL, 0))
115+
116+
Py_LOCAL_INLINE(void) futex_init(int *f) {
117+
*f = 0;
118+
}
119+
120+
Py_LOCAL_INLINE(void) futex_lock(int *f) {
121+
int current = __sync_val_compare_and_swap(f, 0, 1);
122+
if (current == 0)
123+
return;
124+
if (current != 2)
125+
current = __sync_lock_test_and_set(f, 2);
126+
while (current != 0) {
127+
futex_wait(f, 2);
128+
current = __sync_lock_test_and_set(f, 2);
129+
}
130+
}
131+
132+
Py_LOCAL_INLINE(void) futex_unlock(int *f) {
133+
if (__sync_fetch_and_sub(f, 1) != 1) {
134+
*f = 0;
135+
futex_wake(f, 1);
136+
}
137+
}
138+
139+
140+
/*
141+
** furtex
142+
**
143+
** recursive futex lock
144+
*/
145+
typedef struct {
146+
int futex;
147+
int count;
148+
pthread_t tid;
149+
} furtex_t;
150+
151+
Py_LOCAL_INLINE(void) furtex_init(furtex_t *f) {
152+
f->futex = f->count = 0;
153+
f->tid = 0;
154+
}
155+
156+
Py_LOCAL_INLINE(void) furtex_lock(furtex_t *f) {
157+
pthread_t tid = pthread_self();
158+
if (f->count && pthread_equal(f->tid, tid)) {
159+
f->count++;
160+
assert(f->count > 1);
161+
return;
162+
}
163+
futex_lock(&(f->futex));
164+
f->tid = tid;
165+
assert(f->count == 0);
166+
f->count = 1;
167+
}
168+
169+
Py_LOCAL_INLINE(void) furtex_unlock(furtex_t *f) {
170+
/* this function assumes we own the lock! */
171+
assert(f->count > 0);
172+
if (--f->count)
173+
return;
174+
futex_unlock(&(f->futex));
175+
}
176+
177+
85178
#define PyObject_HEAD_INIT(type) \
86179
{ _PyObject_EXTRA_INIT \
87180
1, type },
@@ -173,6 +266,7 @@ typedef PyObject *(*ssizessizeargfunc)(PyObject *, Py_ssize_t, Py_ssize_t);
173266
typedef int(*ssizeobjargproc)(PyObject *, Py_ssize_t, PyObject *);
174267
typedef int(*ssizessizeobjargproc)(PyObject *, Py_ssize_t, Py_ssize_t, PyObject *);
175268
typedef int(*objobjargproc)(PyObject *, PyObject *, PyObject *);
269+
typedef void (*lockfunc)(PyObject *);
176270

177271
#ifndef Py_LIMITED_API
178272
/* buffer interface */
@@ -421,6 +515,9 @@ typedef struct _typeobject {
421515

422516
destructor tp_finalize;
423517

518+
lockfunc tp_lock;
519+
lockfunc tp_unlock;
520+
424521
#ifdef COUNT_ALLOCS
425522
/* these must be last and never explicitly initialized */
426523
Py_ssize_t tp_allocs;
@@ -776,13 +873,13 @@ PyAPI_FUNC(void) _Py_Dealloc(PyObject *);
776873

777874
#define Py_INCREF(op) ( \
778875
_Py_INC_REFTOTAL _Py_REF_DEBUG_COMMA \
779-
((PyObject *)(op))->ob_refcnt++)
876+
__sync_fetch_and_add(&(((PyObject *)(op))->ob_refcnt), 1) )
780877

781878
#define Py_DECREF(op) \
782879
do { \
783880
PyObject *_py_decref_tmp = (PyObject *)(op); \
784881
if (_Py_DEC_REFTOTAL _Py_REF_DEBUG_COMMA \
785-
--(_py_decref_tmp)->ob_refcnt != 0) \
882+
__sync_sub_and_fetch(&(_py_decref_tmp->ob_refcnt),1) != 0) \
786883
_Py_CHECK_REFCNT(_py_decref_tmp) \
787884
else \
788885
_Py_Dealloc(_py_decref_tmp); \

Include/objimpl.h

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -253,6 +253,9 @@ typedef union _gc_head {
253253
double dummy; /* force worst-case alignment */
254254
} PyGC_Head;
255255

256+
PyAPI_FUNC(void) gc_lock(void);
257+
PyAPI_FUNC(void) gc_lock2(furtex_t *);
258+
PyAPI_FUNC(void) gc_unlock(void);
256259
extern PyGC_Head *_PyGC_generation0;
257260

258261
#define _Py_AS_GC(o) ((PyGC_Head *)(o)-1)
@@ -287,15 +290,18 @@ extern PyGC_Head *_PyGC_generation0;
287290

288291
/* Tell the GC to track this object. NB: While the object is tracked the
289292
* collector it must be safe to call the ob_traverse method. */
293+
#if 0
290294
#define _PyObject_GC_TRACK(o) do { \
291295
PyGC_Head *g = _Py_AS_GC(o); \
296+
gc_lock(); \
292297
if (_PyGCHead_REFS(g) != _PyGC_REFS_UNTRACKED) \
293298
Py_FatalError("GC object already tracked"); \
294299
_PyGCHead_SET_REFS(g, _PyGC_REFS_REACHABLE); \
295300
g->gc.gc_next = _PyGC_generation0; \
296301
g->gc.gc_prev = _PyGC_generation0->gc.gc_prev; \
297302
g->gc.gc_prev->gc.gc_next = g; \
298303
_PyGC_generation0->gc.gc_prev = g; \
304+
gc_unlock(); \
299305
} while (0);
300306

301307
/* Tell the GC to stop tracking this object.
@@ -304,13 +310,23 @@ extern PyGC_Head *_PyGC_generation0;
304310
*/
305311
#define _PyObject_GC_UNTRACK(o) do { \
306312
PyGC_Head *g = _Py_AS_GC(o); \
313+
gc_lock(); \
307314
assert(_PyGCHead_REFS(g) != _PyGC_REFS_UNTRACKED); \
308315
_PyGCHead_SET_REFS(g, _PyGC_REFS_UNTRACKED); \
309316
g->gc.gc_prev->gc.gc_next = g->gc.gc_next; \
310317
g->gc.gc_next->gc.gc_prev = g->gc.gc_prev; \
311318
g->gc.gc_next = NULL; \
319+
gc_unlock(); \
312320
} while (0);
313321

322+
#else
323+
324+
#define _PyObject_GC_TRACK(o)
325+
326+
#define _PyObject_GC_UNTRACK(o)
327+
328+
#endif
329+
314330
/* True if the object is currently tracked by the GC. */
315331
#define _PyObject_GC_IS_TRACKED(o) \
316332
(_PyGC_REFS(o) != _PyGC_REFS_UNTRACKED)
@@ -320,6 +336,7 @@ extern PyGC_Head *_PyGC_generation0;
320336
#define _PyObject_GC_MAY_BE_TRACKED(obj) \
321337
(PyObject_IS_GC(obj) && \
322338
(!PyTuple_CheckExact(obj) || _PyObject_GC_IS_TRACKED(obj)))
339+
323340
#endif /* Py_LIMITED_API */
324341

325342
PyAPI_FUNC(PyObject *) _PyObject_GC_Malloc(size_t size);

Include/pystate.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -189,9 +189,12 @@ PyAPI_FUNC(int) PyThreadState_SetAsyncExc(long, PyObject *);
189189
/* Assuming the current thread holds the GIL, this is the
190190
PyThreadState for the current thread. */
191191
#ifdef Py_BUILD_CORE
192+
/*
192193
PyAPI_DATA(_Py_atomic_address) _PyThreadState_Current;
193194
# define PyThreadState_GET() \
194195
((PyThreadState*)_Py_atomic_load_relaxed(&_PyThreadState_Current))
196+
*/
197+
# define PyThreadState_GET() PyGILState_GetThisThreadState()
195198
#else
196199
# define PyThreadState_GET() PyThreadState_Get()
197200
#endif

Lib/test/test_ordered_dict.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -627,7 +627,7 @@ def test_sizeof_exact(self):
627627
size = support.calcobjsize
628628
check = self.check_sizeof
629629

630-
basicsize = size('n2P' + '3PnPn2P') + calcsize('2nPn')
630+
basicsize = size('n2PiiL' + '3PnPn2P') + calcsize('2nPn')
631631
entrysize = calcsize('n2P') + calcsize('P')
632632
nodesize = calcsize('Pn2P')
633633

Lib/test/test_sys.py

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -916,9 +916,9 @@ def inner():
916916
# method-wrapper (descriptor object)
917917
check({}.__iter__, size('2P'))
918918
# dict
919-
check({}, size('n2P') + calcsize('2nPn') + 8*calcsize('n2P'))
919+
check({}, size('n2PiiL') + calcsize('2nPn') + 8*calcsize('n2P'))
920920
longdict = {1:1, 2:2, 3:3, 4:4, 5:5, 6:6, 7:7, 8:8}
921-
check(longdict, size('n2P') + calcsize('2nPn') + 16*calcsize('n2P'))
921+
check(longdict, size('n2PiiL') + calcsize('2nPn') + 16*calcsize('n2P'))
922922
# dictionary-keyview
923923
check({}.keys(), size('P'))
924924
# dictionary-valueview
@@ -1064,22 +1064,22 @@ def delx(self): del self.__x
10641064
check((1,2,3), vsize('') + 3*self.P)
10651065
# type
10661066
# static type: PyTypeObject
1067-
s = vsize('P2n15Pl4Pn9Pn11PIP')
1067+
s = vsize('P2n15Pl4Pn9Pn11PIPPPPP')
10681068
check(int, s)
10691069
s = vsize('P2n15Pl4Pn9Pn11PIP' # PyTypeObject
10701070
'3P' # PyAsyncMethods
10711071
'36P' # PyNumberMethods
10721072
'3P' # PyMappingMethods
10731073
'10P' # PySequenceMethods
10741074
'2P' # PyBufferProcs
1075-
'4P')
1075+
'4PPPPP')
10761076
# Separate block for PyDictKeysObject with 4 entries
10771077
s += calcsize("2nPn") + 4*calcsize("n2P")
10781078
# class
10791079
class newstyleclass(object): pass
10801080
check(newstyleclass, s)
10811081
# dict with shared keys
1082-
check(newstyleclass().__dict__, size('n2P' + '2nPn'))
1082+
check(newstyleclass().__dict__, size('n2PiiL' + '2nPn'))
10831083
# unicode
10841084
# each tuple contains a string and its expected character size
10851085
# don't put any static strings here, as they may contain

Modules/_collectionsmodule.c

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,10 @@
77
#include <sys/types.h> /* For size_t */
88
#endif
99

10+
static furtex_t module_furtex = {0, 0, 0};
11+
#define module_lock() furtex_lock(&module_furtex)
12+
#define module_unlock() furtex_unlock(&module_furtex)
13+
1014
/* collections module implementation of a deque() datatype
1115
Written and maintained by Raymond D. Hettinger <python@rcn.com>
1216
*/
@@ -120,10 +124,14 @@ static block *freeblocks[MAXFREEBLOCKS];
120124
static block *
121125
newblock(void) {
122126
block *b;
127+
module_lock();
123128
if (numfreeblocks) {
124129
numfreeblocks--;
125-
return freeblocks[numfreeblocks];
130+
b = freeblocks[numfreeblocks];
131+
module_unlock();
132+
return b;
126133
}
134+
module_unlock();
127135
b = PyMem_Malloc(sizeof(block));
128136
if (b != NULL) {
129137
return b;
@@ -135,10 +143,13 @@ newblock(void) {
135143
static void
136144
freeblock(block *b)
137145
{
146+
module_lock();
138147
if (numfreeblocks < MAXFREEBLOCKS) {
139148
freeblocks[numfreeblocks] = b;
140149
numfreeblocks++;
150+
module_unlock();
141151
} else {
152+
module_unlock();
142153
PyMem_Free(b);
143154
}
144155
}

0 commit comments

Comments
 (0)