Author: Remi Meier <remi.me...@gmail.com>
Branch: finalizer-queues
Changeset: r2003:7e7619989b07
Date: 2016-11-21 14:41 +0100
http://bitbucket.org/pypy/stmgc/changeset/7e7619989b07/

Log:    allow to run arbitrary things in finalizer triggers

diff --git a/c8/stm/finalizer.c b/c8/stm/finalizer.c
--- a/c8/stm/finalizer.c
+++ b/c8/stm/finalizer.c
@@ -17,8 +17,8 @@
     f->objects_with_finalizers = list_create();
     f->probably_young_objects_with_finalizers = list_create();
     f->run_finalizers = list_create();
-    f->running_next = NULL;
     f->lock = 0;
+    f->running_trigger_now = NULL;
 }
 
 static void setup_finalizer(void)
@@ -75,16 +75,10 @@
            'g_finalizers.run_finalizers', dropping any initial NULLs
            (finalizers already called) */
         struct list_s *src = STM_PSEGMENT->finalizers->run_finalizers;
-        uintptr_t frm = 0;
-        if (STM_PSEGMENT->finalizers->running_next != NULL) {
-            frm = *STM_PSEGMENT->finalizers->running_next;
-            assert(frm <= list_count(src));
-            *STM_PSEGMENT->finalizers->running_next = (uintptr_t)-1;
-        }
-        if (frm < list_count(src)) {
+        if (list_count(src)) {
             g_finalizers.run_finalizers = list_extend(
                 g_finalizers.run_finalizers,
-                src, frm);
+                src, 0);
         }
     }
     LIST_FREE(STM_PSEGMENT->finalizers->run_finalizers);
@@ -108,15 +102,16 @@
 {
     /* like _commit_finalizers(), but forget everything from the
        current transaction */
-    if (pseg->finalizers->running_next != NULL) {
-        *pseg->finalizers->running_next = (uintptr_t)-1;
-    }
     LIST_FREE(pseg->finalizers->run_finalizers);
     LIST_FREE(pseg->finalizers->objects_with_finalizers);
     LIST_FREE(pseg->finalizers->probably_young_objects_with_finalizers);
     // re-init
     init_finalizers(pseg->finalizers);
 
+    // if we were running triggers, release the lock:
+    if (g_finalizers.running_trigger_now == pseg)
+        g_finalizers.running_trigger_now = NULL;
+
     /* call the light finalizers for objects that are about to
        be forgotten from the current transaction */
     char *old_gs_register = STM_SEGMENT->segment_base;
@@ -494,15 +489,17 @@
         getfield on *dying obj*).
 */
 
-static bool _trigger_finalizer_queues(struct finalizers_s *f)
+static void _trigger_finalizer_queues(struct finalizers_s *f)
 {
     /* runs triggers of finalizer queues that have elements in the queue. May
-       run outside of a transaction, as triggers should be safe to call(?)
+       NOT run outside of a transaction, but triggers never leave the
+       transactional zone.
 
-       returns true if there are old-style finalizers to run */
+       returns true if there are also old-style finalizers to run */
+    assert(in_transaction(STM_PSEGMENT->pub.running_thread));
 
-    bool trigger_oldstyle = false;
-    bool *to_trigger = calloc(g_finalizer_triggers.count, sizeof(bool));
+    bool *to_trigger = (bool*)alloca(g_finalizer_triggers.count * 
sizeof(bool));
+    memset(to_trigger, 0, g_finalizer_triggers.count * sizeof(bool));
 
     while (__sync_lock_test_and_set(&f->lock, 1) != 0) {
         /* somebody is adding more finalizers (_commit_finalizer()) */
@@ -513,10 +510,7 @@
     for (int i = 0; i < count; i += 2) {
         int qindex = (int)list_item(f->run_finalizers, i + 1);
         dprintf(("qindex=%d\n", qindex));
-        if (qindex == -1)
-            trigger_oldstyle = true;
-        else
-            to_trigger[qindex] = true;
+        to_trigger[qindex] = true;
     }
 
     __sync_lock_release(&f->lock);
@@ -525,22 +519,20 @@
     for (int i = 0; i < g_finalizer_triggers.count; i++) {
         if (to_trigger[i]) {
             dprintf(("invoke-finalizer-trigger(qindex=%d)\n", i));
-            // XXX: check that triggers *really* cannot touch GC-memory,
-            // otherwise, this needs to run in an (inevitable) transaction
-#ifndef NDEBUG
-            char *old_gs_register = STM_SEGMENT->segment_base;
-            set_gs_register(NULL);
-#endif
             g_finalizer_triggers.triggers[i]();
-#ifndef NDEBUG
-            set_gs_register(old_gs_register);
-#endif
         }
     }
+}
 
-    free(to_trigger);
-
-    return trigger_oldstyle;
+static bool _has_oldstyle_finalizers(struct finalizers_s *f)
+{
+    int count = list_count(f->run_finalizers);
+    for (int i = 0; i < count; i += 2) {
+        int qindex = (int)list_item(f->run_finalizers, i + 1);
+        if (qindex == -1)
+            return true;
+    }
+    return false;
 }
 
 static void _invoke_local_finalizers()
@@ -548,10 +540,27 @@
     /* called inside a transaction; invoke local triggers, process old-style
      * local finalizers */
     dprintf(("invoke_local_finalizers %lu\n", 
list_count(STM_PSEGMENT->finalizers->run_finalizers)));
-    if (list_is_empty(STM_PSEGMENT->finalizers->run_finalizers))
+    if (list_is_empty(STM_PSEGMENT->finalizers->run_finalizers)
+        && list_is_empty(g_finalizers.run_finalizers))
         return;
 
-    if (!_trigger_finalizer_queues(STM_PSEGMENT->finalizers))
+    struct stm_priv_segment_info_s *pseg = 
get_priv_segment(STM_SEGMENT->segment_num);
+    //try to run local triggers
+    if (STM_PSEGMENT->finalizers->running_trigger_now == NULL) {
+        // we are not recursively running them
+        STM_PSEGMENT->finalizers->running_trigger_now = pseg;
+        _trigger_finalizer_queues(STM_PSEGMENT->finalizers);
+        STM_PSEGMENT->finalizers->running_trigger_now = NULL;
+    }
+
+    // try to run global triggers
+    if (__sync_lock_test_and_set(&g_finalizers.running_trigger_now, pseg) == 
NULL) {
+        // nobody is already running these triggers (recursively)
+        _trigger_finalizer_queues(&g_finalizers);
+        g_finalizers.running_trigger_now = NULL;
+    }
+
+    if (!_has_oldstyle_finalizers(STM_PSEGMENT->finalizers))
         return; // no oldstyle to run
 
     object_t *obj;
@@ -560,25 +569,25 @@
     }
 }
 
-
 static void _invoke_general_finalizers(stm_thread_local_t *tl)
 {
     /* called between transactions
-     * run old-style finalizers (q_index=-1) and run triggers for all finalizer
+     * triggers not called here, since all should have been called already in 
_invoke_local_finalizers!
+     * run old-style finalizers (q_index=-1)
      * queues that are not empty. */
     dprintf(("invoke_general_finalizers %lu\n", 
list_count(g_finalizers.run_finalizers)));
     if (list_is_empty(g_finalizers.run_finalizers))
         return;
 
-    if (!_trigger_finalizer_queues(&g_finalizers))
-        return; // no oldstyle finalizers to run
+    if (!_has_oldstyle_finalizers(&g_finalizers))
+        return; // no oldstyle to run
 
     // run old-style finalizers:
-    dprintf(("invoke-oldstyle-finalizers\n"));
     rewind_jmp_buf rjbuf;
     stm_rewind_jmp_enterframe(tl, &rjbuf);
     _stm_start_transaction(tl);
 
+    dprintf(("invoke_oldstyle_finalizers %lu\n", 
list_count(g_finalizers.run_finalizers)));
     object_t *obj;
     while ((obj = stm_next_to_finalize(-1)) != NULL) {
         assert(STM_PSEGMENT->transaction_state == TS_INEVITABLE);
diff --git a/c8/stm/finalizer.h b/c8/stm/finalizer.h
--- a/c8/stm/finalizer.h
+++ b/c8/stm/finalizer.h
@@ -6,10 +6,10 @@
 /* see deal_with_objects_with_finalizers() for explanation of these fields */
 struct finalizers_s {
     long lock;
+    struct stm_priv_segment_info_s * running_trigger_now; /* our PSEG, if we 
are running triggers */
     struct list_s *objects_with_finalizers;
-    struct list_s *probably_young_objects_with_finalizers;
+    struct list_s *probably_young_objects_with_finalizers; /* empty on 
g_finalizers! */
     struct list_s *run_finalizers;
-    uintptr_t *running_next;
 };
 
 static void mark_visit_from_finalizer_pending(void);
@@ -46,8 +46,7 @@
 
 
 #define exec_local_finalizers()  do {                   \
-    if (!list_is_empty(STM_PSEGMENT->finalizers->run_finalizers)) \
-        _invoke_local_finalizers();  \
+     _invoke_local_finalizers();                     \
 } while (0)
 
 #endif
diff --git a/c8/test/test_finalizer.py b/c8/test/test_finalizer.py
--- a/c8/test/test_finalizer.py
+++ b/c8/test/test_finalizer.py
@@ -150,7 +150,7 @@
 
 class TestRegularFinalizerQueues(BaseTest):
     expect_content_character = None
-    run_major_collect_in_finalizer = False
+    run_major_collect_in_trigger = False
 
     def setup_method(self, meth):
         BaseTest.setup_method(self, meth)
@@ -158,9 +158,13 @@
         @ffi.callback("stm_finalizer_trigger_fn")
         def trigger0():
             self.queues_triggered.append(0)
+            if self.run_major_collect_in_trigger:
+                stm_major_collect()
         @ffi.callback("stm_finalizer_trigger_fn")
         def trigger1():
             self.queues_triggered.append(1)
+            if self.run_major_collect_in_trigger:
+                stm_major_collect()
         self._trigger_keepalive = [trigger0, trigger1]
         triggers = ffi.new("stm_finalizer_trigger_fn[]", 
self._trigger_keepalive)
         lib.stm_setup_finalizer_queues(2, triggers)
@@ -276,8 +280,8 @@
 
         self.start_transaction()
         stm_major_collect()
-        assert not self.get_queues_triggered()
-        self.commit_transaction() # invoke finalizers
+        assert {0} == self.get_queues_triggered()
+        self.commit_transaction() # invoke finalizers (triggers again...)
         self.start_transaction()
         assert {0} == self.get_queues_triggered()
         assert [lp1b] == self.get_to_finalize(0)
@@ -301,17 +305,19 @@
         assert self.get_queues_triggered() == {0, 1}
         assert len(self.get_to_finalize(0)) == 1
 
-    def test_run_major_collect_in_finalizer(self):
-        self.run_major_collect_in_finalizer = True
+    def test_run_major_collect_in_trigger(self):
+        self.run_major_collect_in_trigger  = True
         self.start_transaction()
         lp1 = stm_allocate(32)
         lp2 = stm_allocate(32)
         lp3 = stm_allocate(32)
         lib.stm_enable_finalizer(0, lp1)
-        lib.stm_enable_finalizer(0, lp2)
-        lib.stm_enable_finalizer(0, lp3)
+        lib.stm_enable_finalizer(1, lp2)
+        lib.stm_enable_finalizer(1, lp3)
         print lp1, lp2, lp3
         stm_major_collect()
+        assert 1 == len(self.get_to_finalize(0))
+        assert 2 == len(self.get_to_finalize(1))
 
     def test_new_objects_w_finalizers(self):
         self.switch(2)
_______________________________________________
pypy-commit mailing list
pypy-commit@python.org
https://mail.python.org/mailman/listinfo/pypy-commit

Reply via email to