Author: Remi Meier <[email protected]>
Branch: c7
Changeset: r612:53b7a774625c
Date: 2014-01-15 11:56 +0100
http://bitbucket.org/pypy/stmgc/changeset/53b7a774625c/

Log:    try introducing safe-points

diff --git a/c7/core.c b/c7/core.c
--- a/c7/core.c
+++ b/c7/core.c
@@ -8,6 +8,7 @@
 #include <sys/syscall.h>
 #include <asm/prctl.h>
 #include <sys/prctl.h>
+#include <pthread.h>
 
 #include "core.h"
 #include "list.h"
@@ -49,6 +50,7 @@
     struct _thread_local1_s _tl1;
     int thread_num;
     bool running_transaction;
+    bool need_abort;
     char *thread_base;
     struct stm_list_s *modified_objects;
     struct stm_list_s *new_object_ranges;
@@ -69,6 +71,7 @@
 
 /************************************************************/
 uintptr_t _stm_reserve_page(void);
+void stm_abort_transaction(void);
 
 static void spin_loop(void)
 {
@@ -115,6 +118,59 @@
 #endif
 }
 
+/************************************************************/
+
+/* a multi-reader, single-writer lock: transactions normally take a reader
+   lock, so don't conflict with each other; when we need to do a global GC,
+   we take a writer lock to "stop the world".  Note the initializer here,
+   which should give the correct priority for stm_possible_safe_point(). */
+static pthread_rwlock_t rwlock_shared =
+    PTHREAD_RWLOCK_WRITER_NONRECURSIVE_INITIALIZER_NP;
+
+struct tx_descriptor *in_single_thread = NULL;
+
+void stm_start_sharedlock(void)
+{
+    int err = pthread_rwlock_rdlock(&rwlock_shared);
+    if (err != 0)
+        abort();
+}
+
+void stm_stop_sharedlock(void)
+{
+    int err = pthread_rwlock_unlock(&rwlock_shared);
+    if (err != 0)
+        abort();
+}
+
+static void start_exclusivelock(void)
+{
+    int err = pthread_rwlock_wrlock(&rwlock_shared);
+    if (err != 0)
+        abort();
+}
+
+static void stop_exclusivelock(void)
+{
+    int err = pthread_rwlock_unlock(&rwlock_shared);
+    if (err != 0)
+        abort();
+}
+
+void _stm_start_safe_point(void)
+{
+    stm_stop_sharedlock();
+}
+
+void _stm_stop_safe_point(void)
+{
+    stm_start_sharedlock();
+    if (_STM_TL2->need_abort)
+        stm_abort_transaction();
+}
+
+
+
 bool _stm_was_read(object_t *obj)
 {
     read_marker_t *marker = (read_marker_t *)(((uintptr_t)obj) >> 4);
@@ -227,9 +283,8 @@
     return (object_t*)res;
 }
 
-void stm_abort_transaction(void);
 
-enum detect_conflicts_e { CANNOT_CONFLICT, CAN_CONFLICT };
+enum detect_conflicts_e { CANNOT_CONFLICT, CAN_CONFLICT, CHECK_CONFLICT };
 
 static void update_to_current_version(enum detect_conflicts_e check_conflict)
 {
@@ -265,8 +320,12 @@
     write_fence();
     pending_updates = NULL;
 
-    if (conflict_found_or_dont_check && check_conflict == CAN_CONFLICT) {
-        stm_abort_transaction();
+    if (conflict_found_or_dont_check) {
+        if (check_conflict == CAN_CONFLICT) {
+            stm_abort_transaction();
+        } else {                  /* CHECK_CONFLICT */
+            _STM_TL2->need_abort = 1;
+        }
     }
 }
 
@@ -532,6 +591,8 @@
 {
     assert(!_STM_TL2->running_transaction);
 
+    stm_start_sharedlock();
+    
     uint8_t old_rv = _STM_TL1->transaction_read_version;
     _STM_TL1->transaction_read_version = old_rv + 1;
     if (UNLIKELY(old_rv == 0xff))
@@ -557,6 +618,7 @@
 
     _STM_TL1->jmpbufptr = jmpbufptr;
     _STM_TL2->running_transaction = 1;
+    _STM_TL2->need_abort = 0;
 }
 
 #if 0
@@ -580,100 +642,87 @@
 void stm_stop_transaction(void)
 {
     assert(_STM_TL2->running_transaction);
-#if 0
+    stm_stop_sharedlock();
+    start_exclusivelock();
 
-    write_fence();   /* see later in this function for why */
+    _STM_TL1->jmpbufptr = NULL;          /* cannot abort any more */
+    
+    /* copy modified object versions to other threads */
+    pending_updates = _STM_TL2->modified_objects;
+    int my_thread_num = _STM_TL2->thread_num;
+    int other_thread_num = 1 - my_thread_num;
+    _stm_restore_local_state(other_thread_num);
+    update_to_current_version(CHECK_CONFLICT); /* sets need_abort */
+    _stm_restore_local_state(my_thread_num);
+    
+    
+    /* /\* walk the new_object_ranges and manually copy the new objects */
+    /*    to the other thread's pages in the (hopefully rare) case that */
+    /*    the page they belong to is already unshared *\/ */
+    /* long i; */
+    /* struct stm_list_s *lst = _STM_TL2->new_object_ranges; */
+    /* for (i = stm_list_count(lst); i > 0; ) { */
+    /*     i -= 2; */
+    /*     uintptr_t pagenum = (uintptr_t)stm_list_item(lst, i); */
 
+    /*     /\* NB. the read next line should work even against a parallel */
+    /*        thread, thanks to the lock acquisition we do earlier (see the */
+    /*        beginning of this function).  Indeed, if this read returns */
+    /*        SHARED_PAGE, then we know that the real value in memory was */
+    /*        actually SHARED_PAGE at least at the time of the */
+    /*        acquire_lock().  It may have been modified afterwards by a */
+    /*        compare_and_swap() in the other thread, but then we know for */
+    /*        sure that the other thread is seeing the last, up-to-date */
+    /*        version of our data --- this is the reason of the */
+    /*        write_fence() just before the acquire_lock(). */
+    /*     *\/ */
+    /*     if (flag_page_private[pagenum] != SHARED_PAGE) { */
+    /*         object_t *range = stm_list_item(lst, i + 1); */
+    /*         uint16_t start, stop; */
+    /*         FROM_RANGE(start, stop, range); */
+    /*         update_new_objects_in_other_threads(pagenum, start, stop); */
+    /*     } */
+    /* } */
+    
+    /* /\* do the same for the partially-allocated pages *\/ */
+    /* long j; */
+    /* for (j = 2; j < LARGE_OBJECT_WORDS; j++) { */
+    /*     alloc_for_size_t *alloc = &_STM_TL2->alloc[j]; */
+    /*     uint16_t start = alloc->start; */
+    /*     uint16_t cur = (uintptr_t)alloc->next; */
 
-    if (leader_thread_num != _STM_TL2->thread_num) {
-        /* non-leader thread */
-        if (global_history != NULL) {
-            update_to_current_version(CAN_CONFLICT);
-            assert(global_history == NULL);
-        }
+    /*     if (start == cur) { */
+    /*         /\* nothing to do: this page (or fraction thereof) was left */
+    /*            empty by the previous transaction, and starts empty as */
+    /*            well in the new transaction.  'flag_partial_page' is */
+    /*            unchanged. *\/ */
+    /*     } */
+    /*     else { */
+    /*         uintptr_t pagenum = ((uintptr_t)(alloc->next - 1)) / 4096UL; */
+    /*         /\* for the new transaction, it will start here: *\/ */
+    /*         alloc->start = cur; */
 
-        /* steal leadership now */
-        leader_thread_num = _STM_TL2->thread_num;
-    }
+    /*         if (alloc->flag_partial_page) { */
+    /*             if (flag_page_private[pagenum] != SHARED_PAGE) { */
+    /*                 update_new_objects_in_other_threads(pagenum, start, 
cur); */
+    /*             } */
+    /*         } */
+    /*         else { */
+    /*             /\* we can skip checking flag_page_private[] in non-debug */
+    /*                builds, because the whole page can only contain */
+    /*                objects made by the just-finished transaction. *\/ */
+    /*             assert(flag_page_private[pagenum] == SHARED_PAGE); */
 
-    /* now we are the leader thread.  the leader can always commit */
-    _STM_TL1->jmpbufptr = NULL;          /* cannot abort any more */
-    undo_log_current = undo_log_pages;   /* throw away the content */
+    /*             /\* the next transaction will start with this page */
+    /*                containing objects that are now committed, so */
+    /*                we need to set this flag now *\/ */
+    /*             alloc->flag_partial_page = true; */
+    /*         } */
+    /*     } */
+    /* } */
 
-    /* add these objects to the global_history */
-    _STM_TL2->modified_objects->nextlist = global_history;
-    global_history = _STM_TL2->modified_objects;
-    _STM_TL2->modified_objects = stm_list_create();
-
-    uint16_t wv = _STM_TL1->transaction_write_version;
-    if (gh_write_version_first < wv) gh_write_version_first = wv;
-
-    /* walk the new_object_ranges and manually copy the new objects
-       to the other thread's pages in the (hopefully rare) case that
-       the page they belong to is already unshared */
-    long i;
-    struct stm_list_s *lst = _STM_TL2->new_object_ranges;
-    for (i = stm_list_count(lst); i > 0; ) {
-        i -= 2;
-        uintptr_t pagenum = (uintptr_t)stm_list_item(lst, i);
-
-        /* NB. the read next line should work even against a parallel
-           thread, thanks to the lock acquisition we do earlier (see the
-           beginning of this function).  Indeed, if this read returns
-           SHARED_PAGE, then we know that the real value in memory was
-           actually SHARED_PAGE at least at the time of the
-           acquire_lock().  It may have been modified afterwards by a
-           compare_and_swap() in the other thread, but then we know for
-           sure that the other thread is seeing the last, up-to-date
-           version of our data --- this is the reason of the
-           write_fence() just before the acquire_lock().
-        */
-        if (flag_page_private[pagenum] != SHARED_PAGE) {
-            object_t *range = stm_list_item(lst, i + 1);
-            uint16_t start, stop;
-            FROM_RANGE(start, stop, range);
-            update_new_objects_in_other_threads(pagenum, start, stop);
-        }
-    }
-
-    /* do the same for the partially-allocated pages */
-    long j;
-    for (j = 2; j < LARGE_OBJECT_WORDS; j++) {
-        alloc_for_size_t *alloc = &_STM_TL2->alloc[j];
-        uint16_t start = alloc->start;
-        uint16_t cur = (uintptr_t)alloc->next;
-
-        if (start == cur) {
-            /* nothing to do: this page (or fraction thereof) was left
-               empty by the previous transaction, and starts empty as
-               well in the new transaction.  'flag_partial_page' is
-               unchanged. */
-        }
-        else {
-            uintptr_t pagenum = ((uintptr_t)(alloc->next - 1)) / 4096UL;
-            /* for the new transaction, it will start here: */
-            alloc->start = cur;
-
-            if (alloc->flag_partial_page) {
-                if (flag_page_private[pagenum] != SHARED_PAGE) {
-                    update_new_objects_in_other_threads(pagenum, start, cur);
-                }
-            }
-            else {
-                /* we can skip checking flag_page_private[] in non-debug
-                   builds, because the whole page can only contain
-                   objects made by the just-finished transaction. */
-                assert(flag_page_private[pagenum] == SHARED_PAGE);
-
-                /* the next transaction will start with this page
-                   containing objects that are now committed, so
-                   we need to set this flag now */
-                alloc->flag_partial_page = true;
-            }
-        }
-    }
-#endif
     _STM_TL2->running_transaction = 0;
+    stop_exclusivelock();
 }
 
 void stm_abort_transaction(void)
@@ -686,10 +735,11 @@
         uint16_t num_allocated = ((uintptr_t)alloc->next) - alloc->start;
         alloc->next -= num_allocated;
     }
-    stm_list_clear(_STM_TL2->new_object_ranges);
+    /* stm_list_clear(_STM_TL2->new_object_ranges); */
     stm_list_clear(_STM_TL2->modified_objects);
     assert(_STM_TL1->jmpbufptr != NULL);
     assert(_STM_TL1->jmpbufptr != (jmpbufptr_t *)-1);   /* for tests only */
     _STM_TL2->running_transaction = 0;
+    stm_stop_sharedlock();
     __builtin_longjmp(*_STM_TL1->jmpbufptr, 1);
 }
diff --git a/c7/core.h b/c7/core.h
--- a/c7/core.h
+++ b/c7/core.h
@@ -98,4 +98,10 @@
 
 bool _stm_is_in_nursery(char *ptr);
 object_t *_stm_allocate_old(size_t size);
+
+void _stm_start_safe_point(void);
+void _stm_stop_safe_point(void);
 #endif
+
+
+
diff --git a/c7/test/support.py b/c7/test/support.py
--- a/c7/test/support.py
+++ b/c7/test/support.py
@@ -50,6 +50,10 @@
 bool _stm_is_in_nursery(char *ptr);
 object_t *_stm_allocate_old(size_t size);
 
+void _stm_start_safe_point(void);
+void _stm_stop_safe_point(void);
+bool _stm_check_stop_safe_point(void);
+
 void *memset(void *s, int c, size_t n);
 """)
 
@@ -65,7 +69,7 @@
 
 bool _stm_stop_transaction(void) {
     jmpbufptr_t here;
-    if (__builtin_setjmp(here) == 0) {
+    if (__builtin_setjmp(here) == 0) { // returned directly
          assert(_STM_TL1->jmpbufptr == (jmpbufptr_t*)-1);
          _STM_TL1->jmpbufptr = &here;
          stm_stop_transaction();
@@ -76,6 +80,20 @@
     return 1;
 }
 
+bool _stm_check_stop_safe_point(void) {
+    jmpbufptr_t here;
+    if (__builtin_setjmp(here) == 0) { // returned directly
+         assert(_STM_TL1->jmpbufptr == (jmpbufptr_t*)-1);
+         _STM_TL1->jmpbufptr = &here;
+         _stm_stop_safe_point();
+         _STM_TL1->jmpbufptr = (jmpbufptr_t*)-1;
+         return 0;
+    }
+    _STM_TL1->jmpbufptr = (jmpbufptr_t*)-1;
+    return 1;
+}
+
+
 ''', sources=source_files,
      define_macros=[('STM_TESTS', '1')],
      undef_macros=['NDEBUG'],
@@ -123,6 +141,16 @@
     else:
         assert res == 0
 
+def stm_start_safe_point():
+    lib._stm_start_safe_point()
+
+def stm_stop_safe_point(expected_conflict=False):
+    res = lib._stm_check_stop_safe_point()
+    if expected_conflict:
+        assert res == 1
+    else:
+        assert res == 0
+
 
 class BaseTest(object):
 
diff --git a/c7/test/test_basic.py b/c7/test/test_basic.py
--- a/c7/test/test_basic.py
+++ b/c7/test/test_basic.py
@@ -25,10 +25,12 @@
 
     def test_transaction_start_stop(self):
         stm_start_transaction()
+        stm_start_safe_point()
         self.switch(1)
         stm_start_transaction()
         stm_stop_transaction()
         self.switch(0)
+        stm_stop_safe_point()
         stm_stop_transaction()
 
     def test_simple_read(self):
@@ -54,52 +56,85 @@
         lp1, p1 = stm_allocate_old(16)
         stm_start_transaction()
         stm_write(lp1)
+        assert stm_was_written(lp1)
         p1[15] = 'a'
         self.switch(1)
         stm_start_transaction()
         stm_read(lp1)
+        assert stm_was_read(lp1)
         tp1 = stm_get_real_address(lp1)
         assert tp1[15] == '\0'
         
+    def test_read_write_1(self):
+        lp1, p1 = stm_allocate_old(16)
+        p1[8] = 'a'
+        stm_start_transaction()
+        stm_stop_transaction()
+        #
+        self.switch(1)
+        stm_start_transaction()
+        stm_write(lp1)
+        p1 = stm_get_real_address(lp1)
+        assert p1[8] == 'a'
+        p1[8] = 'b'
+        stm_start_safe_point()
+        #
+        self.switch(0)
+        stm_start_transaction()
+        stm_read(lp1)
+        p1 = stm_get_real_address(lp1)
+        assert p1[8] == 'a'
+        stm_start_safe_point()
+        #
+        self.switch(1)
+        stm_stop_safe_point()
+        stm_stop_transaction(False)
+        #
+        self.switch(0)
+        stm_stop_safe_point(True) # detects rw conflict
         
-
-    def test_read_write_1(self):
+        
+    def test_read_write_2(self):
         stm_start_transaction()
-        p1 = stm_allocate(16)
+        lp1, p1 = stm_allocate(16)
         p1[8] = 'a'
         stm_stop_transaction(False)
         #
-        self.switch("sub1")
+        self.switch(1)
         stm_start_transaction()
-        stm_write(p1)
+        stm_write(lp1)
+        p1 = stm_get_real_address(lp1)
         assert p1[8] == 'a'
         p1[8] = 'b'
         #
-        self.switch("main")
+        self.switch(0)
         stm_start_transaction()
-        stm_read(p1)
+        stm_read(lp1)
+        p1 = stm_get_real_address(lp1)
         assert p1[8] == 'a'
         #
-        self.switch("sub1")
+        self.switch(1)
         stm_stop_transaction(False)
         #
-        self.switch("main")
+        self.switch(0)
+        p1 = stm_get_real_address(lp1)
         assert p1[8] == 'a'
 
+        
     def test_start_transaction_updates(self):
         stm_start_transaction()
         p1 = stm_allocate(16)
         p1[8] = 'a'
         stm_stop_transaction(False)
         #
-        self.switch("sub1")
+        self.switch(1)
         stm_start_transaction()
         stm_write(p1)
         assert p1[8] == 'a'
         p1[8] = 'b'
         stm_stop_transaction(False)
         #
-        self.switch("main")
+        self.switch(0)
         assert p1[8] == 'a'
         stm_start_transaction()
         assert p1[8] == 'b'
@@ -107,11 +142,11 @@
     def test_resolve_no_conflict_empty(self):
         stm_start_transaction()
         #
-        self.switch("sub1")
+        self.switch(1)
         stm_start_transaction()
         stm_stop_transaction(False)
         #
-        self.switch("main")
+        self.switch(0)
         stm_stop_transaction(False)
 
     def test_resolve_no_conflict_write_only_in_already_committed(self):
@@ -121,13 +156,13 @@
         stm_stop_transaction(False)
         stm_start_transaction()
         #
-        self.switch("sub1")
+        self.switch(1)
         stm_start_transaction()
         stm_write(p1)
         p1[8] = 'b'
         stm_stop_transaction(False)
         #
-        self.switch("main")
+        self.switch(0)
         assert p1[8] == 'a'
         stm_stop_transaction(False)
         assert p1[8] == 'b'
@@ -139,13 +174,13 @@
         stm_stop_transaction(False)
         stm_start_transaction()
         #
-        self.switch("sub1")
+        self.switch(1)
         stm_start_transaction()
         stm_write(p1)
         p1[8] = 'b'
         stm_stop_transaction(False)
         #
-        self.switch("main")
+        self.switch(0)
         stm_read(p1)
         assert p1[8] == 'a'
         stm_stop_transaction(expected_conflict=True)
@@ -160,13 +195,13 @@
         stm_stop_transaction(False)
         stm_start_transaction()
         #
-        self.switch("sub1")
+        self.switch(1)
         stm_start_transaction()
         stm_write(p1)
         p1[8] = 'b'
         stm_stop_transaction(False)
         #
-        self.switch("main")
+        self.switch(0)
         assert p1[8] == 'a'
         stm_write(p1)
         p1[8] = 'c'
@@ -184,13 +219,13 @@
         stm_stop_transaction(False)
         stm_start_transaction()
         #
-        self.switch("sub1")
+        self.switch(1)
         stm_start_transaction()
         stm_write(p1)
         p1[8] = 'b'
         stm_stop_transaction(False)
         #
-        self.switch("main")
+        self.switch(0)
         stm_write(p2)
         p2[8] = 'C'
         stm_stop_transaction(False)
@@ -206,14 +241,14 @@
         stm_stop_transaction(False)
         stm_start_transaction()
         #
-        self.switch("sub1")
+        self.switch(1)
         stm_start_transaction()
         stm_write(p1)
         assert p1[8] == 'A'
         p1[8] = 'B'
         stm_stop_transaction(False)
         #
-        self.switch("main")
+        self.switch(0)
         stm_read(p2)
         assert p2[8] == 'a'
         p3 = stm_allocate(16)   # goes into the same page, which is
@@ -233,14 +268,14 @@
         stm_stop_transaction(False)
         stm_start_transaction()
         #
-        self.switch("sub1")
+        self.switch(1)
         stm_start_transaction()
         stm_write(p1)
         assert p1[8] == 'A'
         p1[8] = 'B'
         stm_stop_transaction(False)
         #
-        self.switch("main")
+        self.switch(0)
         stm_write(p2)
         assert p2[8] == 'a'
         p2[8] = 'b'
@@ -261,14 +296,14 @@
         stm_stop_transaction(False)
         stm_start_transaction()
         #
-        self.switch("sub1")
+        self.switch(1)
         stm_start_transaction()
         stm_write(p1)
         assert p1[8] == 'A'
         p1[8] = 'B'
         stm_stop_transaction(False)
         #
-        self.switch("main")
+        self.switch(0)
         p3 = stm_allocate(16)  # goes into the same page, which I will
         p3[8] = ':'            #  modify just below
         stm_write(p2)
_______________________________________________
pypy-commit mailing list
[email protected]
https://mail.python.org/mailman/listinfo/pypy-commit

Reply via email to