Author: Remi Meier
Branch: 
Changeset: r1097:e43e756ae446
Date: 2014-03-26 18:21 +0100
http://bitbucket.org/pypy/stmgc/changeset/e43e756ae446/

Log:    initial HTM version

diff --git a/duhton/Makefile b/duhton/Makefile
--- a/duhton/Makefile
+++ b/duhton/Makefile
@@ -25,5 +25,13 @@
        clang -DSTM_DEBUGPRINT -pthread -g -DDu_DEBUG -o duhton_debug_nostm *.c 
../gil-c7/stmgc.c -Wall -DUSE_GIL -ferror-limit=1
 
 
+duhton_htm: *.c *.h ../htm-c7/stmgc.? ../htm-c7/htm.h
+       clang -pthread -g -DNDEBUG -O2 -o duhton_htm *.c ../htm-c7/stmgc.c 
-Wall  -DUSE_HTM
+
+
+duhton_debug_htm: *.c *.h ../htm-c7/stmgc.? ../htm-c7/htm.h
+       clang -DSTM_DEBUGPRINT -pthread -g -DDu_DEBUG -o duhton_debug_htm *.c 
../htm-c7/stmgc.c -Wall -DUSE_HTM -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
@@ -6,11 +6,16 @@
 #include <stdio.h>
 #include <stdlib.h>
 #include <assert.h>
+
 #ifdef USE_GIL
 #  include "../gil-c7/stmgc.h"
 #else
+#ifdef USE_HTM
+#  include "../htm-c7/stmgc.h"
+#else
 #  include "../c7/stmgc.h"
 #endif
+#endif
 
 
 extern __thread stm_thread_local_t stm_thread_local;
diff --git a/gil-c7/stmgc.h b/gil-c7/stmgc.h
--- a/gil-c7/stmgc.h
+++ b/gil-c7/stmgc.h
@@ -11,6 +11,8 @@
 
 #define TLPREFIX    /* nothing */
 
+#define STM_NB_SEGMENTS    4
+
 
 typedef struct { /* empty */ } stm_jmpbuf_t;
 
diff --git a/htm-c7/htm.h b/htm-c7/htm.h
new file mode 100644
--- /dev/null
+++ b/htm-c7/htm.h
@@ -0,0 +1,73 @@
+#ifndef _HTM_H
+#define _HTM_H
+
+#include <stdio.h>
+#include <assert.h>
+#include <pthread.h>
+
+
+
+#define XBEGIN_OK              (~0)
+#define XBEGIN_UNKNOWN         (0)
+#define XBEGIN_XABORT          (1 << 0)
+#define XBEGIN_MAYBE_RETRY     (1 << 1)
+#define XBEGIN_NORMAL_CONFLICT (1 << 2)
+#define XBEGIN_BUFFER_OVERFLOW (1 << 3)
+#define XBEGIN_DEBUG           (1 << 4)
+#define XBEGIN_NESTED_ABORT    (1 << 5)
+#define XBEGIN_XABORT_ARG(x) (((x) >> 24) & 0xFF)
+
+static __thread char buf[128];
+char* xbegin_status(int status)
+{
+    if (status == XBEGIN_OK)
+        snprintf(buf, 128, "OK");
+    else if (status == XBEGIN_UNKNOWN)
+        snprintf(buf, 128, "UNKNOWN");
+    else if (status & XBEGIN_XABORT)
+        snprintf(buf, 128, "XABORT(%d)", XBEGIN_XABORT_ARG(status));
+    else if (status & XBEGIN_MAYBE_RETRY)
+        snprintf(buf, 128, "MAYBE_RETRY");
+    else if (status & XBEGIN_NORMAL_CONFLICT)
+        snprintf(buf, 128, "NORMAL_CONFLICT");
+    else if (status & XBEGIN_BUFFER_OVERFLOW)
+        snprintf(buf, 128, "BUFFER_OVERFLOW");
+    else if (status & XBEGIN_DEBUG)
+        snprintf(buf, 128, "DEBUG");
+    else if (status & XBEGIN_NESTED_ABORT)
+        snprintf(buf, 128, "NESTED_ABORT");
+    return buf;
+}
+
+static __attribute__((__always_inline__)) inline int xbegin()
+{
+    int result = XBEGIN_OK;
+    asm volatile(".byte 0xC7, 0xF8; .long 0" : "+a" (result) :: "memory");
+    return result;
+}
+
+static __attribute__((__always_inline__)) inline void xend()
+{
+    asm volatile(".byte 0x0F, 0x01, 0xD5" ::: "memory");
+}
+
+#define xabort(argument) do { asm volatile(".byte 0xC6, 0xF8, %P0" :: "i" 
(argument) : "memory"); } while (0);
+
+
+
+static __attribute__((__always_inline__)) inline int xtest()
+{
+    unsigned char result;
+    asm volatile(".byte 0x0F, 0x01, 0xD6; setnz %0" : "=r" (result) :: 
"memory");
+    return result;
+}
+
+
+static __attribute__((__always_inline__)) inline int 
mutex_locked(pthread_mutex_t* mut)
+{
+    return !!mut->__data.__lock;
+}
+
+
+
+#endif
diff --git a/htm-c7/stmgc.c b/htm-c7/stmgc.c
new file mode 100644
--- /dev/null
+++ b/htm-c7/stmgc.c
@@ -0,0 +1,402 @@
+#include "stmgc.h"
+#include <string.h>
+#include <stdio.h>
+#include "htm.h"
+
+pthread_mutex_t _stm_gil = PTHREAD_MUTEX_INITIALIZER;
+stm_thread_local_t *_stm_tloc;
+struct stm_segment_info_s _stm_segment;
+
+
+
+
+static void acquire_gil(stm_thread_local_t *tl) {
+    if (pthread_mutex_lock(&_stm_gil) == 0) {
+        _stm_tloc = tl;
+        return;
+    }
+    abort();
+}
+
+void stm_start_inevitable_transaction(stm_thread_local_t *tl) {
+    if (mutex_locked(&_stm_gil)) {
+        acquire_gil(tl);
+        return;
+    }
+
+    int status;
+ transaction_retry:
+    if ((status = xbegin()) == XBEGIN_OK) {
+        if (mutex_locked(&_stm_gil))
+            xabort(0);
+        /* transaction OK */
+    }
+    else {
+        if (mutex_locked(&_stm_gil))
+            acquire_gil(tl);
+        else if (status & (XBEGIN_MAYBE_RETRY | XBEGIN_NORMAL_CONFLICT))
+            goto transaction_retry;
+        else
+            acquire_gil(tl);
+    }
+
+    _stm_tloc = tl;
+}
+
+void stm_commit_transaction(void) {
+    stm_collect(0);
+    _stm_tloc = NULL;
+    if (mutex_locked(&_stm_gil)) {
+        assert(!xtest());
+        if (pthread_mutex_unlock(&_stm_gil) != 0) abort();
+    } else {
+        xend();
+    }
+}
+
+
+
+
+
+
+/************************************************************/
+
+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;
+static struct list_s *young_weakrefs;
+
+void stm_setup(void)
+{
+    objects_pointing_to_nursery = list_create();
+    young_weakrefs = 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_GCFLAG_WRITE_BARRIER;
+    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;
+#define _stm_nursery_start ((uintptr_t)_stm_nursery_base)
+
+static bool _is_in_nursery(object_t *obj)
+{
+    return ((char *)obj >= _stm_nursery_base &&
+            (char *)obj < _stm_nursery_end);
+}
+
+long stm_can_move(object_t *obj)
+{
+    return _is_in_nursery(obj);
+}
+
+#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);
+}
+
+#define WEAKREF_PTR(wr, sz)  ((object_t * TLPREFIX *)(((char *)(wr)) + (sz) - 
sizeof(void*)))
+
+static void move_young_weakrefs(void)
+{
+    LIST_FOREACH_R(
+        young_weakrefs,
+        object_t * /*item*/,
+        ({
+            assert(_is_in_nursery(item));
+            object_t *TLPREFIX *pforwarded_array = (object_t *TLPREFIX *)item;
+
+            /* the following checks are done like in nursery.c: */
+            if (pforwarded_array[0] != GCWORD_MOVED) {
+                /* weakref dies */
+                continue;
+            }
+
+            item = pforwarded_array[1]; /* moved location */
+
+            assert(!_is_in_nursery(item));
+
+            ssize_t size = 16;
+            object_t *pointing_to = *WEAKREF_PTR(item, size);
+            assert(pointing_to != NULL);
+
+            if (_is_in_nursery(pointing_to)) {
+                object_t *TLPREFIX *pforwarded_array = (object_t *TLPREFIX 
*)pointing_to;
+                /* the following checks are done like in nursery.c: */
+                if (pforwarded_array[0] != GCWORD_MOVED) {
+                    /* pointing_to dies */
+                    *WEAKREF_PTR(item, size) = NULL;
+                    continue;   /* no need to remember in old_weakrefs */
+                }
+                else {
+                    /* moved location */
+                    *WEAKREF_PTR(item, size) = pforwarded_array[1];
+                }
+            }
+            else {
+                /* pointing_to was already old */
+            }
+            //LIST_APPEND(STM_PSEGMENT->old_weakrefs, item);
+        }));
+    list_clear(young_weakrefs);
+}
+
+void stm_collect(long level)
+{
+    /* 'level' is ignored, only minor collections are implemented */
+    collect_roots_in_nursery();
+    collect_oldrefs_to_nursery();
+    move_young_weakrefs();
+    throw_away_nursery();
+}
+
+object_t *_stm_allocate_slowpath(ssize_t size_rounded_up)
+{
+    /* run minor collection */
+    //fprintf(stderr, "minor collect\n");
+    stm_collect(0);
+
+    char *p = _stm_nursery_current;
+    char *end = p + size_rounded_up;
+    assert(end <= _stm_nursery_end);
+    _stm_nursery_current = end;
+    return (object_t *)p;
+}
+
+object_t *stm_allocate_weakref(ssize_t size_rounded_up)
+{
+    assert(size_rounded_up == 16);
+    object_t *obj = stm_allocate(size_rounded_up);
+    LIST_APPEND(young_weakrefs, obj);
+    return obj;
+}
diff --git a/htm-c7/stmgc.h b/htm-c7/stmgc.h
new file mode 100644
--- /dev/null
+++ b/htm-c7/stmgc.h
@@ -0,0 +1,152 @@
+#ifndef _STMGC_H
+#define _STMGC_H
+
+#include <stddef.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include <assert.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <pthread.h>
+
+#define TLPREFIX    /* nothing */
+
+#define STM_NB_SEGMENTS    4
+
+typedef struct { /* empty */ } stm_jmpbuf_t;
+
+typedef struct object_s {
+    uint32_t gil_flags;
+} object_t;
+
+typedef struct stm_thread_local_s {
+    object_t **shadowstack;
+    object_t **shadowstack_base;
+    object_t *thread_local_obj;
+    long last_abort__bytes_in_nursery;
+}  stm_thread_local_t;
+
+extern stm_thread_local_t *_stm_tloc;
+extern char *_stm_nursery_current, *_stm_nursery_end;
+
+struct stm_segment_info_s {
+    stm_jmpbuf_t *jmpbuf_ptr;  /* compat only -- always NULL */
+    char *nursery_current;     /* compat only -- always NULL */
+};
+extern struct stm_segment_info_s _stm_segment;
+#define STM_SEGMENT (&_stm_segment)
+
+#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)
+
+
+object_t *_stm_allocate_old(ssize_t size);
+
+object_t *_stm_allocate_external(ssize_t);
+object_t *_stm_allocate_slowpath(ssize_t);
+object_t *stm_allocate_weakref(ssize_t size_rounded_up);
+
+__attribute__((always_inline))
+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;
+    tl->last_abort__bytes_in_nursery = 0;
+}
+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);
+void stm_collect(long level);
+
+
+void stm_start_inevitable_transaction(stm_thread_local_t *tl);
+void stm_commit_transaction(void);
+
+inline static void stm_become_inevitable(
+    stm_thread_local_t *tl, const char *msg) { }
+inline static void _stm_become_inevitable(const char *msg) { }
+inline static void stm_become_globally_unique_transaction(
+    stm_thread_local_t *tl, const char *msg) { }
+
+static inline int stm_is_inevitable(void) { return 1; }
+inline static void stm_read(object_t *ob) { }
+
+void _stm_write_slowpath(object_t *);
+
+__attribute__((always_inline))
+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; }
+static inline void stm_safe_point(void) { }
+
+#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))
+#define STM_POP_ROOT_RET(tl)    (*(--(tl).shadowstack))
+
+
+extern ssize_t stmcb_size_rounded_up(struct object_s *);
+extern void stmcb_trace(struct object_s *, void (object_t **));
+
+inline static object_t *stm_setup_prebuilt(object_t *preb) {
+    if (preb != NULL)
+        preb->gil_flags |= _STM_GCFLAG_WRITE_BARRIER;
+    return preb;
+}
+inline static object_t *stm_setup_prebuilt_weakref(object_t *preb) {
+    return stm_setup_prebuilt(preb);
+}
+
+inline static long stm_identityhash(object_t *obj) {
+    return (long)obj;   // XXX fails after a minor collection
+}
+inline static long stm_id(object_t *obj) {
+    return (long)obj;
+}
+inline static void stm_set_prebuilt_identityhash(object_t *obj, long hash) {
+    // XXX ignored
+}
+long stm_can_move(object_t *);
+
+inline static void stm_call_on_abort(stm_thread_local_t *tl, void *key,
+                                     void callback(void *)) {
+    // XXX ignored
+}
+
+#endif
_______________________________________________
pypy-commit mailing list
[email protected]
https://mail.python.org/mailman/listinfo/pypy-commit

Reply via email to