Author: Armin Rigo <ar...@tunes.org>
Branch: stmgc-c7
Changeset: r69887:e0337a4058f2
Date: 2014-03-12 09:33 +0100
http://bitbucket.org/pypy/pypy/changeset/e0337a4058f2/

Log:    Reintroduce stm_perform_transaction() and atomic transactions.

diff --git a/rpython/rlib/rstm.py b/rpython/rlib/rstm.py
--- a/rpython/rlib/rstm.py
+++ b/rpython/rlib/rstm.py
@@ -66,11 +66,11 @@
 
 @dont_look_inside
 def increment_atomic():
-    llop.stm_change_atomic(lltype.Signed, +1)
+    llop.stm_increment_atomic(lltype.Void)
 
 @dont_look_inside
 def decrement_atomic():
-    llop.stm_change_atomic(lltype.Signed, -1)
+    llop.stm_decrement_atomic(lltype.Void)
 
 @dont_look_inside
 def is_atomic():
@@ -96,7 +96,7 @@
 def before_external_call():
     if we_are_translated():
         # this tries to commit, or becomes inevitable if atomic
-        llop.stm_commit_transaction(lltype.Void)
+        llop.stm_commit_if_not_atomic(lltype.Void)
 before_external_call._dont_reach_me_in_del_ = True
 before_external_call._transaction_break_ = True
 
@@ -104,7 +104,7 @@
 def after_external_call():
     if we_are_translated():
         # starts a new transaction if we are not atomic already
-        llop.stm_start_inevitable_transaction(lltype.Void)
+        llop.stm_start_inevitable_if_not_atomic(lltype.Void)
 after_external_call._dont_reach_me_in_del_ = True
 after_external_call._transaction_break_ = True
 
diff --git a/rpython/rtyper/lltypesystem/lloperation.py 
b/rpython/rtyper/lltypesystem/lloperation.py
--- a/rpython/rtyper/lltypesystem/lloperation.py
+++ b/rpython/rtyper/lltypesystem/lloperation.py
@@ -438,6 +438,7 @@
                                                       # see threadlocalref.py
     'stm_threadlocal_get':    LLOp(sideeffects=False),
     'stm_threadlocal_set':    LLOp(),
+    'stm_perform_transaction':LLOp(canmallocgc=True),
 
 ##    'stm_allocate_nonmovable_int_adr': LLOp(sideeffects=False, 
canmallocgc=True),
 ##    'stm_become_inevitable':  LLOp(canmallocgc=True),
diff --git a/rpython/translator/c/genc.py b/rpython/translator/c/genc.py
--- a/rpython/translator/c/genc.py
+++ b/rpython/translator/c/genc.py
@@ -786,7 +786,8 @@
             if node.forward_decl:
                 print >> f, node.forward_decl
         elif node.name is not None:
-            print >> f, '%s %s;' % (node.typetag, node.name)
+            if node.typetag != '':
+                print >> f, '%s %s;' % (node.typetag, node.name)
     print >> f
     for node in structdeflist:
         for line in node.definition():
diff --git a/rpython/translator/stm/funcgen.py 
b/rpython/translator/stm/funcgen.py
--- a/rpython/translator/stm/funcgen.py
+++ b/rpython/translator/stm/funcgen.py
@@ -134,13 +134,11 @@
         return '/* %s = */ STM_POP_ROOT_RET(stm_thread_local);' % (arg0,)
     return 'STM_POP_ROOT(stm_thread_local, %s);' % (arg0,)
 
-def stm_commit_transaction(funcgen, op):
-   return '{ int e = errno; stm_commit_transaction(); errno = e; }'
+def stm_commit_if_not_atomic(funcgen, op):
+   return 'pypy_stm_commit_if_not_atomic();'
 
-def stm_start_inevitable_transaction(funcgen, op):
-    return ('{ int e = errno; '
-            'stm_start_inevitable_transaction(&stm_thread_local); '
-            'errno = e; }')
+def stm_start_inevitable_if_not_atomic(funcgen, op):
+    return 'pypy_stm_start_inevitable_if_not_atomic();'
 
 def stm_enter_callback_call(funcgen, op):
     result = funcgen.expr(op.result)
@@ -167,6 +165,12 @@
     arg0 = funcgen.expr(op.args[0])
     return 'stm_thread_local.thread_local_obj = (object_t *)%s;' % (arg0,)
 
+def stm_perform_transaction(funcgen, op):
+    arg0 = funcgen.expr(op.args[0])
+    arg1 = funcgen.expr(op.args[1])
+    return ('pypy_stm_perform_transaction((object_t *)%s, '
+            '(int(*)(object_t *, int))%s);' % (arg0, arg1))
+
 
 ##def stm_initialize(funcgen, op):
 ##    return '''stm_initialize();
@@ -297,16 +301,6 @@
 ##    result = funcgen.expr(op.result)
 ##    return '%s = ((struct rpyobj_s*)%s)->tid;' % (result, arg0)
 
-##def stm_hash(funcgen, op):
-##    arg0 = funcgen.expr(op.args[0])
-##    result = funcgen.expr(op.result)
-##    return '%s = stm_hash((gcptr)%s);' % (result, arg0)
-
-##def stm_id(funcgen, op):
-##    arg0 = funcgen.expr(op.args[0])
-##    result = funcgen.expr(op.result)
-##    return '%s = stm_id((gcptr)%s);' % (result, arg0)
-
 ##def stm_change_atomic(funcgen, op):
 ##    arg0 = funcgen.expr(op.args[0])
 ##    return 'stm_atomic(%s);' % (arg0,)
@@ -315,20 +309,6 @@
 ##    result = funcgen.expr(op.result)
 ##    return '%s = stm_atomic(0);' % (result,)
 
-##def stm_threadlocal_get(funcgen, op):
-##    result = funcgen.expr(op.result)
-##    return '%s = (%s)stm_thread_local_obj;' % (
-##        result, cdecl(funcgen.lltypename(op.result), ''))
-
-##def stm_threadlocal_set(funcgen, op):
-##    arg0 = funcgen.expr(op.args[0])
-##    return 'stm_thread_local_obj = (gcptr)%s;' % (arg0,)
-
-##def stm_perform_transaction(funcgen, op):
-##    arg0 = funcgen.expr(op.args[0])
-##    arg1 = funcgen.expr(op.args[1])
-##    return 'stm_perform_transaction((gcptr)%s, %s);' % (arg0, arg1)
-
 ##def stm_enter_callback_call(funcgen, op):
 ##    result = funcgen.expr(op.result)
 ##    return '%s = stm_enter_callback_call();' % (result,)
diff --git a/rpython/translator/stm/src_stm/revision 
b/rpython/translator/stm/src_stm/revision
--- a/rpython/translator/stm/src_stm/revision
+++ b/rpython/translator/stm/src_stm/revision
@@ -1,1 +1,1 @@
-3ce4c20d80e7
+3f0d8773b90b
diff --git a/rpython/translator/stm/src_stm/stm/core.c 
b/rpython/translator/stm/src_stm/stm/core.c
--- a/rpython/translator/stm/src_stm/stm/core.c
+++ b/rpython/translator/stm/src_stm/stm/core.c
@@ -138,6 +138,8 @@
 
 void _stm_start_transaction(stm_thread_local_t *tl, stm_jmpbuf_t *jmpbuf)
 {
+    assert(!_stm_in_transaction(tl));
+
     s_mutex_lock();
 
   retry:
@@ -467,7 +469,7 @@
     struct stm_priv_segment_info_s *pseg = get_priv_segment(segment_num);
 
     /* throw away the content of the nursery */
-    throw_away_nursery(pseg);
+    long bytes_in_nursery = throw_away_nursery(pseg);
 
     /* reset all the modified objects (incl. re-adding GCFLAG_WRITE_BARRIER) */
     reset_modified_from_other_segments(segment_num);
@@ -477,6 +479,7 @@
     stm_thread_local_t *tl = pseg->pub.running_thread;
     tl->shadowstack = pseg->shadowstack_at_start_of_transaction;
     tl->thread_local_obj = pseg->threadlocal_at_start_of_transaction;
+    tl->last_abort__bytes_in_nursery = bytes_in_nursery;
 
     /* reset these lists to NULL too on abort */
     LIST_FREE(pseg->objects_pointing_to_nursery);
diff --git a/rpython/translator/stm/src_stm/stm/fprintcolor.c 
b/rpython/translator/stm/src_stm/stm/fprintcolor.c
--- a/rpython/translator/stm/src_stm/stm/fprintcolor.c
+++ b/rpython/translator/stm/src_stm/stm/fprintcolor.c
@@ -4,11 +4,6 @@
 /* ------------------------------------------------------------ */
 
 
-static int dprintfcolor(void)
-{
-    return 31 + STM_SEGMENT->segment_num % 6;
-}
-
 static int threadcolor_printf(const char *format, ...)
 {
     char buffer[2048];
diff --git a/rpython/translator/stm/src_stm/stm/fprintcolor.h 
b/rpython/translator/stm/src_stm/stm/fprintcolor.h
--- a/rpython/translator/stm/src_stm/stm/fprintcolor.h
+++ b/rpython/translator/stm/src_stm/stm/fprintcolor.h
@@ -8,7 +8,10 @@
 
 
 #define dprintf(args)   threadcolor_printf args
-static int dprintfcolor(void);
+static inline int dprintfcolor(void)
+{
+    return 31 + STM_SEGMENT->segment_num % 6;
+}
 
 static int threadcolor_printf(const char *format, ...)
      __attribute__((format (printf, 1, 2)));
diff --git a/rpython/translator/stm/src_stm/stm/nursery.c 
b/rpython/translator/stm/src_stm/stm/nursery.c
--- a/rpython/translator/stm/src_stm/stm/nursery.c
+++ b/rpython/translator/stm/src_stm/stm/nursery.c
@@ -216,15 +216,15 @@
                    _collect_now(item));
 }
 
-static void throw_away_nursery(struct stm_priv_segment_info_s *pseg)
+static size_t throw_away_nursery(struct stm_priv_segment_info_s *pseg)
 {
     /* reset the nursery by zeroing it */
-    size_t size;
+    size_t nursery_used;
     char *realnursery;
 
     realnursery = REAL_ADDRESS(pseg->pub.segment_base, _stm_nursery_start);
-    size = pseg->pub.nursery_current - (stm_char *)_stm_nursery_start;
-    memset(realnursery, 0, size);
+    nursery_used = pseg->pub.nursery_current - (stm_char *)_stm_nursery_start;
+    memset(realnursery, 0, nursery_used);
 
     pseg->pub.nursery_current = (stm_char *)_stm_nursery_start;
 
@@ -251,6 +251,7 @@
     }
 
     tree_clear(pseg->nursery_objects_shadows);
+    return nursery_used;
 }
 
 #define MINOR_NOTHING_TO_DO(pseg)                                       \
diff --git a/rpython/translator/stm/src_stm/stm/nursery.h 
b/rpython/translator/stm/src_stm/stm/nursery.h
--- a/rpython/translator/stm/src_stm/stm/nursery.h
+++ b/rpython/translator/stm/src_stm/stm/nursery.h
@@ -12,7 +12,7 @@
 
 static void minor_collection(bool commit);
 static void check_nursery_at_transaction_start(void);
-static void throw_away_nursery(struct stm_priv_segment_info_s *pseg);
+static size_t throw_away_nursery(struct stm_priv_segment_info_s *pseg);
 static void major_do_minor_collections(void);
 
 static inline bool must_abort(void) {
diff --git a/rpython/translator/stm/src_stm/stmgc.h 
b/rpython/translator/stm/src_stm/stmgc.h
--- a/rpython/translator/stm/src_stm/stmgc.h
+++ b/rpython/translator/stm/src_stm/stmgc.h
@@ -58,7 +58,10 @@
        the following raw region of memory is cleared. */
     char *mem_clear_on_abort;
     size_t mem_bytes_to_clear_on_abort;
-    /* the next fields are handled automatically by the library */
+    /* after an abort, some details about the abort are stored there.
+       (these fields are not modified on a successful commit) */
+    long last_abort__bytes_in_nursery;
+    /* the next fields are handled internally by the library */
     int associated_segment_num;
     struct stm_thread_local_s *prev, *next;
 } stm_thread_local_t;
@@ -246,6 +249,9 @@
     if (STM_SEGMENT->jmpbuf_ptr != NULL)
         _stm_become_inevitable(msg);
 }
+static inline int stm_is_inevitable(void) {
+    return (STM_SEGMENT->jmpbuf_ptr == NULL);
+}
 
 /* Forces a safe-point if needed.  Normally not needed: this is
    automatic if you call stm_allocate(). */
diff --git a/rpython/translator/stm/src_stm/stmgcintf.c 
b/rpython/translator/stm/src_stm/stmgcintf.c
--- a/rpython/translator/stm/src_stm/stmgcintf.c
+++ b/rpython/translator/stm/src_stm/stmgcintf.c
@@ -4,6 +4,10 @@
 
 __thread struct stm_thread_local_s stm_thread_local;
 
+/* 0 = not initialized; 1 = normal mode; 2 or more = atomic mode */
+__thread long pypy_stm_ready_atomic;
+__thread uintptr_t pypy_stm_nursery_low_fill_mark;
+
 extern Signed pypy_stmcb_size_rounded_up(void*);
 extern void pypy_stmcb_trace(void*, void(*)(void*));
 
@@ -28,7 +32,7 @@
 
 #define LOW_FILL_MARK   400000
 
-stm_char *pypy_stm_nursery_low_fill_mark;
+static long pypy_transaction_length;
 
 
 void pypy_stm_set_transaction_length(long percentage)
@@ -38,36 +42,124 @@
     long low_fill_mark = LOW_FILL_MARK * percentage / 100;
     if (low_fill_mark > NURSERY_SIZE / 2)
         low_fill_mark = NURSERY_SIZE / 2;
-    pypy_stm_nursery_low_fill_mark = ((stm_char *)_stm_nursery_start) +
-                                     low_fill_mark;
+    pypy_transaction_length = low_fill_mark;
 }
 
 void pypy_stm_setup(void)
 {
     stm_setup();
     stm_register_thread_local(&stm_thread_local);
+    pypy_stm_ready_atomic = 1;
     pypy_stm_set_transaction_length(100);
-    stm_start_inevitable_transaction(&stm_thread_local);
+    pypy_stm_start_inevitable_if_not_atomic();
 }
 
 long pypy_stm_enter_callback_call(void)
 {
-    long token = 0;
-
-    if (stm_thread_local.shadowstack == NULL) {
+    if (pypy_stm_ready_atomic == 0) {
         /* first time we see this thread */
-        token = 1;
+        int e = errno;
         stm_register_thread_local(&stm_thread_local);
+        errno = e;
+        pypy_stm_ready_atomic = 1;
+        pypy_stm_start_inevitable_if_not_atomic();
+        return 1;
     }
-    stm_start_inevitable_transaction(&stm_thread_local);
-    return token;
+    else {
+        /* callback from C code, itself called from Python code */
+        pypy_stm_start_inevitable_if_not_atomic();
+        return 0;
+    }
 }
 
 void pypy_stm_leave_callback_call(long token)
 {
-    stm_commit_transaction();
     if (token == 1) {
+        /* if we're returning into foreign C code that was not itself
+           called from Python code, then we're ignoring the atomic
+           status and committing anyway. */
+        int e = errno;
+        pypy_stm_ready_atomic = 1;
+        stm_commit_transaction();
+        pypy_stm_ready_atomic = 0;
         stm_unregister_thread_local(&stm_thread_local);
-        assert(stm_thread_local.shadowstack == NULL);
+        errno = e;
+    }
+    else {
+        pypy_stm_commit_if_not_atomic();
     }
 }
+
+void pypy_stm_perform_transaction(object_t *arg, int callback(object_t *, int))
+{   /* must save roots around this call */
+    stm_jmpbuf_t jmpbuf;
+    long volatile v_counter = 0;
+#ifndef NDEBUG
+    object_t **volatile old_shadowstack = stm_thread_local.shadowstack;
+#endif
+
+    STM_PUSH_ROOT(stm_thread_local, arg);
+    /*STM_PUSH_ROOT(END_MARKER_OFF); XXX redo this optimization */
+
+    while (1) {
+
+        if (pypy_stm_ready_atomic == 1) {
+            stm_commit_transaction();
+            STM_START_TRANSACTION(&stm_thread_local, jmpbuf);
+        }
+
+        /* After setjmp(), the local variables v_* are preserved because they
+         * are volatile.  The other variables are only declared here. */
+        long counter, result;
+        counter = v_counter;
+        v_counter = counter + 1;
+
+        /* If counter==0, initialize 'pypy_stm_nursery_low_fill_mark'
+           from the configured length limit.  If counter>0, we did an
+           abort, and we can now configure 'pypy_stm_nursery_low_fill_mark'
+           to a value slightly smaller than the value at last abort.
+        */
+        if (stm_is_inevitable()) {
+            pypy_stm_nursery_low_fill_mark = 0;
+        }
+        else {
+            long limit;
+            if (counter == 0) {
+                limit = pypy_transaction_length;
+            }
+            else {
+                limit = stm_thread_local.last_abort__bytes_in_nursery;
+                limit -= (limit >> 4);
+            }
+            pypy_stm_nursery_low_fill_mark = _stm_nursery_start + limit;
+        }
+
+        /* invoke the callback in the new transaction */
+        STM_POP_ROOT(stm_thread_local, arg);
+        assert(old_shadowstack == stm_thread_local.shadowstack);
+        STM_PUSH_ROOT(stm_thread_local, arg);
+        result = callback(arg, counter);
+        if (result <= 0)
+            break;
+        v_counter = 0;
+    }
+
+    if (STM_SEGMENT->jmpbuf_ptr == &jmpbuf) {
+        /* we can't leave this function leaving a non-inevitable
+           transaction whose jmpbuf points into this function
+        */
+        if (pypy_stm_ready_atomic == 1) {
+            stm_commit_transaction();
+            stm_start_inevitable_transaction(&stm_thread_local);
+            pypy_stm_nursery_low_fill_mark = 0;
+        }
+        else {
+            _stm_become_inevitable("perform_transaction left with atomic");
+        }
+    }
+
+    //gcptr x = stm_pop_root();   /* pop the END_MARKER */
+    //assert(x == END_MARKER_OFF || x == END_MARKER_ON);
+    STM_POP_ROOT_RET(stm_thread_local);             /* pop the 'arg' */
+    assert(old_shadowstack == stm_thread_local.shadowstack);
+}
diff --git a/rpython/translator/stm/src_stm/stmgcintf.h 
b/rpython/translator/stm/src_stm/stmgcintf.h
--- a/rpython/translator/stm/src_stm/stmgcintf.h
+++ b/rpython/translator/stm/src_stm/stmgcintf.h
@@ -4,24 +4,55 @@
 
 /* meant to be #included after src_stm/stmgc.h */
 
+#include <errno.h>
 #include "stmgc.h"
 #include "stm/atomic.h"    /* for spin_loop() and write_fence() */
 
 extern __thread struct stm_thread_local_s stm_thread_local;
-extern stm_char *pypy_stm_nursery_low_fill_mark;
+extern __thread long pypy_stm_ready_atomic;
+extern __thread uintptr_t pypy_stm_nursery_low_fill_mark;
 
 void pypy_stm_setup(void);
 void pypy_stm_setup_prebuilt(void);   /* generated into stm_prebuilt.c */
+
+static inline void pypy_stm_commit_if_not_atomic(void) {
+    if (pypy_stm_ready_atomic == 1) {
+        int e = errno;
+        stm_commit_transaction();
+        errno = e;
+    }
+}
+static inline void pypy_stm_start_inevitable_if_not_atomic(void) {
+    if (pypy_stm_ready_atomic == 1) {
+        int e = errno;
+        stm_start_inevitable_transaction(&stm_thread_local);
+        pypy_stm_nursery_low_fill_mark = 0;
+        errno = e;
+    }
+}
+static inline void pypy_stm_increment_atomic(void) {
+    pypy_stm_ready_atomic++;
+}
+static inline void pypy_stm_decrement_atomic(void) {
+    if (--pypy_stm_ready_atomic == 0)
+        pypy_stm_ready_atomic = 1;
+}
+static inline long pypy_stm_get_atomic(void) {
+    return pypy_stm_ready_atomic - 1;
+}
 long pypy_stm_enter_callback_call(void);
 void pypy_stm_leave_callback_call(long);
 void pypy_stm_set_transaction_length(long);
+void pypy_stm_perform_transaction(object_t *, int(object_t *, int));
 
 static inline int pypy_stm_should_break_transaction(void)
 {
     /* we should break the current transaction if we have used more than
-       some initial portion of the nursery, or if we are running inevitable */
-    return (STM_SEGMENT->nursery_current >= pypy_stm_nursery_low_fill_mark ||
-            STM_SEGMENT->jmpbuf_ptr == NULL);
+       some initial portion of the nursery, or if we are running inevitable
+       (in which case pypy_stm_nursery_low_fill_mark is set to 0)
+    */
+    uintptr_t current = (uintptr_t)STM_SEGMENT->nursery_current;
+    return current >= pypy_stm_nursery_low_fill_mark;
 }
 
 
_______________________________________________
pypy-commit mailing list
pypy-commit@python.org
https://mail.python.org/mailman/listinfo/pypy-commit

Reply via email to