[pypy-commit] stmgc default: c8: pass some first finalizer tests

Raemi noreply at buildbot.pypy.org
Tue Feb 24 23:50:36 CET 2015


Author: Remi Meier <remi.meier at gmail.com>
Branch: 
Changeset: r1659:7aceea4cc94e
Date: 2015-02-24 17:12 +0100
http://bitbucket.org/pypy/stmgc/changeset/7aceea4cc94e/

Log:	c8: pass some first finalizer tests

diff --git a/c8/stm/core.c b/c8/stm/core.c
--- a/c8/stm/core.c
+++ b/c8/stm/core.c
@@ -773,6 +773,8 @@
     assert(tree_is_cleared(STM_PSEGMENT->nursery_objects_shadows));
     assert(tree_is_cleared(STM_PSEGMENT->callbacks_on_commit_and_abort[0]));
     assert(tree_is_cleared(STM_PSEGMENT->callbacks_on_commit_and_abort[1]));
+    assert(list_is_empty(STM_PSEGMENT->young_objects_with_light_finalizers));
+    assert(STM_PSEGMENT->finalizers == NULL);
 
     check_nursery_at_transaction_start();
 
@@ -871,6 +873,8 @@
 
 void stm_commit_transaction(void)
 {
+    exec_local_finalizers();
+
     assert(!_has_mutex());
     assert(STM_PSEGMENT->safe_point == SP_RUNNING);
     assert(STM_PSEGMENT->running_pthread == pthread_self());
@@ -884,7 +888,6 @@
     _validate_and_add_to_commit_log();
 
 
-
     /* XXX do we still need a s_mutex_lock() section here? */
     s_mutex_lock();
 
@@ -893,6 +896,8 @@
 
     stm_rewind_jmp_forget(STM_SEGMENT->running_thread);
 
+    commit_finalizers();
+
     invoke_and_clear_user_callbacks(0);   /* for commit */
 
     if (globally_unique_transaction && was_inev) {
@@ -900,10 +905,15 @@
     }
 
     /* done */
+    stm_thread_local_t *tl = STM_SEGMENT->running_thread;
     _finish_transaction();
     /* cannot access STM_SEGMENT or STM_PSEGMENT from here ! */
 
     s_mutex_unlock();
+
+    /* between transactions, call finalizers. this will execute
+       a transaction itself */
+    invoke_general_finalizers(tl);
 }
 
 static void reset_modified_from_backup_copies(int segment_num)
@@ -959,6 +969,8 @@
                        (int)pseg->transaction_state);
     }
 
+    abort_finalizers(pseg);
+
     long bytes_in_nursery = throw_away_nursery(pseg);
 
     acquire_modification_lock(segment_num);
diff --git a/c8/stm/core.h b/c8/stm/core.h
--- a/c8/stm/core.h
+++ b/c8/stm/core.h
@@ -110,6 +110,14 @@
     pthread_t running_pthread;
 #endif
 
+    /* light finalizers */
+    struct list_s *young_objects_with_light_finalizers;
+    struct list_s *old_objects_with_light_finalizers;
+
+    /* regular finalizers (objs from the current transaction only) */
+    struct finalizers_s *finalizers;
+
+
     /* This is for smallmalloc.c */
     struct small_malloc_data_s small_malloc_data;
 
diff --git a/c8/stm/finalizer.c b/c8/stm/finalizer.c
--- a/c8/stm/finalizer.c
+++ b/c8/stm/finalizer.c
@@ -28,6 +28,8 @@
 
 static void _commit_finalizers(void)
 {
+    /* move finalizer lists to g_finalizers for major collections */
+
     if (STM_PSEGMENT->finalizers->run_finalizers != NULL) {
         /* copy 'STM_PSEGMENT->finalizers->run_finalizers' into
            'g_finalizers.run_finalizers', dropping any initial NULLs
@@ -96,14 +98,14 @@
         list_clear(lst);
     }
 
-    /* also deals with overflow objects: they are at the tail of
+    /* also deals with newly created objects: they are at the tail of
        old_objects_with_light_finalizers (this list is kept in order
        and we cannot add any already-committed object) */
     lst = pseg->old_objects_with_light_finalizers;
     count = list_count(lst);
     while (count > 0) {
         object_t *obj = (object_t *)list_item(lst, --count);
-        if (!IS_OVERFLOW_OBJ(pseg, obj))
+        if (!(obj->stm_flags & GCFLAG_WB_EXECUTED))
             break;
         lst->count = count;
         if (must_fix_gs) {
@@ -153,7 +155,8 @@
 
 static void deal_with_young_objects_with_finalizers(void)
 {
-    /* for light finalizers */
+    /* for light finalizers: executes finalizers for objs that don't survive
+       this minor gc */
     struct list_s *lst = STM_PSEGMENT->young_objects_with_light_finalizers;
     long i, count = list_count(lst);
     for (i = 0; i < count; i++) {
@@ -174,48 +177,48 @@
     list_clear(lst);
 }
 
-static void deal_with_old_objects_with_finalizers(void)
-{
-    /* for light finalizers */
-    int old_gs_register = STM_SEGMENT->segment_num;
-    int current_gs_register = old_gs_register;
-    long j;
-    for (j = 1; j <= NB_SEGMENTS; j++) {
-        struct stm_priv_segment_info_s *pseg = get_priv_segment(j);
+/* static void deal_with_old_objects_with_finalizers(void) */
+/* { */
+/*     /\* for light finalizers *\/ */
+/*     int old_gs_register = STM_SEGMENT->segment_num; */
+/*     int current_gs_register = old_gs_register; */
+/*     long j; */
+/*     for (j = 1; j <= NB_SEGMENTS; j++) { */
+/*         struct stm_priv_segment_info_s *pseg = get_priv_segment(j); */
 
-        struct list_s *lst = pseg->old_objects_with_light_finalizers;
-        long i, count = list_count(lst);
-        lst->count = 0;
-        for (i = 0; i < count; i++) {
-            object_t *obj = (object_t *)list_item(lst, i);
-            if (!mark_visited_test(obj)) {
-                /* not marked: object dies */
-                /* we're calling the light finalizer in the same
-                   segment as where it was originally registered.  For
-                   objects that existed since a long time, it doesn't
-                   change anything: any thread should see the same old
-                   content (because if it wasn't the case, the object
-                   would be in a 'modified_old_objects' list
-                   somewhere, and so it wouldn't be dead).  But it's
-                   important if the object was created by the same
-                   transaction: then only that segment sees valid
-                   content.
-                */
-                if (j != current_gs_register) {
-                    set_gs_register(get_segment_base(j));
-                    current_gs_register = j;
-                }
-                stmcb_light_finalizer(obj);
-            }
-            else {
-                /* object survives */
-                list_set_item(lst, lst->count++, (uintptr_t)obj);
-            }
-        }
-    }
-    if (old_gs_register != current_gs_register)
-        set_gs_register(get_segment_base(old_gs_register));
-}
+/*         struct list_s *lst = pseg->old_objects_with_light_finalizers; */
+/*         long i, count = list_count(lst); */
+/*         lst->count = 0; */
+/*         for (i = 0; i < count; i++) { */
+/*             object_t *obj = (object_t *)list_item(lst, i); */
+/*             if (!mark_visited_test(obj)) { */
+/*                 /\* not marked: object dies *\/ */
+/*                 /\* we're calling the light finalizer in the same */
+/*                    segment as where it was originally registered.  For */
+/*                    objects that existed since a long time, it doesn't */
+/*                    change anything: any thread should see the same old */
+/*                    content (because if it wasn't the case, the object */
+/*                    would be in a 'modified_old_objects' list */
+/*                    somewhere, and so it wouldn't be dead).  But it's */
+/*                    important if the object was created by the same */
+/*                    transaction: then only that segment sees valid */
+/*                    content. */
+/*                 *\/ */
+/*                 if (j != current_gs_register) { */
+/*                     set_gs_register(get_segment_base(j)); */
+/*                     current_gs_register = j; */
+/*                 } */
+/*                 stmcb_light_finalizer(obj); */
+/*             } */
+/*             else { */
+/*                 /\* object survives *\/ */
+/*                 list_set_item(lst, lst->count++, (uintptr_t)obj); */
+/*             } */
+/*         } */
+/*     } */
+/*     if (old_gs_register != current_gs_register) */
+/*         set_gs_register(get_segment_base(old_gs_register)); */
+/* } */
 
 
 /************************************************************/
@@ -224,195 +227,195 @@
     as well as rpython/memory/gc/minimark.py.
 */
 
-static inline int _finalization_state(object_t *obj)
-{
-    /* Returns the state, "0", 1, 2 or 3, as per finalizer-order.rst.
-       One difference is that the official state 0 is returned here
-       as a number that is <= 0. */
-    uintptr_t lock_idx = mark_loc(obj);
-    return write_locks[lock_idx] - (WL_FINALIZ_ORDER_1 - 1);
-}
+/* static inline int _finalization_state(object_t *obj) */
+/* { */
+/*     /\* Returns the state, "0", 1, 2 or 3, as per finalizer-order.rst. */
+/*        One difference is that the official state 0 is returned here */
+/*        as a number that is <= 0. *\/ */
+/*     uintptr_t lock_idx = mark_loc(obj); */
+/*     return write_locks[lock_idx] - (WL_FINALIZ_ORDER_1 - 1); */
+/* } */
 
-static void _bump_finalization_state_from_0_to_1(object_t *obj)
-{
-    uintptr_t lock_idx = mark_loc(obj);
-    assert(write_locks[lock_idx] < WL_FINALIZ_ORDER_1);
-    write_locks[lock_idx] = WL_FINALIZ_ORDER_1;
-}
+/* static void _bump_finalization_state_from_0_to_1(object_t *obj) */
+/* { */
+/*     uintptr_t lock_idx = mark_loc(obj); */
+/*     assert(write_locks[lock_idx] < WL_FINALIZ_ORDER_1); */
+/*     write_locks[lock_idx] = WL_FINALIZ_ORDER_1; */
+/* } */
 
-static struct list_s *_finalizer_tmpstack;
-static struct list_s *_finalizer_emptystack;
-static struct list_s *_finalizer_pending;
+/* static struct list_s *_finalizer_tmpstack; */
+/* static struct list_s *_finalizer_emptystack; */
+/* static struct list_s *_finalizer_pending; */
 
-static inline void _append_to_finalizer_tmpstack(object_t **pobj)
-{
-    object_t *obj = *pobj;
-    if (obj != NULL)
-        LIST_APPEND(_finalizer_tmpstack, obj);
-}
+/* static inline void _append_to_finalizer_tmpstack(object_t **pobj) */
+/* { */
+/*     object_t *obj = *pobj; */
+/*     if (obj != NULL) */
+/*         LIST_APPEND(_finalizer_tmpstack, obj); */
+/* } */
 
-static inline struct list_s *finalizer_trace(char *base, object_t *obj,
-                                             struct list_s *lst)
-{
-    struct object_s *realobj = (struct object_s *)REAL_ADDRESS(base, obj);
-    _finalizer_tmpstack = lst;
-    stmcb_trace(realobj, &_append_to_finalizer_tmpstack);
-    return _finalizer_tmpstack;
-}
+/* static inline struct list_s *finalizer_trace(char *base, object_t *obj, */
+/*                                              struct list_s *lst) */
+/* { */
+/*     struct object_s *realobj = (struct object_s *)REAL_ADDRESS(base, obj); */
+/*     _finalizer_tmpstack = lst; */
+/*     stmcb_trace(realobj, &_append_to_finalizer_tmpstack); */
+/*     return _finalizer_tmpstack; */
+/* } */
 
-static void _recursively_bump_finalization_state(char *base, object_t *obj,
-                                                 int to_state)
-{
-    struct list_s *tmpstack = _finalizer_emptystack;
-    assert(list_is_empty(tmpstack));
+/* static void _recursively_bump_finalization_state(char *base, object_t *obj, */
+/*                                                  int to_state) */
+/* { */
+/*     struct list_s *tmpstack = _finalizer_emptystack; */
+/*     assert(list_is_empty(tmpstack)); */
 
-    while (1) {
-        if (_finalization_state(obj) == to_state - 1) {
-            /* bump to the next state */
-            write_locks[mark_loc(obj)]++;
+/*     while (1) { */
+/*         if (_finalization_state(obj) == to_state - 1) { */
+/*             /\* bump to the next state *\/ */
+/*             write_locks[mark_loc(obj)]++; */
 
-            /* trace */
-            tmpstack = finalizer_trace(base, obj, tmpstack);
-        }
+/*             /\* trace *\/ */
+/*             tmpstack = finalizer_trace(base, obj, tmpstack); */
+/*         } */
 
-        if (list_is_empty(tmpstack))
-            break;
+/*         if (list_is_empty(tmpstack)) */
+/*             break; */
 
-        obj = (object_t *)list_pop_item(tmpstack);
-    }
-    _finalizer_emptystack = tmpstack;
-}
+/*         obj = (object_t *)list_pop_item(tmpstack); */
+/*     } */
+/*     _finalizer_emptystack = tmpstack; */
+/* } */
 
-static struct list_s *mark_finalize_step1(char *base, struct finalizers_s *f)
-{
-    if (f == NULL)
-        return NULL;
+/* static struct list_s *mark_finalize_step1(char *base, struct finalizers_s *f) */
+/* { */
+/*     if (f == NULL) */
+/*         return NULL; */
 
-    struct list_s *marked = list_create();
+/*     struct list_s *marked = list_create(); */
 
-    struct list_s *lst = f->objects_with_finalizers;
-    long i, count = list_count(lst);
-    lst->count = 0;
-    f->count_non_young = 0;
+/*     struct list_s *lst = f->objects_with_finalizers; */
+/*     long i, count = list_count(lst); */
+/*     lst->count = 0; */
+/*     f->count_non_young = 0; */
 
-    for (i = 0; i < count; i++) {
-        object_t *x = (object_t *)list_item(lst, i);
+/*     for (i = 0; i < count; i++) { */
+/*         object_t *x = (object_t *)list_item(lst, i); */
 
-        assert(_finalization_state(x) != 1);
-        if (_finalization_state(x) >= 2) {
-            list_set_item(lst, lst->count++, (uintptr_t)x);
-            continue;
-        }
-        LIST_APPEND(marked, x);
+/*         assert(_finalization_state(x) != 1); */
+/*         if (_finalization_state(x) >= 2) { */
+/*             list_set_item(lst, lst->count++, (uintptr_t)x); */
+/*             continue; */
+/*         } */
+/*         LIST_APPEND(marked, x); */
 
-        struct list_s *pending = _finalizer_pending;
-        LIST_APPEND(pending, x);
-        while (!list_is_empty(pending)) {
-            object_t *y = (object_t *)list_pop_item(pending);
-            int state = _finalization_state(y);
-            if (state <= 0) {
-                _bump_finalization_state_from_0_to_1(y);
-                pending = finalizer_trace(base, y, pending);
-            }
-            else if (state == 2) {
-                _recursively_bump_finalization_state(base, y, 3);
-            }
-        }
-        _finalizer_pending = pending;
-        assert(_finalization_state(x) == 1);
-        _recursively_bump_finalization_state(base, x, 2);
-    }
-    return marked;
-}
+/*         struct list_s *pending = _finalizer_pending; */
+/*         LIST_APPEND(pending, x); */
+/*         while (!list_is_empty(pending)) { */
+/*             object_t *y = (object_t *)list_pop_item(pending); */
+/*             int state = _finalization_state(y); */
+/*             if (state <= 0) { */
+/*                 _bump_finalization_state_from_0_to_1(y); */
+/*                 pending = finalizer_trace(base, y, pending); */
+/*             } */
+/*             else if (state == 2) { */
+/*                 _recursively_bump_finalization_state(base, y, 3); */
+/*             } */
+/*         } */
+/*         _finalizer_pending = pending; */
+/*         assert(_finalization_state(x) == 1); */
+/*         _recursively_bump_finalization_state(base, x, 2); */
+/*     } */
+/*     return marked; */
+/* } */
 
-static void mark_finalize_step2(char *base, struct finalizers_s *f,
-                                struct list_s *marked)
-{
-    if (f == NULL)
-        return;
+/* static void mark_finalize_step2(char *base, struct finalizers_s *f, */
+/*                                 struct list_s *marked) */
+/* { */
+/*     if (f == NULL) */
+/*         return; */
 
-    struct list_s *run_finalizers = f->run_finalizers;
+/*     struct list_s *run_finalizers = f->run_finalizers; */
 
-    long i, count = list_count(marked);
-    for (i = 0; i < count; i++) {
-        object_t *x = (object_t *)list_item(marked, i);
+/*     long i, count = list_count(marked); */
+/*     for (i = 0; i < count; i++) { */
+/*         object_t *x = (object_t *)list_item(marked, i); */
 
-        int state = _finalization_state(x);
-        assert(state >= 2);
-        if (state == 2) {
-            if (run_finalizers == NULL)
-                run_finalizers = list_create();
-            LIST_APPEND(run_finalizers, x);
-            _recursively_bump_finalization_state(base, x, 3);
-        }
-        else {
-            struct list_s *lst = f->objects_with_finalizers;
-            list_set_item(lst, lst->count++, (uintptr_t)x);
-        }
-    }
-    list_free(marked);
+/*         int state = _finalization_state(x); */
+/*         assert(state >= 2); */
+/*         if (state == 2) { */
+/*             if (run_finalizers == NULL) */
+/*                 run_finalizers = list_create(); */
+/*             LIST_APPEND(run_finalizers, x); */
+/*             _recursively_bump_finalization_state(base, x, 3); */
+/*         } */
+/*         else { */
+/*             struct list_s *lst = f->objects_with_finalizers; */
+/*             list_set_item(lst, lst->count++, (uintptr_t)x); */
+/*         } */
+/*     } */
+/*     list_free(marked); */
 
-    f->run_finalizers = run_finalizers;
-}
+/*     f->run_finalizers = run_finalizers; */
+/* } */
 
-static void deal_with_objects_with_finalizers(void)
-{
-    /* for non-light finalizers */
+/* static void deal_with_objects_with_finalizers(void) */
+/* { */
+/*     /\* for non-light finalizers *\/ */
 
-    /* there is one 'objects_with_finalizers' list per segment.
-       Objects that die at a major collection running in the same
-       transaction as they were created will be put in the
-       'run_finalizers' list of that segment.  Objects that survive at
-       least one commit move to the global g_objects_with_finalizers,
-       and when they die they go to g_run_finalizers.  The former kind
-       of dying object must have its finalizer called in the correct
-       thread; the latter kind can be called in any thread, through
-       any segment, because they all should see the same old content
-       anyway.  (If the content was different between segments at this
-       point, the object would be in a 'modified_old_objects' list
-       somewhere, and so it wouldn't be dead).
-    */
-    struct list_s *marked_seg[NB_SEGMENTS + 1];
-    LIST_CREATE(_finalizer_emptystack);
-    LIST_CREATE(_finalizer_pending);
+/*     /\* there is one 'objects_with_finalizers' list per segment. */
+/*        Objects that die at a major collection running in the same */
+/*        transaction as they were created will be put in the */
+/*        'run_finalizers' list of that segment.  Objects that survive at */
+/*        least one commit move to the global g_objects_with_finalizers, */
+/*        and when they die they go to g_run_finalizers.  The former kind */
+/*        of dying object must have its finalizer called in the correct */
+/*        thread; the latter kind can be called in any thread, through */
+/*        any segment, because they all should see the same old content */
+/*        anyway.  (If the content was different between segments at this */
+/*        point, the object would be in a 'modified_old_objects' list */
+/*        somewhere, and so it wouldn't be dead). */
+/*     *\/ */
+/*     struct list_s *marked_seg[NB_SEGMENTS + 1]; */
+/*     LIST_CREATE(_finalizer_emptystack); */
+/*     LIST_CREATE(_finalizer_pending); */
 
-    long j;
-    for (j = 1; j <= NB_SEGMENTS; j++) {
-        struct stm_priv_segment_info_s *pseg = get_priv_segment(j);
-        marked_seg[j] = mark_finalize_step1(pseg->pub.segment_base,
-                                            pseg->finalizers);
-    }
-    marked_seg[0] = mark_finalize_step1(stm_object_pages, &g_finalizers);
+/*     long j; */
+/*     for (j = 1; j <= NB_SEGMENTS; j++) { */
+/*         struct stm_priv_segment_info_s *pseg = get_priv_segment(j); */
+/*         marked_seg[j] = mark_finalize_step1(pseg->pub.segment_base, */
+/*                                             pseg->finalizers); */
+/*     } */
+/*     marked_seg[0] = mark_finalize_step1(stm_object_pages, &g_finalizers); */
 
-    LIST_FREE(_finalizer_pending);
+/*     LIST_FREE(_finalizer_pending); */
 
-    for (j = 1; j <= NB_SEGMENTS; j++) {
-        struct stm_priv_segment_info_s *pseg = get_priv_segment(j);
-        mark_finalize_step2(pseg->pub.segment_base, pseg->finalizers,
-                            marked_seg[j]);
-    }
-    mark_finalize_step2(stm_object_pages, &g_finalizers, marked_seg[0]);
+/*     for (j = 1; j <= NB_SEGMENTS; j++) { */
+/*         struct stm_priv_segment_info_s *pseg = get_priv_segment(j); */
+/*         mark_finalize_step2(pseg->pub.segment_base, pseg->finalizers, */
+/*                             marked_seg[j]); */
+/*     } */
+/*     mark_finalize_step2(stm_object_pages, &g_finalizers, marked_seg[0]); */
 
-    LIST_FREE(_finalizer_emptystack);
-}
+/*     LIST_FREE(_finalizer_emptystack); */
+/* } */
 
-static void mark_visit_from_finalizer1(char *base, struct finalizers_s *f)
-{
-    if (f != NULL && f->run_finalizers != NULL) {
-        LIST_FOREACH_R(f->run_finalizers, object_t * /*item*/,
-                       mark_visit_object(item, base));
-    }
-}
+/* static void mark_visit_from_finalizer1(char *base, struct finalizers_s *f) */
+/* { */
+/*     if (f != NULL && f->run_finalizers != NULL) { */
+/*         LIST_FOREACH_R(f->run_finalizers, object_t * /\*item*\/, */
+/*                        mark_visit_object(item, base)); */
+/*     } */
+/* } */
 
-static void mark_visit_from_finalizer_pending(void)
-{
-    long j;
-    for (j = 1; j <= NB_SEGMENTS; j++) {
-        struct stm_priv_segment_info_s *pseg = get_priv_segment(j);
-        mark_visit_from_finalizer1(pseg->pub.segment_base, pseg->finalizers);
-    }
-    mark_visit_from_finalizer1(stm_object_pages, &g_finalizers);
-}
+/* static void mark_visit_from_finalizer_pending(void) */
+/* { */
+/*     long j; */
+/*     for (j = 1; j <= NB_SEGMENTS; j++) { */
+/*         struct stm_priv_segment_info_s *pseg = get_priv_segment(j); */
+/*         mark_visit_from_finalizer1(pseg->pub.segment_base, pseg->finalizers); */
+/*     } */
+/*     mark_visit_from_finalizer1(stm_object_pages, &g_finalizers); */
+/* } */
 
 static void _execute_finalizers(struct finalizers_s *f)
 {
diff --git a/c8/stm/list.c b/c8/stm/list.c
--- a/c8/stm/list.c
+++ b/c8/stm/list.c
@@ -30,6 +30,21 @@
     return lst;
 }
 
+static struct list_s *list_extend(struct list_s *lst, struct list_s *lst2,
+                                  uintptr_t slicestart)
+{
+    if (lst2->count <= slicestart)
+        return lst;
+    uintptr_t baseindex = lst->count;
+    lst->count = baseindex + lst2->count - slicestart;
+    uintptr_t lastindex = lst->count - 1;
+    if (lastindex > lst->last_allocated)
+        lst = _list_grow(lst, lastindex);
+    memcpy(lst->items + baseindex, lst2->items + slicestart,
+           (lst2->count - slicestart) * sizeof(uintptr_t));
+    return lst;
+}
+
 
 /************************************************************/
 
diff --git a/c8/stm/list.h b/c8/stm/list.h
--- a/c8/stm/list.h
+++ b/c8/stm/list.h
@@ -96,6 +96,10 @@
     return &lst->items[index];
 }
 
+static struct list_s *list_extend(struct list_s *lst, struct list_s *lst2,
+                                  uintptr_t slicestart);
+
+
 #define LIST_FOREACH_R(lst, TYPE, CODE)         \
     do {                                        \
         struct list_s *_lst = (lst);            \
diff --git a/c8/stm/nursery.c b/c8/stm/nursery.c
--- a/c8/stm/nursery.c
+++ b/c8/stm/nursery.c
@@ -37,6 +37,10 @@
         tree_contains(STM_PSEGMENT->young_outside_nursery, (uintptr_t)obj));
 }
 
+static inline bool _is_from_same_transaction(object_t *obj) {
+    return _is_young(obj) || (obj->stm_flags & GCFLAG_WB_EXECUTED);
+}
+
 long stm_can_move(object_t *obj)
 {
     /* 'long' return value to avoid using 'bool' in the public interface */
@@ -226,6 +230,22 @@
     }
 }
 
+static void collect_objs_still_young_but_with_finalizers(void)
+{
+    struct list_s *lst = STM_PSEGMENT->finalizers->objects_with_finalizers;
+    uintptr_t i, total = list_count(lst);
+
+    for (i = STM_PSEGMENT->finalizers->count_non_young; i < total; i++) {
+
+        object_t *o = (object_t *)list_item(lst, i);
+        minor_trace_if_young(&o);
+
+        /* was not actually movable */
+        assert(o == (object_t *)list_item(lst, i));
+    }
+    STM_PSEGMENT->finalizers->count_non_young = total;
+}
+
 
 static size_t throw_away_nursery(struct stm_priv_segment_info_s *pseg)
 {
@@ -296,12 +316,16 @@
 
     collect_roots_in_nursery();
 
+    if (STM_PSEGMENT->finalizers != NULL)
+        collect_objs_still_young_but_with_finalizers();
+
     collect_oldrefs_to_nursery();
 
     /* now all surviving nursery objects have been moved out */
     acquire_privatization_lock(STM_SEGMENT->segment_num);
     stm_move_young_weakrefs();
     release_privatization_lock(STM_SEGMENT->segment_num);
+    deal_with_young_objects_with_finalizers();
 
     assert(list_is_empty(STM_PSEGMENT->objects_pointing_to_nursery));
 
diff --git a/c8/stm/setup.c b/c8/stm/setup.c
--- a/c8/stm/setup.c
+++ b/c8/stm/setup.c
@@ -108,6 +108,8 @@
         pr->nursery_objects_shadows = tree_create();
         pr->callbacks_on_commit_and_abort[0] = tree_create();
         pr->callbacks_on_commit_and_abort[1] = tree_create();
+        pr->young_objects_with_light_finalizers = list_create();
+        pr->old_objects_with_light_finalizers = list_create();
 
         pr->last_commit_log_entry = &commit_log_root;
         pr->pub.transaction_read_version = 0xff;
@@ -127,6 +129,7 @@
     setup_gcpage();
     setup_pages();
     setup_forksupport();
+    setup_finalizer();
 
     set_gs_register(get_segment_base(0));
 }
@@ -153,6 +156,8 @@
         tree_free(pr->nursery_objects_shadows);
         tree_free(pr->callbacks_on_commit_and_abort[0]);
         tree_free(pr->callbacks_on_commit_and_abort[1]);
+        list_free(pr->young_objects_with_light_finalizers);
+        list_free(pr->old_objects_with_light_finalizers);
     }
 
     munmap(stm_object_pages, TOTAL_MEMORY);
@@ -160,6 +165,7 @@
     commit_log_root.next = NULL; /* xxx:free them */
     commit_log_root.segment_num = -1;
 
+    teardown_finalizer();
     teardown_sync();
     teardown_gcpage();
     teardown_smallmalloc();
diff --git a/c8/stm/sync.c b/c8/stm/sync.c
--- a/c8/stm/sync.c
+++ b/c8/stm/sync.c
@@ -193,6 +193,7 @@
     assert(_stm_in_transaction(tl));
     set_gs_register(get_segment_base(tl->associated_segment_num));
     assert(STM_SEGMENT->running_thread == tl);
+    exec_local_finalizers();
 }
 
 void _stm_test_switch_segment(int segnum)
diff --git a/c8/stmgc.h b/c8/stmgc.h
--- a/c8/stmgc.h
+++ b/c8/stmgc.h
@@ -270,6 +270,31 @@
 void stm_validate(void);
 
 
+
+/* Support for light finalizers.  This is a simple version of
+   finalizers that guarantees not to do anything fancy, like not
+   resurrecting objects. */
+extern void (*stmcb_light_finalizer)(object_t *);
+void stm_enable_light_finalizer(object_t *);
+
+/* Support for regular finalizers.  Unreachable objects with
+   finalizers are kept alive, as well as everything they point to, and
+   stmcb_finalizer() is called after the major GC.  If there are
+   several objects with finalizers that reference each other in a
+   well-defined order (i.e. there are no cycles), then they are
+   finalized in order from outermost to innermost (i.e. starting with
+   the ones that are unreachable even from others).
+
+   For objects that have been created by the current transaction, if a
+   major GC runs while that transaction is alive and finds the object
+   unreachable, the finalizer is called immediately in the same
+   transaction.  For older objects, the finalizer is called from a
+   random thread between regular transactions, in a new custom
+   transaction. */
+extern void (*stmcb_finalizer)(object_t *);
+object_t *stm_allocate_with_finalizer(ssize_t size_rounded_up);
+
+
 /* dummies for now: */
 __attribute__((always_inline))
 static inline void stm_write_card(object_t *obj, uintptr_t index)
diff --git a/c8/test/support.py b/c8/test/support.py
--- a/c8/test/support.py
+++ b/c8/test/support.py
@@ -40,6 +40,7 @@
 /*void stm_write(object_t *obj); use _checked_stm_write() instead */
 object_t *stm_allocate(ssize_t size_rounded_up);
 object_t *stm_allocate_weakref(ssize_t size_rounded_up);
+object_t *stm_allocate_with_finalizer(ssize_t size_rounded_up);
 
 void stm_setup(void);
 void stm_teardown(void);
@@ -77,6 +78,7 @@
 bool _check_become_inevitable(stm_thread_local_t *tl);
 bool _check_become_globally_unique_transaction(stm_thread_local_t *tl);
 int stm_is_inevitable(void);
+long current_segment_num(void);
 
 void _set_type_id(object_t *obj, uint32_t h);
 uint32_t _get_type_id(object_t *obj);
@@ -128,6 +130,12 @@
 bool (*_stm_smallmalloc_keep)(char *data);
 void _stm_smallmalloc_sweep_test(void);
 
+
+void (*stmcb_light_finalizer)(object_t *);
+void stm_enable_light_finalizer(object_t *);
+
+void (*stmcb_finalizer)(object_t *);
+
 """)
 
 
@@ -322,6 +330,10 @@
     }
 }
 
+long current_segment_num(void)
+{
+    return STM_SEGMENT->segment_num;
+}
 ''', sources=source_files,
      define_macros=[('STM_TESTS', '1'),
                     ('STM_NO_AUTOMATIC_SETJMP', '1'),
@@ -332,7 +344,7 @@
                     ],
      undef_macros=['NDEBUG'],
      include_dirs=[parent_dir],
-     extra_compile_args=['-g', '-O0', '-Wall', '-Werror', '-ferror-limit=5'],
+     extra_compile_args=['-g', '-O0', '-Wall', '-ferror-limit=5'],
      extra_link_args=['-g', '-lrt'],
      force_generic_engine=True)
 
@@ -386,15 +398,27 @@
     lib._set_weakref(o, point_to_obj)
     return o
 
-def stm_get_weakref(o):
-    return lib._get_weakref(o)
-
 def stm_allocate_refs(n):
     o = lib.stm_allocate(HDR + n * WORD)
     tid = 421420 + n
     lib._set_type_id(o, tid)
     return o
 
+def stm_allocate_with_finalizer(size):
+    o = lib.stm_allocate_with_finalizer(size)
+    tid = 42 + size
+    lib._set_type_id(o, tid)
+    return o
+
+def stm_allocate_with_finalizer_refs(n):
+    o = lib.stm_allocate_with_finalizer(HDR + n * WORD)
+    tid = 421420 + n
+    lib._set_type_id(o, tid)
+    return o
+
+def stm_get_weakref(o):
+    return lib._get_weakref(o)
+
 def stm_set_ref(obj, idx, ref, use_cards=False):
     if use_cards:
         stm_write_card(obj, idx)
diff --git a/c8/test/test_list.py b/c8/test/test_list.py
--- a/c8/test/test_list.py
+++ b/c8/test/test_list.py
@@ -6,6 +6,12 @@
 ffi = cffi.FFI()
 ffi.cdef("""
 struct list_s *list_create(void);
+void list_free(struct list_s *lst);
+struct list_s *list_append(struct list_s *lst, uintptr_t item);
+uintptr_t list_count(struct list_s *lst);
+uintptr_t list_item(struct list_s *lst, uintptr_t index);
+struct list_s *list_extend(struct list_s *lst, struct list_s *lst2,
+                           uintptr_t slicestart);
 
 struct tree_s *tree_create(void);
 void tree_free(struct tree_s *tree);
@@ -127,3 +133,16 @@
 def test_hash_permutation():
     hashes = [((n ^ (n << 4)) & 0xFF0) for n in range(256)]
     assert set(hashes) == set(range(0, 4096, 16))
+
+def test_list_extend():
+    a = lib.list_create()
+    b = lib.list_create()
+    for i in range(100, 120):
+        b = lib.list_append(b, i)
+    a = lib.list_extend(a, b, 3)
+    a = lib.list_extend(a, b, 13)
+    assert lib.list_count(a) == 17 + 7
+    for i, expected in enumerate(range(103, 120) + range(113, 120)):
+        assert lib.list_item(a, i) == expected
+    lib.list_free(b)
+    lib.list_free(a)


More information about the pypy-commit mailing list