Author: Remi Meier <[email protected]>
Branch: finalizer-queues
Changeset: r2001:26622312cfa8
Date: 2016-11-17 11:37 +0100
http://bitbucket.org/pypy/stmgc/changeset/26622312cfa8/
Log: write some tests for finalizer queues
diff --git a/c8/stm/finalizer.c b/c8/stm/finalizer.c
--- a/c8/stm/finalizer.c
+++ b/c8/stm/finalizer.c
@@ -528,9 +528,13 @@
// 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
}
}
@@ -586,6 +590,8 @@
}
object_t* stm_next_to_finalize(int queue_index) {
+ assert(STM_PSEGMENT->transaction_state != TS_NONE);
+
/* first check local run_finalizers queue, then global */
if (!list_is_empty(STM_PSEGMENT->finalizers->run_finalizers)) {
struct list_s *lst = STM_PSEGMENT->finalizers->run_finalizers;
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
@@ -11,6 +11,7 @@
def trigger():
# triggers not needed for destructors
assert False
+ self._trigger_keepalive = trigger
triggers = ffi.new("stm_finalizer_trigger_fn*", trigger)
lib.stm_setup_finalizer_queues(1, triggers)
triggers = None
@@ -147,6 +148,190 @@
self.expect_finalized([lp1], from_tlnum=0)
+class TestRegularFinalizerQueues(BaseTest):
+ expect_content_character = None
+ run_major_collect_in_finalizer = False
+
+ def setup_method(self, meth):
+ BaseTest.setup_method(self, meth)
+ #
+ @ffi.callback("stm_finalizer_trigger_fn")
+ def trigger0():
+ self.queues_triggered.append(0)
+ @ffi.callback("stm_finalizer_trigger_fn")
+ def trigger1():
+ self.queues_triggered.append(1)
+ self._trigger_keepalive = [trigger0, trigger1]
+ triggers = ffi.new("stm_finalizer_trigger_fn[]",
self._trigger_keepalive)
+ lib.stm_setup_finalizer_queues(2, triggers)
+ #
+ self.queues_triggered = []
+
+ def get_queues_triggered(self):
+ res = set(self.queues_triggered)
+ self.queues_triggered = []
+ return res
+
+ def get_to_finalize(self, qindex):
+ obj = lib.stm_next_to_finalize(qindex)
+ res = []
+ while obj:
+ res.append(obj)
+ obj = lib.stm_next_to_finalize(qindex)
+ return res
+
+
+
+ def test_no_finalizer(self):
+ self.start_transaction()
+ lp1 = stm_allocate(48)
+ stm_major_collect()
+ assert not self.get_queues_triggered()
+
+ def test_no_finalizer_in_minor_collection(self):
+ self.start_transaction()
+ lp1 = stm_allocate(48)
+ lib.stm_enable_finalizer(0, lp1)
+ stm_minor_collect()
+ assert not self.get_queues_triggered()
+
+ def test_finalizer_in_major_collection(self):
+ self.start_transaction()
+ for repeat in range(4):
+ lp1 = stm_allocate(48)
+ lp2 = stm_allocate(48)
+ lp3 = stm_allocate(48)
+ if repeat % 2 == 0: # register on young
+ lib.stm_enable_finalizer(0, lp1)
+ lib.stm_enable_finalizer(1, lp2)
+ lib.stm_enable_finalizer(1, lp3)
+ assert not self.get_queues_triggered()
+ self.push_roots([lp1, lp2, lp3])
+ stm_minor_collect()
+ lp1, lp2, lp3 = self.pop_roots()
+ if repeat % 2 == 1: # register on old
+ lib.stm_enable_finalizer(0, lp1)
+ lib.stm_enable_finalizer(1, lp2)
+ lib.stm_enable_finalizer(1, lp3)
+ print repeat, lp1, lp2, lp3
+ stm_major_collect()
+ assert {0, 1} == self.get_queues_triggered()
+ assert [lp1] == self.get_to_finalize(0)
+ assert [lp2, lp3] == self.get_to_finalize(1)
+
+ def test_finalizer_from_other_thread(self):
+ self.start_transaction()
+ lp1 = stm_allocate(48)
+ lib.stm_enable_finalizer(0, lp1)
+ stm_set_char(lp1, 'H')
+ self.expect_content_character = 'H'
+ self.push_root(lp1)
+ stm_minor_collect()
+ lp1 = self.pop_root()
+ print lp1
+ #
+ self.switch(1)
+ self.start_transaction()
+ stm_major_collect()
+ assert not self.get_queues_triggered()
+ #
+ self.switch(0)
+ assert {0} == self.get_queues_triggered()
+ assert [lp1] == self.get_to_finalize(0)
+
+ def test_finalizer_ordering(self):
+ self.start_transaction()
+ lp1 = stm_allocate_refs(1)
+ lp2 = stm_allocate_refs(1)
+ lp3 = stm_allocate_refs(1)
+ lib.stm_enable_finalizer(1, lp1)
+ lib.stm_enable_finalizer(1, lp2)
+ lib.stm_enable_finalizer(1, lp3)
+
+ print lp1, lp2, lp3
+ stm_set_ref(lp3, 0, lp1)
+ stm_set_ref(lp1, 0, lp2)
+
+ self.push_roots([lp1, lp2, lp3])
+ stm_minor_collect()
+ lp1, lp2, lp3 = self.pop_roots()
+
+ stm_major_collect()
+ assert {1} == self.get_queues_triggered()
+ assert [lp3] == self.get_to_finalize(1)
+
+ def test_finalizer_extra_transaction(self):
+ self.start_transaction()
+ lp1 = stm_allocate(32)
+ lib.stm_enable_finalizer(0, lp1)
+ self.push_root(lp1)
+ self.commit_transaction()
+
+ self.start_transaction()
+ lp1b = self.pop_root()
+ # assert lp1b == lp1 <- lp1 can be in nursery now
+ assert not self.get_queues_triggered()
+ self.commit_transaction() # finalizer-obj moved to global queue
+ assert not self.get_queues_triggered()
+
+ self.start_transaction()
+ stm_major_collect()
+ assert not self.get_queues_triggered()
+ self.commit_transaction() # invoke finalizers
+ self.start_transaction()
+ assert {0} == self.get_queues_triggered()
+ assert [lp1b] == self.get_to_finalize(0)
+
+ def test_run_cb_for_all_threads(self):
+ self.start_transaction()
+ lp1 = stm_allocate(48)
+ lib.stm_enable_finalizer(0, lp1)
+ print lp1
+ #
+ self.switch(1)
+ self.start_transaction()
+ lp2 = stm_allocate(56)
+ lib.stm_enable_finalizer(1, lp2)
+ print lp2
+
+ assert not self.get_queues_triggered()
+ stm_major_collect()
+ assert len(self.get_to_finalize(1)) == 1
+ self.switch(0)
+ 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
+ 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)
+ print lp1, lp2, lp3
+ stm_major_collect()
+
+ def test_new_objects_w_finalizers(self):
+ self.switch(2)
+ self.start_transaction()
+ lp1 = stm_allocate_refs(3)
+ lp2 = stm_allocate_refs(3)
+ lib.stm_enable_finalizer(0, lp1)
+ lib.stm_enable_finalizer(0, lp2)
+ stm_set_ref(lp1, 0, lp2)
+
+ self.push_root(lp1)
+ stm_minor_collect()
+ lp1 = self.pop_root()
+ lp2 = stm_get_ref(lp1, 0)
+ # lp1, lp2 have WB_EXECUTED objs
+
+ assert not self.get_queues_triggered()
+ stm_major_collect()
+ assert [lp1] == self.get_to_finalize(0)
+
class TestOldStyleRegularFinalizer(BaseTest):
expect_content_character = None
@@ -159,6 +344,7 @@
def trigger():
# triggers not needed for oldstyle-finalizer tests
assert False
+ self._trigger_keepalive = trigger
triggers = ffi.new("stm_finalizer_trigger_fn*", trigger)
lib.stm_setup_finalizer_queues(1, triggers)
#
_______________________________________________
pypy-commit mailing list
[email protected]
https://mail.python.org/mailman/listinfo/pypy-commit