Author: Armin Rigo <[email protected]>
Branch:
Changeset: r925:96659ea5511f
Date: 2014-03-02 18:43 +0100
http://bitbucket.org/pypy/stmgc/changeset/96659ea5511f/
Log: Add "gil-c7", a small file that presents the same API to programs
but is implemented with a GIL. It contains the minor collector but
no major collector.
diff --git a/duhton/Makefile b/duhton/Makefile
--- a/duhton/Makefile
+++ b/duhton/Makefile
@@ -16,5 +16,14 @@
duhton_debug: *.c *.h $(C7SOURCES) $(C7HEADERS)
clang -DSTM_DEBUGPRINT -pthread -g -DDu_DEBUG -o duhton_debug *.c
../c7/stmgc.c -Wall
+
+duhton_nostm: *.c *.h ../gil-c7/stmgc.?
+ clang -pthread -g -DNDEBUG -O2 -o duhton_nostm *.c ../gil-c7/stmgc.c
-Wall -DUSE_GIL
+
+
+duhton_debug_nostm: *.c *.h ../gil-c7/stmgc.?
+ clang -DSTM_DEBUGPRINT -pthread -g -DDu_DEBUG -o duhton_debug_nostm *.c
../gil-c7/stmgc.c -Wall -DUSE_GIL -ferror-limit=1
+
+
clean:
rm -f duhton duhton_debug duhton_release
diff --git a/duhton/duhton.h b/duhton/duhton.h
--- a/duhton/duhton.h
+++ b/duhton/duhton.h
@@ -1,14 +1,18 @@
#ifndef _DUHTON_H_
#define _DUHTON_H_
+/* #undef USE_GIL */ /* forces "gil-c7" instead of "c7" */
+
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
-#include "../c7/stmgc.h"
+#ifdef USE_GIL
+# include "../gil-c7/stmgc.h"
+#else
+# include "../c7/stmgc.h"
+#endif
-#define STM 1 /* hackish removal of all read/write
- barriers. synchronization is up to
- the program */
+
#define DEFAULT_NUM_THREADS 2
extern __thread stm_thread_local_t stm_thread_local;
@@ -179,17 +183,8 @@
p1 = (typeof(p1))_pop_root())
-#if STM
#define _du_read1(p1) stm_read((object_t *)(p1))
#define _du_write1(p1) stm_write((object_t *)(p1))
-#else
-#define _du_read1(p1)
-#define _du_write1(p1) { \
- if (UNLIKELY(((object_t *)(p1))->stm_flags & GCFLAG_WRITE_BARRIER)) { \
- LIST_APPEND(_STM_TL->old_objects_to_trace, ((object_t *)(p1))); \
- ((object_t *)(p1))->stm_flags &= ~GCFLAG_WRITE_BARRIER; \
- }}
-#endif
#ifndef NDEBUG
diff --git a/gil-c7/stmgc.c b/gil-c7/stmgc.c
new file mode 100644
--- /dev/null
+++ b/gil-c7/stmgc.c
@@ -0,0 +1,280 @@
+#include "stmgc.h"
+#include <string.h>
+#include <stdio.h>
+
+pthread_mutex_t _stm_gil = PTHREAD_MUTEX_INITIALIZER;
+stm_thread_local_t *_stm_tloc;
+
+
+/************************************************************/
+
+struct list_s {
+ uintptr_t count;
+ uintptr_t last_allocated;
+ uintptr_t items[];
+};
+
+static struct list_s *list_create(void);
+
+static inline void list_free(struct list_s *lst)
+{
+ free(lst);
+}
+
+#define LIST_FREE(lst) (list_free(lst), (lst) = NULL)
+
+
+static struct list_s *_list_grow(struct list_s *, uintptr_t);
+
+static inline struct list_s *list_append(struct list_s *lst, uintptr_t item)
+{
+ uintptr_t index = lst->count++;
+ if (UNLIKELY(index > lst->last_allocated))
+ lst = _list_grow(lst, index);
+ lst->items[index] = item;
+ return lst;
+}
+
+#define LIST_APPEND(lst, e) ((lst) = list_append((lst), (uintptr_t)(e)))
+
+
+__attribute__((unused))
+static inline void list_clear(struct list_s *lst)
+{
+ lst->count = 0;
+}
+
+static inline bool list_is_empty(struct list_s *lst)
+{
+ return (lst->count == 0);
+}
+
+__attribute__((unused))
+static inline uintptr_t list_count(struct list_s *lst)
+{
+ return lst->count;
+}
+
+static inline uintptr_t list_pop_item(struct list_s *lst)
+{
+ assert(lst->count > 0);
+ return lst->items[--lst->count];
+}
+
+__attribute__((unused))
+static inline uintptr_t list_item(struct list_s *lst, uintptr_t index)
+{
+ return lst->items[index];
+}
+
+__attribute__((unused))
+static inline void list_set_item(struct list_s *lst, uintptr_t index,
+ uintptr_t newitem)
+{
+ lst->items[index] = newitem;
+}
+
+#define LIST_FOREACH_R(lst, TYPE, CODE) \
+ do { \
+ struct list_s *_lst = (lst); \
+ uintptr_t _i; \
+ for (_i = _lst->count; _i--; ) { \
+ TYPE item = (TYPE)_lst->items[_i]; \
+ CODE; \
+ } \
+ } while (0)
+
+#define LIST_SETSIZE(n) (sizeof(struct list_s) + LIST_ITEMSSIZE(n))
+#define LIST_ITEMSSIZE(n) ((n) * sizeof(uintptr_t))
+#define LIST_OVERCNT(n) (33 + ((((n) / 2) * 3) | 1))
+
+static struct list_s *list_create(void)
+{
+ uintptr_t initial_allocation = 32;
+ struct list_s *lst = malloc(LIST_SETSIZE(initial_allocation));
+ if (lst == NULL)
+ abort();
+
+ lst->count = 0;
+ lst->last_allocated = initial_allocation - 1;
+ return lst;
+}
+
+static struct list_s *_list_grow(struct list_s *lst, uintptr_t nalloc)
+{
+ nalloc = LIST_OVERCNT(nalloc);
+ lst = realloc(lst, LIST_SETSIZE(nalloc));
+ if (lst == NULL)
+ abort();
+
+ lst->last_allocated = nalloc - 1;
+ return lst;
+}
+
+/************************************************************/
+
+#define GCFLAG_WRITE_BARRIER _STM_GCFLAG_WRITE_BARRIER
+
+static struct list_s *objects_pointing_to_nursery;
+
+void stm_setup(void)
+{
+ objects_pointing_to_nursery = list_create();
+}
+
+void stm_teardown(void)
+{
+ list_free(objects_pointing_to_nursery);
+}
+
+void _stm_write_slowpath(object_t *obj)
+{
+ obj->gil_flags &= ~GCFLAG_WRITE_BARRIER;
+ LIST_APPEND(objects_pointing_to_nursery, obj);
+}
+
+object_t *_stm_allocate_old(ssize_t size)
+{
+ char *p = malloc(size);
+ assert(p);
+ memset(p, 0, size);
+ ((object_t *)p)->gil_flags = STM_FLAGS_PREBUILT;
+ return (object_t *)p;
+}
+
+object_t *_stm_allocate_external(ssize_t size)
+{
+ char *p = malloc(size);
+ assert(p);
+ memset(p, 0, size);
+ _stm_write_slowpath((object_t *)p);
+ return (object_t *)p;
+}
+
+/************************************************************/
+
+
+#define NB_NURSERY_PAGES 1024 // 4MB
+#define NURSERY_SIZE (NB_NURSERY_PAGES * 4096UL)
+
+char *_stm_nursery_base = NULL;
+char *_stm_nursery_current = NULL;
+char *_stm_nursery_end = NULL;
+
+static bool _is_in_nursery(object_t *obj)
+{
+ return ((char *)obj >= _stm_nursery_base &&
+ (char *)obj < _stm_nursery_end);
+}
+
+#define GCWORD_MOVED ((object_t *) -42)
+
+static void minor_trace_if_young(object_t **pobj)
+{
+ object_t *obj = *pobj;
+ object_t *nobj;
+
+ if (obj == NULL)
+ return;
+
+ if (_is_in_nursery(obj)) {
+ /* If the object was already seen here, its first word was set
+ to GCWORD_MOVED. In that case, the forwarding location, i.e.
+ where the object moved to, is stored in the second word in 'obj'. */
+ object_t *TLPREFIX *pforwarded_array = (object_t *TLPREFIX *)obj;
+
+ if (pforwarded_array[0] == GCWORD_MOVED) {
+ *pobj = pforwarded_array[1]; /* already moved */
+ return;
+ }
+
+ /* We need to make a copy of this object.
+ */
+ size_t size = stmcb_size_rounded_up(obj);
+
+ nobj = malloc(size);
+ assert(nobj);
+
+ /* Copy the object */
+ memcpy(nobj, obj, size);
+
+ /* Done copying the object. */
+ //dprintf(("\t\t\t\t\t%p -> %p\n", obj, nobj));
+ pforwarded_array[0] = GCWORD_MOVED;
+ pforwarded_array[1] = nobj;
+ *pobj = nobj;
+ }
+
+ else {
+ /* The object was not in the nursery at all */
+ return;
+ }
+
+ /* Must trace the object later */
+ LIST_APPEND(objects_pointing_to_nursery, nobj);
+}
+
+static void collect_roots_in_nursery(void)
+{
+ object_t **current = _stm_tloc->shadowstack;
+ object_t **base = _stm_tloc->shadowstack_base;
+ while (current-- != base) {
+ minor_trace_if_young(current);
+ }
+ minor_trace_if_young(&_stm_tloc->thread_local_obj);
+}
+
+static inline void _collect_now(object_t *obj)
+{
+ assert(!_is_in_nursery(obj));
+
+ /* We must not have GCFLAG_WRITE_BARRIER so far. Add it now. */
+ assert(!(obj->gil_flags & GCFLAG_WRITE_BARRIER));
+ obj->gil_flags |= GCFLAG_WRITE_BARRIER;
+
+ /* Trace the 'obj' to replace pointers to nursery with pointers
+ outside the nursery, possibly forcing nursery objects out and
+ adding them to 'objects_pointing_to_nursery' as well. */
+ stmcb_trace(obj, &minor_trace_if_young);
+}
+
+static void collect_oldrefs_to_nursery(void)
+{
+ struct list_s *lst = objects_pointing_to_nursery;
+
+ while (!list_is_empty(lst)) {
+ object_t *obj = (object_t *)list_pop_item(lst);
+
+ _collect_now(obj);
+
+ /* the list could have moved while appending */
+ lst = objects_pointing_to_nursery;
+ }
+}
+
+static void throw_away_nursery(void)
+{
+ if (_stm_nursery_base == NULL) {
+ _stm_nursery_base = malloc(NURSERY_SIZE);
+ assert(_stm_nursery_base);
+ _stm_nursery_end = _stm_nursery_base + NURSERY_SIZE;
+ }
+
+ _stm_nursery_current = _stm_nursery_base;
+ memset(_stm_nursery_base, 0, NURSERY_SIZE);
+}
+
+object_t *_stm_allocate_slowpath(ssize_t size_rounded_up)
+{
+ /* run minor collection */
+ //fprintf(stderr, "minor collect\n");
+ collect_roots_in_nursery();
+ collect_oldrefs_to_nursery();
+ throw_away_nursery();
+
+ char *p = _stm_nursery_current;
+ char *end = p + size_rounded_up;
+ assert(end <= _stm_nursery_end);
+ _stm_nursery_current = end;
+ return (object_t *)p;
+}
diff --git a/gil-c7/stmgc.h b/gil-c7/stmgc.h
new file mode 100644
--- /dev/null
+++ b/gil-c7/stmgc.h
@@ -0,0 +1,105 @@
+#include <stddef.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include <assert.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <pthread.h>
+
+#define TLPREFIX /* nothing */
+
+
+typedef struct { /* empty */ } stm_jmpbuf_t;
+
+typedef struct object_s {
+ uint32_t gil_flags;
+} object_t;
+
+typedef struct {
+ object_t **shadowstack;
+ object_t **shadowstack_base;
+ object_t *thread_local_obj;
+} stm_thread_local_t;
+
+extern stm_thread_local_t *_stm_tloc;
+extern char *_stm_nursery_current, *_stm_nursery_end;
+
+#ifdef NDEBUG
+#define OPT_ASSERT(cond) do { if (!(cond)) __builtin_unreachable(); } while (0)
+#else
+#define OPT_ASSERT(cond) assert(cond)
+#endif
+#define UNLIKELY(x) __builtin_expect(x, false)
+
+#define _STM_GCFLAG_WRITE_BARRIER 0x01
+#define _STM_FAST_ALLOC (66*1024)
+#define STM_FLAGS_PREBUILT _STM_GCFLAG_WRITE_BARRIER
+
+
+object_t *_stm_allocate_old(ssize_t size);
+
+object_t *_stm_allocate_external(ssize_t);
+object_t *_stm_allocate_slowpath(ssize_t);
+
+inline static object_t *stm_allocate(ssize_t size_rounded_up) {
+ OPT_ASSERT(size_rounded_up >= 16);
+ OPT_ASSERT((size_rounded_up & 7) == 0);
+
+ if (UNLIKELY(size_rounded_up >= _STM_FAST_ALLOC))
+ return _stm_allocate_external(size_rounded_up);
+
+ char *p = _stm_nursery_current;
+ char *end = p + size_rounded_up;
+ _stm_nursery_current = end;
+ if (UNLIKELY(end > _stm_nursery_end))
+ return _stm_allocate_slowpath(size_rounded_up);
+
+ return (object_t *)p;
+}
+
+inline static void stm_register_thread_local(stm_thread_local_t *tl) {
+ tl->thread_local_obj = NULL;
+ tl->shadowstack_base = (object_t **)malloc(768*1024);
+ assert(tl->shadowstack_base);
+ tl->shadowstack = tl->shadowstack_base;
+}
+inline static void stm_unregister_thread_local(stm_thread_local_t *tl) {
+ free(tl->shadowstack_base);
+}
+
+extern pthread_mutex_t _stm_gil;
+
+void stm_setup(void);
+void stm_teardown(void);
+
+inline static void stm_start_inevitable_transaction(stm_thread_local_t *tl) {
+ if (pthread_mutex_lock(&_stm_gil) != 0) abort();
+ _stm_tloc = tl;
+}
+inline static void stm_commit_transaction(void) {
+ _stm_tloc = NULL;
+ if (pthread_mutex_unlock(&_stm_gil) != 0) abort();
+}
+inline static void stm_become_inevitable(const char *msg) { }
+inline static void stm_read(object_t *ob) { }
+
+void _stm_write_slowpath(object_t *);
+
+inline static void stm_write(object_t *ob) {
+ if (UNLIKELY(ob->gil_flags & _STM_GCFLAG_WRITE_BARRIER))
+ _stm_write_slowpath(ob);
+}
+
+inline static char *_stm_real_address(object_t *ob) { return (char *)ob; }
+
+#define STM_START_TRANSACTION(tl, here) do { \
+ (void)&(here); \
+ stm_start_inevitable_transaction(tl); \
+} while (0)
+
+#define STM_PUSH_ROOT(tl, p) (*((tl).shadowstack++) = (object_t *)(p))
+#define STM_POP_ROOT(tl, p) ((p) = (typeof(p))*(--(tl).shadowstack))
+
+
+extern ssize_t stmcb_size_rounded_up(struct object_s *);
+extern void stmcb_trace(struct object_s *, void (object_t **));
_______________________________________________
pypy-commit mailing list
[email protected]
https://mail.python.org/mailman/listinfo/pypy-commit