Author: Armin Rigo <[email protected]>
Branch: c8-gil-like
Changeset: r1820:5af967809206
Date: 2015-06-12 18:18 +0200
http://bitbucket.org/pypy/stmgc/changeset/5af967809206/

Log:    Do a "full" implementation of stm_should_break_transaction(), as
        opposed to the one in pypy, which didn't account for minor
        collections

diff --git a/c8/stm/core.c b/c8/stm/core.c
--- a/c8/stm/core.c
+++ b/c8/stm/core.c
@@ -1154,7 +1154,7 @@
 #endif
     STM_PSEGMENT->shadowstack_at_start_of_transaction = tl->shadowstack;
     STM_PSEGMENT->threadlocal_at_start_of_transaction = tl->thread_local_obj;
-
+    STM_PSEGMENT->total_throw_away_nursery = 0;
 
     assert(list_is_empty(STM_PSEGMENT->modified_old_objects));
     assert(list_is_empty(STM_PSEGMENT->large_overflow_objects));
@@ -1195,15 +1195,26 @@
     stm_validate();
 }
 
+#ifdef STM_NO_AUTOMATIC_SETJMP
+static int did_abort = 0;
+#endif
+
 long _stm_start_transaction(stm_thread_local_t *tl)
 {
     s_mutex_lock();
 #ifdef STM_NO_AUTOMATIC_SETJMP
-    long repeat_count = 0;    /* test/support.py */
+    long repeat_count = did_abort;    /* test/support.py */
+    did_abort = 0;
 #else
     long repeat_count = stm_rewind_jmp_setjmp(tl);
 #endif
     _do_start_transaction(tl);
+
+    if (repeat_count == 0) {  /* else, 'nursery_mark' was already set
+                                 in abort_data_structures_from_segment_num() */
+        STM_SEGMENT->nursery_mark = ((stm_char *)_stm_nursery_start +
+                                     stm_fill_mark_nursery_bytes);
+    }
     return repeat_count;
 }
 
@@ -1427,7 +1438,7 @@
 
     abort_finalizers(pseg);
 
-    long bytes_in_nursery = throw_away_nursery(pseg);
+    throw_away_nursery(pseg);
 
     /* clear CARD_MARKED on objs (don't care about CARD_MARKED_OLD) */
     LIST_FOREACH_R(pseg->old_objects_with_cards_set, object_t * /*item*/,
@@ -1461,7 +1472,26 @@
     assert(tl->shadowstack == pseg->shadowstack_at_start_of_transaction);
 #endif
     tl->thread_local_obj = pseg->threadlocal_at_start_of_transaction;
-    tl->last_abort__bytes_in_nursery = bytes_in_nursery;
+
+
+    /* Set the next nursery_mark: first compute the value that
+       nursery_mark must have had at the start of the aborted transaction */
+    stm_char *old_mark =pseg->pub.nursery_mark + 
pseg->total_throw_away_nursery;
+
+    /* This means that the limit, in term of bytes, was: */
+    uintptr_t old_limit = old_mark - (stm_char *)_stm_nursery_start;
+
+    /* If 'total_throw_away_nursery' is smaller than old_limit, use that */
+    if (pseg->total_throw_away_nursery < old_limit)
+        old_limit = pseg->total_throw_away_nursery;
+
+    /* Now set the new limit to 90% of the old limit */
+    pseg->pub.nursery_mark = ((stm_char *)_stm_nursery_start +
+                              (uintptr_t)(old_limit * 0.9));
+
+#ifdef STM_NO_AUTOMATIC_SETJMP
+    did_abort = 1;
+#endif
 
     list_clear(pseg->objects_pointing_to_nursery);
     list_clear(pseg->old_objects_with_cards_set);
diff --git a/c8/stm/core.h b/c8/stm/core.h
--- a/c8/stm/core.h
+++ b/c8/stm/core.h
@@ -152,6 +152,9 @@
     stm_char *sq_fragments[SYNC_QUEUE_SIZE];
     int sq_fragsizes[SYNC_QUEUE_SIZE];
     int sq_len;
+
+    /* For nursery_mark */
+    uintptr_t total_throw_away_nursery;
 };
 
 enum /* safe_point */ {
diff --git a/c8/stm/nursery.c b/c8/stm/nursery.c
--- a/c8/stm/nursery.c
+++ b/c8/stm/nursery.c
@@ -11,8 +11,13 @@
 static uintptr_t _stm_nursery_start;
 
 
+#define DEFAULT_FILL_MARK_NURSERY_BYTES   (NURSERY_SIZE / 4)
+
+uintptr_t stm_fill_mark_nursery_bytes = DEFAULT_FILL_MARK_NURSERY_BYTES;
+
 /************************************************************/
 
+
 static void setup_nursery(void)
 {
     assert(_STM_FAST_ALLOC <= NURSERY_SIZE);
@@ -449,7 +454,7 @@
 }
 
 
-static size_t throw_away_nursery(struct stm_priv_segment_info_s *pseg)
+static void throw_away_nursery(struct stm_priv_segment_info_s *pseg)
 {
 #pragma push_macro("STM_PSEGMENT")
 #pragma push_macro("STM_SEGMENT")
@@ -482,7 +487,9 @@
 #endif
 #endif
 
+    pseg->total_throw_away_nursery += nursery_used;
     pseg->pub.nursery_current = (stm_char *)_stm_nursery_start;
+    pseg->pub.nursery_mark -= nursery_used;
 
     /* free any object left from 'young_outside_nursery' */
     if (!tree_is_cleared(pseg->young_outside_nursery)) {
@@ -507,8 +514,6 @@
     }
 
     tree_clear(pseg->nursery_objects_shadows);
-
-    return nursery_used;
 #pragma pop_macro("STM_SEGMENT")
 #pragma pop_macro("STM_PSEGMENT")
 }
diff --git a/c8/stm/nursery.h b/c8/stm/nursery.h
--- a/c8/stm/nursery.h
+++ b/c8/stm/nursery.h
@@ -12,7 +12,7 @@
 
 static void minor_collection(bool commit, bool external);
 static void check_nursery_at_transaction_start(void);
-static size_t throw_away_nursery(struct stm_priv_segment_info_s *pseg);
+static void throw_away_nursery(struct stm_priv_segment_info_s *pseg);
 static void major_do_validation_and_minor_collections(void);
 
 static void assert_memset_zero(void *s, size_t n);
diff --git a/c8/stmgc.h b/c8/stmgc.h
--- a/c8/stmgc.h
+++ b/c8/stmgc.h
@@ -44,6 +44,7 @@
     int segment_num;
     char *segment_base;
     stm_char *nursery_current;
+    stm_char *nursery_mark;
     uintptr_t nursery_end;
     struct stm_thread_local_s *running_thread;
 };
@@ -67,9 +68,6 @@
        the following raw region of memory is cleared. */
     char *mem_clear_on_abort;
     size_t mem_bytes_to_clear_on_abort;
-    /* after an abort, some details about the abort are stored there.
-       (this field is not modified on a successful commit) */
-    long last_abort__bytes_in_nursery;
     /* the next fields are handled internally by the library */
     int last_associated_segment_num;   /* always a valid seg num */
     int thread_local_counter;
@@ -459,7 +457,8 @@
    stm_enter_transactional_zone(); however, it is supposed to be
    called in CPU-heavy threads that had a transaction run for a while,
    and so it *always* forces a commit and starts the next transaction.
-   The new transaction is never inevitable. */
+   The new transaction is never inevitable.  See also
+   stm_should_break_transaction(). */
 void stm_force_transaction_break(stm_thread_local_t *tl);
 
 /* Abort the currently running transaction.  This function never
@@ -490,6 +489,23 @@
 void stm_collect(long level);
 
 
+/* A way to detect that we've run for a while and should call
+   stm_force_transaction_break() */
+static inline int stm_should_break_transaction(void)
+{
+    return ((intptr_t)STM_SEGMENT->nursery_current >=
+            (intptr_t)STM_SEGMENT->nursery_mark);
+}
+extern uintptr_t stm_fill_mark_nursery_bytes;
+/* ^^^ at the start of a transaction, 'nursery_mark' is initialized to
+   'stm_fill_mark_nursery_bytes' inside the nursery.  This value can
+   be larger than the nursery; every minor collection shifts the
+   current 'nursery_mark' down by one nursery-size.  After an abort
+   and restart, 'nursery_mark' is set to ~90% of the value it reached
+   in the last attempt.
+*/
+
+
 /* Prepare an immortal "prebuilt" object managed by the GC.  Takes a
    pointer to an 'object_t', which should not actually be a GC-managed
    structure but a real static structure.  Returns the equivalent
diff --git a/c8/test/support.py b/c8/test/support.py
--- a/c8/test/support.py
+++ b/c8/test/support.py
@@ -28,7 +28,6 @@
     object_t *thread_local_obj;
     char *mem_clear_on_abort;
     size_t mem_bytes_to_clear_on_abort;
-    long last_abort__bytes_in_nursery;
     int last_associated_segment_num;
     struct stm_thread_local_s *prev, *next;
     void *creating_pthread[2];
@@ -37,6 +36,7 @@
 
 char *stm_object_pages;
 char *stm_file_pages;
+uintptr_t stm_fill_mark_nursery_bytes;
 
 void stm_read(object_t *obj);
 /*void stm_write(object_t *obj); use _checked_stm_write() instead */
@@ -104,6 +104,8 @@
 long _check_stm_collect(long level);
 uint64_t _stm_total_allocated(void);
 
+long bytes_before_transaction_break(void);
+
 void _stm_set_nursery_free_count(uint64_t free_count);
 void _stm_largemalloc_init_arena(char *data_start, size_t data_size);
 int _stm_largemalloc_resize_arena(size_t new_size);
@@ -404,6 +406,11 @@
     return *field;
 }
 
+long bytes_before_transaction_break(void)
+{
+    return STM_SEGMENT->nursery_mark - STM_SEGMENT->nursery_current;
+}
+
 
 ssize_t stmcb_size_rounded_up(struct object_s *obj)
 {
diff --git a/c8/test/test_basic.py b/c8/test/test_basic.py
--- a/c8/test/test_basic.py
+++ b/c8/test/test_basic.py
@@ -720,19 +720,53 @@
         lp1 = self.pop_root()
         self.check_char_everywhere(lp1, 'X')
 
-    def test_last_abort__bytes_in_nursery(self):
+    def test_stm_should_break_transaction_1(self):
+        lib.stm_fill_mark_nursery_bytes = 100
+        #
         self.start_transaction()
-        stm_allocate(56)
+        self.commit_transaction()
+        self.start_transaction()
+        assert lib.bytes_before_transaction_break() == 100
+        stm_allocate(64)
+        assert lib.bytes_before_transaction_break() == 36
+        stm_allocate(64)
+        assert lib.bytes_before_transaction_break() == -28
         self.abort_transaction()
-        assert self.get_stm_thread_local().last_abort__bytes_in_nursery == 56
         self.start_transaction()
-        assert self.get_stm_thread_local().last_abort__bytes_in_nursery == 56
+        assert lib.bytes_before_transaction_break() == 90    # 100 * 0.9
+        stm_allocate(200)
+        self.abort_transaction()
+        self.start_transaction()
+        assert lib.bytes_before_transaction_break() == 81    # 90 * 0.9
         self.commit_transaction()
-        assert self.get_stm_thread_local().last_abort__bytes_in_nursery == 56
+        #
         self.start_transaction()
-        assert self.get_stm_thread_local().last_abort__bytes_in_nursery == 56
+        assert lib.bytes_before_transaction_break() == 100
+        stm_allocate(64)
+        assert lib.bytes_before_transaction_break() == 36
         self.abort_transaction()
-        assert self.get_stm_thread_local().last_abort__bytes_in_nursery == 0
+        self.start_transaction()
+        assert lib.bytes_before_transaction_break() == 57    # int(64 * 0.9)
+        stm_allocate(32)
+        assert lib.bytes_before_transaction_break() == 25
+        self.abort_transaction()
+        self.start_transaction()
+        assert lib.bytes_before_transaction_break() == 28    # int(32 * 0.9)
+        stm_allocate(64)
+        assert lib.bytes_before_transaction_break() == -36
+        self.commit_transaction()
+
+    def test_stm_should_break_transaction_2(self):
+        lib.stm_fill_mark_nursery_bytes = 10000000
+        #
+        n = 10000000
+        self.start_transaction()
+        self.commit_transaction()
+        self.start_transaction()
+        for i in range(1000):
+            assert lib.bytes_before_transaction_break() == n
+            stm_allocate(10000)
+            n -= 10000
 
     def test_bug(self):
         lp_char_5 = stm_allocate_old(384)
_______________________________________________
pypy-commit mailing list
[email protected]
https://mail.python.org/mailman/listinfo/pypy-commit

Reply via email to