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