Skip to content

Commit

Permalink
Removed the GIL. Don't merge this!
Browse files Browse the repository at this point in the history
Few programs work now.
  • Loading branch information
larryhastings committed Apr 14, 2016
1 parent 1c756e5 commit 4a1a4ff
Show file tree
Hide file tree
Showing 25 changed files with 1,285 additions and 190 deletions.
17 changes: 9 additions & 8 deletions Include/abstract.h
Original file line number Diff line number Diff line change
Expand Up @@ -503,15 +503,16 @@ xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx*/
void **buffer,
Py_ssize_t *buffer_len);

/*
Takes an arbitrary object which must support the (writable,
single segment) buffer interface and returns a pointer to a
writable memory location in buffer of size buffer_len.
/*
Lock or unlock an object. Locking and unlocking always succeeds.
0 is returned on success. buffer and buffer_len are only
set in case no error occurs. Otherwise, -1 is returned and
an exception set.
*/
If the object does not support locking no exception is set.
*/

PyAPI_FUNC(void) PyObject_Lock(PyObject *obj);
PyAPI_FUNC(void) PyObject_Unlock(PyObject *obj);
PyAPI_FUNC(void) PyObject_Lock2(PyObject *o1, PyObject *o2);
PyAPI_FUNC(void) PyObject_Unlock2(PyObject *o1, PyObject *o2);

/* new buffer API */

Expand Down
1 change: 1 addition & 0 deletions Include/dictobject.h
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ typedef struct _dictkeysobject PyDictKeysObject;
typedef struct {
PyObject_HEAD
Py_ssize_t ma_used;
furtex_t ma_lock;
PyDictKeysObject *ma_keys;
PyObject **ma_values;
} PyDictObject;
Expand Down
1 change: 1 addition & 0 deletions Include/listobject.h
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ typedef struct {
* the list is not yet visible outside the function that builds it.
*/
Py_ssize_t allocated;
furtex_t lock;
} PyListObject;
#endif

Expand Down
101 changes: 99 additions & 2 deletions Include/object.h
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,99 @@ whose size is determined when the object is allocated.
/* PyObject_HEAD defines the initial segment of every PyObject. */
#define PyObject_HEAD PyObject ob_base;


/*
** futex
**
** linux-specific fast userspace lock
*/

#include <errno.h>
#include <linux/futex.h>
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/mman.h>
#include <sys/syscall.h>
#include <sys/time.h>
#include <sys/wait.h>
#include <unistd.h>
#include <pyport.h>

Py_LOCAL_INLINE(int) futex(int *uaddr, int futex_op, int val,
const struct timespec *timeout, int *uaddr2, int val3) {
return syscall(SYS_futex, uaddr, futex_op, val,
timeout, uaddr, val3);
}

#define futex_wait(i_ptr, value) \
(futex(i_ptr, FUTEX_WAIT, value, NULL, NULL, 0))

#define futex_wake(i_ptr, value) \
(futex(i_ptr, FUTEX_WAKE, value, NULL, NULL, 0))

Py_LOCAL_INLINE(void) futex_init(int *f) {
*f = 0;
}

Py_LOCAL_INLINE(void) futex_lock(int *f) {
int current = __sync_val_compare_and_swap(f, 0, 1);
if (current == 0)
return;
if (current != 2)
current = __sync_lock_test_and_set(f, 2);
while (current != 0) {
futex_wait(f, 2);
current = __sync_lock_test_and_set(f, 2);
}
}

Py_LOCAL_INLINE(void) futex_unlock(int *f) {
if (__sync_fetch_and_sub(f, 1) != 1) {
*f = 0;
futex_wake(f, 1);
}
}


/*
** furtex
**
** recursive futex lock
*/
typedef struct {
int futex;
int count;
pthread_t tid;
} furtex_t;

Py_LOCAL_INLINE(void) furtex_init(furtex_t *f) {
f->futex = f->count = 0;
f->tid = 0;
}

Py_LOCAL_INLINE(void) furtex_lock(furtex_t *f) {
pthread_t tid = pthread_self();
if (f->count && pthread_equal(f->tid, tid)) {
f->count++;
assert(f->count > 1);
return;
}
futex_lock(&(f->futex));
f->tid = tid;
assert(f->count == 0);
f->count = 1;
}

Py_LOCAL_INLINE(void) furtex_unlock(furtex_t *f) {
/* this function assumes we own the lock! */
assert(f->count > 0);
if (--f->count)
return;
futex_unlock(&(f->futex));
}


#define PyObject_HEAD_INIT(type) \
{ _PyObject_EXTRA_INIT \
1, type },
Expand Down Expand Up @@ -173,6 +266,7 @@ typedef PyObject *(*ssizessizeargfunc)(PyObject *, Py_ssize_t, Py_ssize_t);
typedef int(*ssizeobjargproc)(PyObject *, Py_ssize_t, PyObject *);
typedef int(*ssizessizeobjargproc)(PyObject *, Py_ssize_t, Py_ssize_t, PyObject *);
typedef int(*objobjargproc)(PyObject *, PyObject *, PyObject *);
typedef void (*lockfunc)(PyObject *);

#ifndef Py_LIMITED_API
/* buffer interface */
Expand Down Expand Up @@ -421,6 +515,9 @@ typedef struct _typeobject {

destructor tp_finalize;

lockfunc tp_lock;
lockfunc tp_unlock;

#ifdef COUNT_ALLOCS
/* these must be last and never explicitly initialized */
Py_ssize_t tp_allocs;
Expand Down Expand Up @@ -776,13 +873,13 @@ PyAPI_FUNC(void) _Py_Dealloc(PyObject *);

#define Py_INCREF(op) ( \
_Py_INC_REFTOTAL _Py_REF_DEBUG_COMMA \
((PyObject *)(op))->ob_refcnt++)
__sync_fetch_and_add(&(((PyObject *)(op))->ob_refcnt), 1) )

#define Py_DECREF(op) \
do { \
PyObject *_py_decref_tmp = (PyObject *)(op); \
if (_Py_DEC_REFTOTAL _Py_REF_DEBUG_COMMA \
--(_py_decref_tmp)->ob_refcnt != 0) \
__sync_sub_and_fetch(&(_py_decref_tmp->ob_refcnt),1) != 0) \
_Py_CHECK_REFCNT(_py_decref_tmp) \
else \
_Py_Dealloc(_py_decref_tmp); \
Expand Down
17 changes: 17 additions & 0 deletions Include/objimpl.h
Original file line number Diff line number Diff line change
Expand Up @@ -253,6 +253,9 @@ typedef union _gc_head {
double dummy; /* force worst-case alignment */
} PyGC_Head;

PyAPI_FUNC(void) gc_lock(void);
PyAPI_FUNC(void) gc_lock2(furtex_t *);
PyAPI_FUNC(void) gc_unlock(void);
extern PyGC_Head *_PyGC_generation0;

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

/* Tell the GC to track this object. NB: While the object is tracked the
* collector it must be safe to call the ob_traverse method. */
#if 0
#define _PyObject_GC_TRACK(o) do { \
PyGC_Head *g = _Py_AS_GC(o); \
gc_lock(); \
if (_PyGCHead_REFS(g) != _PyGC_REFS_UNTRACKED) \
Py_FatalError("GC object already tracked"); \
_PyGCHead_SET_REFS(g, _PyGC_REFS_REACHABLE); \
g->gc.gc_next = _PyGC_generation0; \
g->gc.gc_prev = _PyGC_generation0->gc.gc_prev; \
g->gc.gc_prev->gc.gc_next = g; \
_PyGC_generation0->gc.gc_prev = g; \
gc_unlock(); \
} while (0);

/* Tell the GC to stop tracking this object.
Expand All @@ -304,13 +310,23 @@ extern PyGC_Head *_PyGC_generation0;
*/
#define _PyObject_GC_UNTRACK(o) do { \
PyGC_Head *g = _Py_AS_GC(o); \
gc_lock(); \
assert(_PyGCHead_REFS(g) != _PyGC_REFS_UNTRACKED); \
_PyGCHead_SET_REFS(g, _PyGC_REFS_UNTRACKED); \
g->gc.gc_prev->gc.gc_next = g->gc.gc_next; \
g->gc.gc_next->gc.gc_prev = g->gc.gc_prev; \
g->gc.gc_next = NULL; \
gc_unlock(); \
} while (0);

#else

#define _PyObject_GC_TRACK(o)

#define _PyObject_GC_UNTRACK(o)

#endif

/* True if the object is currently tracked by the GC. */
#define _PyObject_GC_IS_TRACKED(o) \
(_PyGC_REFS(o) != _PyGC_REFS_UNTRACKED)
Expand All @@ -320,6 +336,7 @@ extern PyGC_Head *_PyGC_generation0;
#define _PyObject_GC_MAY_BE_TRACKED(obj) \
(PyObject_IS_GC(obj) && \
(!PyTuple_CheckExact(obj) || _PyObject_GC_IS_TRACKED(obj)))

#endif /* Py_LIMITED_API */

PyAPI_FUNC(PyObject *) _PyObject_GC_Malloc(size_t size);
Expand Down
3 changes: 3 additions & 0 deletions Include/pystate.h
Original file line number Diff line number Diff line change
Expand Up @@ -189,9 +189,12 @@ PyAPI_FUNC(int) PyThreadState_SetAsyncExc(long, PyObject *);
/* Assuming the current thread holds the GIL, this is the
PyThreadState for the current thread. */
#ifdef Py_BUILD_CORE
/*
PyAPI_DATA(_Py_atomic_address) _PyThreadState_Current;
# define PyThreadState_GET() \
((PyThreadState*)_Py_atomic_load_relaxed(&_PyThreadState_Current))
*/
# define PyThreadState_GET() PyGILState_GetThisThreadState()
#else
# define PyThreadState_GET() PyThreadState_Get()
#endif
Expand Down
2 changes: 1 addition & 1 deletion Lib/test/test_ordered_dict.py
Original file line number Diff line number Diff line change
Expand Up @@ -627,7 +627,7 @@ def test_sizeof_exact(self):
size = support.calcobjsize
check = self.check_sizeof

basicsize = size('n2P' + '3PnPn2P') + calcsize('2nPn')
basicsize = size('n2PiiL' + '3PnPn2P') + calcsize('2nPn')
entrysize = calcsize('n2P') + calcsize('P')
nodesize = calcsize('Pn2P')

Expand Down
10 changes: 5 additions & 5 deletions Lib/test/test_sys.py
Original file line number Diff line number Diff line change
Expand Up @@ -916,9 +916,9 @@ def inner():
# method-wrapper (descriptor object)
check({}.__iter__, size('2P'))
# dict
check({}, size('n2P') + calcsize('2nPn') + 8*calcsize('n2P'))
check({}, size('n2PiiL') + calcsize('2nPn') + 8*calcsize('n2P'))
longdict = {1:1, 2:2, 3:3, 4:4, 5:5, 6:6, 7:7, 8:8}
check(longdict, size('n2P') + calcsize('2nPn') + 16*calcsize('n2P'))
check(longdict, size('n2PiiL') + calcsize('2nPn') + 16*calcsize('n2P'))
# dictionary-keyview
check({}.keys(), size('P'))
# dictionary-valueview
Expand Down Expand Up @@ -1064,22 +1064,22 @@ def delx(self): del self.__x
check((1,2,3), vsize('') + 3*self.P)
# type
# static type: PyTypeObject
s = vsize('P2n15Pl4Pn9Pn11PIP')
s = vsize('P2n15Pl4Pn9Pn11PIPPPPP')
check(int, s)
s = vsize('P2n15Pl4Pn9Pn11PIP' # PyTypeObject
'3P' # PyAsyncMethods
'36P' # PyNumberMethods
'3P' # PyMappingMethods
'10P' # PySequenceMethods
'2P' # PyBufferProcs
'4P')
'4PPPPP')
# Separate block for PyDictKeysObject with 4 entries
s += calcsize("2nPn") + 4*calcsize("n2P")
# class
class newstyleclass(object): pass
check(newstyleclass, s)
# dict with shared keys
check(newstyleclass().__dict__, size('n2P' + '2nPn'))
check(newstyleclass().__dict__, size('n2PiiL' + '2nPn'))
# unicode
# each tuple contains a string and its expected character size
# don't put any static strings here, as they may contain
Expand Down
13 changes: 12 additions & 1 deletion Modules/_collectionsmodule.c
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,10 @@
#include <sys/types.h> /* For size_t */
#endif

static furtex_t module_furtex = {0, 0, 0};
#define module_lock() furtex_lock(&module_furtex)
#define module_unlock() furtex_unlock(&module_furtex)

/* collections module implementation of a deque() datatype
Written and maintained by Raymond D. Hettinger <python@rcn.com>
*/
Expand Down Expand Up @@ -120,10 +124,14 @@ static block *freeblocks[MAXFREEBLOCKS];
static block *
newblock(void) {
block *b;
module_lock();
if (numfreeblocks) {
numfreeblocks--;
return freeblocks[numfreeblocks];
b = freeblocks[numfreeblocks];
module_unlock();
return b;
}
module_unlock();
b = PyMem_Malloc(sizeof(block));
if (b != NULL) {
return b;
Expand All @@ -135,10 +143,13 @@ newblock(void) {
static void
freeblock(block *b)
{
module_lock();
if (numfreeblocks < MAXFREEBLOCKS) {
freeblocks[numfreeblocks] = b;
numfreeblocks++;
module_unlock();
} else {
module_unlock();
PyMem_Free(b);
}
}
Expand Down
Loading

0 comments on commit 4a1a4ff

Please sign in to comment.