From 988f6d72c55a041b9b382143e2548571a3510abc Mon Sep 17 00:00:00 2001 From: Neels Hofmeyr Date: Fri, 4 Oct 2019 20:37:17 +0200 Subject: add osmo_fsm_set_dealloc_ctx(), to help with use-after-free This is a simpler and more general solution to the problem so far solved by osmo_fsm_term_safely(true). This extends use-after-free fixes to arbitrary functions, not only FSM instances during termination. The aim is to defer talloc_free() until back in the main loop. Rationale: I discovered an osmo-msc use-after-free crash from an invalid message, caused by this pattern: void event_action() { osmo_fsm_inst_dispatch(foo, FOO_EVENT, NULL); osmo_fsm_inst_dispatch(bar, BAR_EVENT, NULL); } Usually, FOO_EVENT takes successful action, and afterwards we also notify bar. However, in this particular case, FOO_EVENT caused failure, and the immediate error handling directly terminated and deallocated bar. In such a case, dispatching BAR_EVENT causes a use-after-free; this constituted a DoS vector just from sending messages that cause *any* failure during the first event dispatch. Instead, when this is enabled, we do not deallocate 'foo' until event_action() has returned back to the main loop. Test: duplicate fsm_dealloc_test.c using this, and print the number of items deallocated in each test loop, to ensure the feature works. We also verify that the deallocation safety works simply by fsm_dealloc_test.c not crashing. We should probably follow up by refusing event dispatch and state transitions for FSM instances that are terminating or already terminated: see I0adc13a1a998e953b6c850efa2761350dd07e03a. Change-Id: Ief4dba9ea587c9b4aea69993e965fbb20fb80e78 --- tests/fsm/fsm_dealloc_test.err | 3408 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 3408 insertions(+) (limited to 'tests/fsm/fsm_dealloc_test.err') diff --git a/tests/fsm/fsm_dealloc_test.err b/tests/fsm/fsm_dealloc_test.err index d12c5aa3..973f3d42 100644 --- a/tests/fsm/fsm_dealloc_test.err +++ b/tests/fsm/fsm_dealloc_test.err @@ -1,3 +1,6 @@ + + +test_osmo_fsm_term_safely() DLGLOBAL DEBUG scene_alloc() DLGLOBAL DEBUG test(root){alive}: Allocated DLGLOBAL DEBUG test(root){alive}: Allocated @@ -3250,3 +3253,3408 @@ DLGLOBAL DEBUG test(other){alive}: Deallocated, including all deferred deallocat DLGLOBAL DEBUG 0 (-) DLGLOBAL DEBUG --- after destroy-event cascade: DLGLOBAL DEBUG --- all deallocated. + + +test_osmo_fsm_term_safely() done + + +test_osmo_fsm_set_dealloc_ctx() +DLGLOBAL DEBUG scene_alloc() +DLGLOBAL DEBUG test(root){alive}: Allocated +DLGLOBAL DEBUG test(root){alive}: Allocated +DLGLOBAL DEBUG test(root){alive}: is child of test(root) +DLGLOBAL DEBUG test(_branch0){alive}: Allocated +DLGLOBAL DEBUG test(_branch0){alive}: is child of test(_branch0) +DLGLOBAL DEBUG test(_branch0){alive}: Allocated +DLGLOBAL DEBUG test(_branch0){alive}: is child of test(_branch0) +DLGLOBAL DEBUG test(root){alive}: Allocated +DLGLOBAL DEBUG test(root){alive}: is child of test(root) +DLGLOBAL DEBUG test(_branch1){alive}: Allocated +DLGLOBAL DEBUG test(_branch1){alive}: is child of test(_branch1) +DLGLOBAL DEBUG test(_branch1){alive}: Allocated +DLGLOBAL DEBUG test(_branch1){alive}: is child of test(_branch1) +DLGLOBAL DEBUG test(other){alive}: Allocated +DLGLOBAL DEBUG test(_branch0){alive}: _branch0.other[0] = other +DLGLOBAL DEBUG test(other){alive}: other.other[0] = _branch0 +DLGLOBAL DEBUG test(__twig0a){alive}: __twig0a.other[0] = other +DLGLOBAL DEBUG test(other){alive}: other.other[1] = __twig0a +DLGLOBAL DEBUG test(_branch1){alive}: _branch1.other[0] = other +DLGLOBAL DEBUG test(other){alive}: other.other[1] = _branch1 +DLGLOBAL DEBUG test(__twig1a){alive}: __twig1a.other[0] = root +DLGLOBAL DEBUG test(root){alive}: root.other[0] = __twig1a +DLGLOBAL DEBUG ------ before term cascade, got: +DLGLOBAL DEBUG root +DLGLOBAL DEBUG _branch0 +DLGLOBAL DEBUG __twig0a +DLGLOBAL DEBUG __twig0b +DLGLOBAL DEBUG _branch1 +DLGLOBAL DEBUG __twig1a +DLGLOBAL DEBUG __twig1b +DLGLOBAL DEBUG other +DLGLOBAL DEBUG --- +DLGLOBAL DEBUG --- term at root +DLGLOBAL DEBUG test(root){alive}: Terminating (cause = OSMO_FSM_TERM_REGULAR) +DLGLOBAL DEBUG test(root){alive}: pre_term() +DLGLOBAL DEBUG test(_branch1){alive}: Terminating (cause = OSMO_FSM_TERM_PARENT) +DLGLOBAL DEBUG test(_branch1){alive}: pre_term() +DLGLOBAL DEBUG test(__twig1b){alive}: Terminating (cause = OSMO_FSM_TERM_PARENT) +DLGLOBAL DEBUG test(__twig1b){alive}: pre_term() +DLGLOBAL DEBUG test(__twig1b){alive}: Removing from parent test(_branch1) +DLGLOBAL DEBUG 1 (__twig1b.cleanup()) +DLGLOBAL DEBUG test(__twig1b){alive}: cleanup() +DLGLOBAL DEBUG test(__twig1b){alive}: scene forgets __twig1b +DLGLOBAL DEBUG test(_branch1){alive}: Received Event EV_CHILD_GONE +DLGLOBAL DEBUG 2 (__twig1b.cleanup(),_branch1.alive()) +DLGLOBAL DEBUG test(_branch1){alive}: alive(EV_CHILD_GONE) +DLGLOBAL DEBUG 3 (__twig1b.cleanup(),_branch1.alive(),_branch1.child_gone()) +DLGLOBAL DEBUG test(_branch1){alive}: EV_CHILD_GONE: Dropped reference _branch1.child[1] = __twig1b +DLGLOBAL DEBUG test(_branch1){alive}: still exists: child[0] +DLGLOBAL DEBUG 2 (__twig1b.cleanup(),_branch1.alive()) +DLGLOBAL DEBUG 1 (__twig1b.cleanup()) +DLGLOBAL DEBUG test(__twig1b){alive}: cleanup() done +DLGLOBAL DEBUG 0 (-) +DLGLOBAL DEBUG test(__twig1b){alive}: Freeing instance +DLGLOBAL DEBUG test(__twig1b){alive}: Deallocated +DLGLOBAL DEBUG test(__twig1a){alive}: Terminating (cause = OSMO_FSM_TERM_PARENT) +DLGLOBAL DEBUG test(__twig1a){alive}: pre_term() +DLGLOBAL DEBUG test(__twig1a){alive}: Removing from parent test(_branch1) +DLGLOBAL DEBUG 1 (__twig1a.cleanup()) +DLGLOBAL DEBUG test(__twig1a){alive}: cleanup() +DLGLOBAL DEBUG test(__twig1a){alive}: scene forgets __twig1a +DLGLOBAL DEBUG test(__twig1a){alive}: removing reference __twig1a.other[0] -> root +DLGLOBAL DEBUG test(root){alive}: Received Event EV_OTHER_GONE +DLGLOBAL DEBUG 2 (__twig1a.cleanup(),root.alive()) +DLGLOBAL DEBUG test(root){alive}: alive(EV_OTHER_GONE) +DLGLOBAL DEBUG 3 (__twig1a.cleanup(),root.alive(),root.other_gone()) +DLGLOBAL DEBUG test(root){alive}: EV_OTHER_GONE: Dropped reference root.other[0] = __twig1a +DLGLOBAL DEBUG 2 (__twig1a.cleanup(),root.alive()) +DLGLOBAL DEBUG test(root){alive}: Ignoring trigger to terminate: already terminating +DLGLOBAL DEBUG 1 (__twig1a.cleanup()) +DLGLOBAL DEBUG test(_branch1){alive}: Received Event EV_CHILD_GONE +DLGLOBAL DEBUG 2 (__twig1a.cleanup(),_branch1.alive()) +DLGLOBAL DEBUG test(_branch1){alive}: alive(EV_CHILD_GONE) +DLGLOBAL DEBUG 3 (__twig1a.cleanup(),_branch1.alive(),_branch1.child_gone()) +DLGLOBAL DEBUG test(_branch1){alive}: EV_CHILD_GONE: Dropped reference _branch1.child[0] = __twig1a +DLGLOBAL DEBUG test(_branch1){alive}: No more children +DLGLOBAL DEBUG 2 (__twig1a.cleanup(),_branch1.alive()) +DLGLOBAL DEBUG test(_branch1){alive}: Ignoring trigger to terminate: already terminating +DLGLOBAL DEBUG 1 (__twig1a.cleanup()) +DLGLOBAL DEBUG test(__twig1a){alive}: cleanup() done +DLGLOBAL DEBUG 0 (-) +DLGLOBAL DEBUG test(__twig1a){alive}: Freeing instance +DLGLOBAL DEBUG test(__twig1a){alive}: Deallocated +DLGLOBAL DEBUG test(_branch1){alive}: Removing from parent test(root) +DLGLOBAL DEBUG 1 (_branch1.cleanup()) +DLGLOBAL DEBUG test(_branch1){alive}: cleanup() +DLGLOBAL DEBUG test(_branch1){alive}: scene forgets _branch1 +DLGLOBAL DEBUG test(_branch1){alive}: removing reference _branch1.other[0] -> other +DLGLOBAL DEBUG test(other){alive}: Received Event EV_OTHER_GONE +DLGLOBAL DEBUG 2 (_branch1.cleanup(),other.alive()) +DLGLOBAL DEBUG test(other){alive}: alive(EV_OTHER_GONE) +DLGLOBAL DEBUG 3 (_branch1.cleanup(),other.alive(),other.other_gone()) +DLGLOBAL DEBUG test(other){alive}: EV_OTHER_GONE: Dropped reference other.other[1] = _branch1 +DLGLOBAL DEBUG 2 (_branch1.cleanup(),other.alive()) +DLGLOBAL DEBUG test(other){alive}: Terminating (cause = OSMO_FSM_TERM_REGULAR) +DLGLOBAL DEBUG test(other){alive}: pre_term() +DLGLOBAL DEBUG 3 (_branch1.cleanup(),other.alive(),other.cleanup()) +DLGLOBAL DEBUG test(other){alive}: cleanup() +DLGLOBAL DEBUG test(other){alive}: scene forgets other +DLGLOBAL DEBUG test(other){alive}: removing reference other.other[0] -> _branch0 +DLGLOBAL DEBUG test(_branch0){alive}: Received Event EV_OTHER_GONE +DLGLOBAL DEBUG 4 (_branch1.cleanup(),other.alive(),other.cleanup(),_branch0.alive()) +DLGLOBAL DEBUG test(_branch0){alive}: alive(EV_OTHER_GONE) +DLGLOBAL DEBUG 5 (_branch1.cleanup(),other.alive(),other.cleanup(),_branch0.alive(),_branch0.other_gone()) +DLGLOBAL DEBUG test(_branch0){alive}: EV_OTHER_GONE: Dropped reference _branch0.other[0] = other +DLGLOBAL DEBUG 4 (_branch1.cleanup(),other.alive(),other.cleanup(),_branch0.alive()) +DLGLOBAL DEBUG test(_branch0){alive}: Terminating (cause = OSMO_FSM_TERM_REGULAR) +DLGLOBAL DEBUG test(_branch0){alive}: pre_term() +DLGLOBAL DEBUG test(__twig0b){alive}: Terminating (cause = OSMO_FSM_TERM_PARENT) +DLGLOBAL DEBUG test(__twig0b){alive}: pre_term() +DLGLOBAL DEBUG test(__twig0b){alive}: Removing from parent test(_branch0) +DLGLOBAL DEBUG 5 (_branch1.cleanup(),other.alive(),other.cleanup(),_branch0.alive(),__twig0b.cleanup()) +DLGLOBAL DEBUG test(__twig0b){alive}: cleanup() +DLGLOBAL DEBUG test(__twig0b){alive}: scene forgets __twig0b +DLGLOBAL DEBUG test(_branch0){alive}: Received Event EV_CHILD_GONE +DLGLOBAL DEBUG 6 (_branch1.cleanup(),other.alive(),other.cleanup(),2*_branch0.alive(),__twig0b.cleanup()) +DLGLOBAL DEBUG test(_branch0){alive}: alive(EV_CHILD_GONE) +DLGLOBAL DEBUG 7 (_branch1.cleanup(),other.alive(),other.cleanup(),2*_branch0.alive(),__twig0b.cleanup(),_branch0.child_gone()) +DLGLOBAL DEBUG test(_branch0){alive}: EV_CHILD_GONE: Dropped reference _branch0.child[1] = __twig0b +DLGLOBAL DEBUG test(_branch0){alive}: still exists: child[0] +DLGLOBAL DEBUG 6 (_branch1.cleanup(),other.alive(),other.cleanup(),2*_branch0.alive(),__twig0b.cleanup()) +DLGLOBAL DEBUG 5 (_branch1.cleanup(),other.alive(),other.cleanup(),_branch0.alive(),__twig0b.cleanup()) +DLGLOBAL DEBUG test(__twig0b){alive}: cleanup() done +DLGLOBAL DEBUG 4 (_branch1.cleanup(),other.alive(),other.cleanup(),_branch0.alive()) +DLGLOBAL DEBUG test(__twig0b){alive}: Freeing instance +DLGLOBAL DEBUG test(__twig0b){alive}: Deallocated +DLGLOBAL DEBUG test(__twig0a){alive}: Terminating (cause = OSMO_FSM_TERM_PARENT) +DLGLOBAL DEBUG test(__twig0a){alive}: pre_term() +DLGLOBAL DEBUG test(__twig0a){alive}: Removing from parent test(_branch0) +DLGLOBAL DEBUG 5 (_branch1.cleanup(),other.alive(),other.cleanup(),_branch0.alive(),__twig0a.cleanup()) +DLGLOBAL DEBUG test(__twig0a){alive}: cleanup() +DLGLOBAL DEBUG test(__twig0a){alive}: scene forgets __twig0a +DLGLOBAL DEBUG test(__twig0a){alive}: removing reference __twig0a.other[0] -> other +DLGLOBAL DEBUG test(other){alive}: Received Event EV_OTHER_GONE +DLGLOBAL DEBUG 6 (_branch1.cleanup(),2*other.alive(),other.cleanup(),_branch0.alive(),__twig0a.cleanup()) +DLGLOBAL DEBUG test(other){alive}: alive(EV_OTHER_GONE) +DLGLOBAL DEBUG 7 (_branch1.cleanup(),2*other.alive(),other.cleanup(),_branch0.alive(),__twig0a.cleanup(),other.other_gone()) +DLGLOBAL DEBUG 6 (_branch1.cleanup(),2*other.alive(),other.cleanup(),_branch0.alive(),__twig0a.cleanup()) +DLGLOBAL DEBUG 5 (_branch1.cleanup(),other.alive(),other.cleanup(),_branch0.alive(),__twig0a.cleanup()) +DLGLOBAL DEBUG test(_branch0){alive}: Received Event EV_CHILD_GONE +DLGLOBAL DEBUG 6 (_branch1.cleanup(),other.alive(),other.cleanup(),2*_branch0.alive(),__twig0a.cleanup()) +DLGLOBAL DEBUG test(_branch0){alive}: alive(EV_CHILD_GONE) +DLGLOBAL DEBUG 7 (_branch1.cleanup(),other.alive(),other.cleanup(),2*_branch0.alive(),__twig0a.cleanup(),_branch0.child_gone()) +DLGLOBAL DEBUG test(_branch0){alive}: EV_CHILD_GONE: Dropped reference _branch0.child[0] = __twig0a +DLGLOBAL DEBUG test(_branch0){alive}: No more children +DLGLOBAL DEBUG 6 (_branch1.cleanup(),other.alive(),other.cleanup(),2*_branch0.alive(),__twig0a.cleanup()) +DLGLOBAL DEBUG test(_branch0){alive}: Ignoring trigger to terminate: already terminating +DLGLOBAL DEBUG 5 (_branch1.cleanup(),other.alive(),other.cleanup(),_branch0.alive(),__twig0a.cleanup()) +DLGLOBAL DEBUG test(__twig0a){alive}: cleanup() done +DLGLOBAL DEBUG 4 (_branch1.cleanup(),other.alive(),other.cleanup(),_branch0.alive()) +DLGLOBAL DEBUG test(__twig0a){alive}: Freeing instance +DLGLOBAL DEBUG test(__twig0a){alive}: Deallocated +DLGLOBAL DEBUG test(_branch0){alive}: Removing from parent test(root) +DLGLOBAL DEBUG 5 (_branch1.cleanup(),other.alive(),other.cleanup(),_branch0.alive(),_branch0.cleanup()) +DLGLOBAL DEBUG test(_branch0){alive}: cleanup() +DLGLOBAL DEBUG test(_branch0){alive}: scene forgets _branch0 +DLGLOBAL DEBUG test(root){alive}: Received Event EV_CHILD_GONE +DLGLOBAL DEBUG 6 (_branch1.cleanup(),other.alive(),other.cleanup(),_branch0.alive(),_branch0.cleanup(),root.alive()) +DLGLOBAL DEBUG test(root){alive}: alive(EV_CHILD_GONE) +DLGLOBAL DEBUG 7 (_branch1.cleanup(),other.alive(),other.cleanup(),_branch0.alive(),_branch0.cleanup(),root.alive(),root.child_gone()) +DLGLOBAL DEBUG test(root){alive}: EV_CHILD_GONE: Dropped reference root.child[0] = _branch0 +DLGLOBAL DEBUG test(root){alive}: still exists: child[1] +DLGLOBAL DEBUG 6 (_branch1.cleanup(),other.alive(),other.cleanup(),_branch0.alive(),_branch0.cleanup(),root.alive()) +DLGLOBAL DEBUG 5 (_branch1.cleanup(),other.alive(),other.cleanup(),_branch0.alive(),_branch0.cleanup()) +DLGLOBAL DEBUG test(_branch0){alive}: cleanup() done +DLGLOBAL DEBUG 4 (_branch1.cleanup(),other.alive(),other.cleanup(),_branch0.alive()) +DLGLOBAL DEBUG test(_branch0){alive}: Freeing instance +DLGLOBAL DEBUG test(_branch0){alive}: Deallocated +DLGLOBAL DEBUG test(root){alive}: Received Event EV_CHILD_GONE +DLGLOBAL DEBUG 5 (_branch1.cleanup(),other.alive(),other.cleanup(),_branch0.alive(),root.alive()) +DLGLOBAL DEBUG test(root){alive}: alive(EV_CHILD_GONE) +DLGLOBAL DEBUG test(root){alive}: EV_CHILD_GONE with NULL data, must be a parent_term event. Ignore. +DLGLOBAL DEBUG 4 (_branch1.cleanup(),other.alive(),other.cleanup(),_branch0.alive()) +DLGLOBAL DEBUG 3 (_branch1.cleanup(),other.alive(),other.cleanup()) +DLGLOBAL DEBUG test(other){alive}: cleanup() done +DLGLOBAL DEBUG 2 (_branch1.cleanup(),other.alive()) +DLGLOBAL DEBUG test(other){alive}: Freeing instance +DLGLOBAL DEBUG test(other){alive}: Deallocated +DLGLOBAL DEBUG 1 (_branch1.cleanup()) +DLGLOBAL DEBUG test(root){alive}: Received Event EV_CHILD_GONE +DLGLOBAL DEBUG 2 (_branch1.cleanup(),root.alive()) +DLGLOBAL DEBUG test(root){alive}: alive(EV_CHILD_GONE) +DLGLOBAL DEBUG 3 (_branch1.cleanup(),root.alive(),root.child_gone()) +DLGLOBAL DEBUG test(root){alive}: EV_CHILD_GONE: Dropped reference root.child[1] = _branch1 +DLGLOBAL DEBUG test(root){alive}: No more children +DLGLOBAL DEBUG 2 (_branch1.cleanup(),root.alive()) +DLGLOBAL DEBUG test(root){alive}: Ignoring trigger to terminate: already terminating +DLGLOBAL DEBUG 1 (_branch1.cleanup()) +DLGLOBAL DEBUG test(_branch1){alive}: cleanup() done +DLGLOBAL DEBUG 0 (-) +DLGLOBAL DEBUG test(_branch1){alive}: Freeing instance +DLGLOBAL DEBUG test(_branch1){alive}: Deallocated +DLGLOBAL DEBUG 1 (root.cleanup()) +DLGLOBAL DEBUG test(root){alive}: cleanup() +DLGLOBAL DEBUG test(root){alive}: scene forgets root +DLGLOBAL DEBUG test(root){alive}: cleanup() done +DLGLOBAL DEBUG 0 (-) +DLGLOBAL DEBUG test(root){alive}: Freeing instance +DLGLOBAL DEBUG test(root){alive}: Deallocated +DLGLOBAL DEBUG --- after term cascade: +DLGLOBAL DEBUG --- all deallocated. +*** loop_ctx contains 33 blocks, deallocating. +DLGLOBAL DEBUG scene_alloc() +DLGLOBAL DEBUG test(root){alive}: Allocated +DLGLOBAL DEBUG test(root){alive}: Allocated +DLGLOBAL DEBUG test(root){alive}: is child of test(root) +DLGLOBAL DEBUG test(_branch0){alive}: Allocated +DLGLOBAL DEBUG test(_branch0){alive}: is child of test(_branch0) +DLGLOBAL DEBUG test(_branch0){alive}: Allocated +DLGLOBAL DEBUG test(_branch0){alive}: is child of test(_branch0) +DLGLOBAL DEBUG test(root){alive}: Allocated +DLGLOBAL DEBUG test(root){alive}: is child of test(root) +DLGLOBAL DEBUG test(_branch1){alive}: Allocated +DLGLOBAL DEBUG test(_branch1){alive}: is child of test(_branch1) +DLGLOBAL DEBUG test(_branch1){alive}: Allocated +DLGLOBAL DEBUG test(_branch1){alive}: is child of test(_branch1) +DLGLOBAL DEBUG test(other){alive}: Allocated +DLGLOBAL DEBUG test(_branch0){alive}: _branch0.other[0] = other +DLGLOBAL DEBUG test(other){alive}: other.other[0] = _branch0 +DLGLOBAL DEBUG test(__twig0a){alive}: __twig0a.other[0] = other +DLGLOBAL DEBUG test(other){alive}: other.other[1] = __twig0a +DLGLOBAL DEBUG test(_branch1){alive}: _branch1.other[0] = other +DLGLOBAL DEBUG test(other){alive}: other.other[1] = _branch1 +DLGLOBAL DEBUG test(__twig1a){alive}: __twig1a.other[0] = root +DLGLOBAL DEBUG test(root){alive}: root.other[0] = __twig1a +DLGLOBAL DEBUG ------ before destroy-event cascade, got: +DLGLOBAL DEBUG root +DLGLOBAL DEBUG _branch0 +DLGLOBAL DEBUG __twig0a +DLGLOBAL DEBUG __twig0b +DLGLOBAL DEBUG _branch1 +DLGLOBAL DEBUG __twig1a +DLGLOBAL DEBUG __twig1b +DLGLOBAL DEBUG other +DLGLOBAL DEBUG --- +DLGLOBAL DEBUG --- destroy-event at root +DLGLOBAL DEBUG test(root){alive}: Received Event EV_DESTROY +DLGLOBAL DEBUG 1 (root.alive()) +DLGLOBAL DEBUG test(root){alive}: alive(EV_DESTROY) +DLGLOBAL DEBUG test(root){alive}: Terminating (cause = OSMO_FSM_TERM_REGULAR) +DLGLOBAL DEBUG test(root){alive}: pre_term() +DLGLOBAL DEBUG test(_branch1){alive}: Terminating (cause = OSMO_FSM_TERM_PARENT) +DLGLOBAL DEBUG test(_branch1){alive}: pre_term() +DLGLOBAL DEBUG test(__twig1b){alive}: Terminating (cause = OSMO_FSM_TERM_PARENT) +DLGLOBAL DEBUG test(__twig1b){alive}: pre_term() +DLGLOBAL DEBUG test(__twig1b){alive}: Removing from parent test(_branch1) +DLGLOBAL DEBUG 2 (root.alive(),__twig1b.cleanup()) +DLGLOBAL DEBUG test(__twig1b){alive}: cleanup() +DLGLOBAL DEBUG test(__twig1b){alive}: scene forgets __twig1b +DLGLOBAL DEBUG test(_branch1){alive}: Received Event EV_CHILD_GONE +DLGLOBAL DEBUG 3 (root.alive(),__twig1b.cleanup(),_branch1.alive()) +DLGLOBAL DEBUG test(_branch1){alive}: alive(EV_CHILD_GONE) +DLGLOBAL DEBUG 4 (root.alive(),__twig1b.cleanup(),_branch1.alive(),_branch1.child_gone()) +DLGLOBAL DEBUG test(_branch1){alive}: EV_CHILD_GONE: Dropped reference _branch1.child[1] = __twig1b +DLGLOBAL DEBUG test(_branch1){alive}: still exists: child[0] +DLGLOBAL DEBUG 3 (root.alive(),__twig1b.cleanup(),_branch1.alive()) +DLGLOBAL DEBUG 2 (root.alive(),__twig1b.cleanup()) +DLGLOBAL DEBUG test(__twig1b){alive}: cleanup() done +DLGLOBAL DEBUG 1 (root.alive()) +DLGLOBAL DEBUG test(__twig1b){alive}: Freeing instance +DLGLOBAL DEBUG test(__twig1b){alive}: Deallocated +DLGLOBAL DEBUG test(__twig1a){alive}: Terminating (cause = OSMO_FSM_TERM_PARENT) +DLGLOBAL DEBUG test(__twig1a){alive}: pre_term() +DLGLOBAL DEBUG test(__twig1a){alive}: Removing from parent test(_branch1) +DLGLOBAL DEBUG 2 (root.alive(),__twig1a.cleanup()) +DLGLOBAL DEBUG test(__twig1a){alive}: cleanup() +DLGLOBAL DEBUG test(__twig1a){alive}: scene forgets __twig1a +DLGLOBAL DEBUG test(__twig1a){alive}: removing reference __twig1a.other[0] -> root +DLGLOBAL DEBUG test(root){alive}: Received Event EV_OTHER_GONE +DLGLOBAL DEBUG 3 (2*root.alive(),__twig1a.cleanup()) +DLGLOBAL DEBUG test(root){alive}: alive(EV_OTHER_GONE) +DLGLOBAL DEBUG 4 (2*root.alive(),__twig1a.cleanup(),root.other_gone()) +DLGLOBAL DEBUG test(root){alive}: EV_OTHER_GONE: Dropped reference root.other[0] = __twig1a +DLGLOBAL DEBUG 3 (2*root.alive(),__twig1a.cleanup()) +DLGLOBAL DEBUG test(root){alive}: Ignoring trigger to terminate: already terminating +DLGLOBAL DEBUG 2 (root.alive(),__twig1a.cleanup()) +DLGLOBAL DEBUG test(_branch1){alive}: Received Event EV_CHILD_GONE +DLGLOBAL DEBUG 3 (root.alive(),__twig1a.cleanup(),_branch1.alive()) +DLGLOBAL DEBUG test(_branch1){alive}: alive(EV_CHILD_GONE) +DLGLOBAL DEBUG 4 (root.alive(),__twig1a.cleanup(),_branch1.alive(),_branch1.child_gone()) +DLGLOBAL DEBUG test(_branch1){alive}: EV_CHILD_GONE: Dropped reference _branch1.child[0] = __twig1a +DLGLOBAL DEBUG test(_branch1){alive}: No more children +DLGLOBAL DEBUG 3 (root.alive(),__twig1a.cleanup(),_branch1.alive()) +DLGLOBAL DEBUG test(_branch1){alive}: Ignoring trigger to terminate: already terminating +DLGLOBAL DEBUG 2 (root.alive(),__twig1a.cleanup()) +DLGLOBAL DEBUG test(__twig1a){alive}: cleanup() done +DLGLOBAL DEBUG 1 (root.alive()) +DLGLOBAL DEBUG test(__twig1a){alive}: Freeing instance +DLGLOBAL DEBUG test(__twig1a){alive}: Deallocated +DLGLOBAL DEBUG test(_branch1){alive}: Removing from parent test(root) +DLGLOBAL DEBUG 2 (root.alive(),_branch1.cleanup()) +DLGLOBAL DEBUG test(_branch1){alive}: cleanup() +DLGLOBAL DEBUG test(_branch1){alive}: scene forgets _branch1 +DLGLOBAL DEBUG test(_branch1){alive}: removing reference _branch1.other[0] -> other +DLGLOBAL DEBUG test(other){alive}: Received Event EV_OTHER_GONE +DLGLOBAL DEBUG 3 (root.alive(),_branch1.cleanup(),other.alive()) +DLGLOBAL DEBUG test(other){alive}: alive(EV_OTHER_GONE) +DLGLOBAL DEBUG 4 (root.alive(),_branch1.cleanup(),other.alive(),other.other_gone()) +DLGLOBAL DEBUG test(other){alive}: EV_OTHER_GONE: Dropped reference other.other[1] = _branch1 +DLGLOBAL DEBUG 3 (root.alive(),_branch1.cleanup(),other.alive()) +DLGLOBAL DEBUG test(other){alive}: Terminating (cause = OSMO_FSM_TERM_REGULAR) +DLGLOBAL DEBUG test(other){alive}: pre_term() +DLGLOBAL DEBUG 4 (root.alive(),_branch1.cleanup(),other.alive(),other.cleanup()) +DLGLOBAL DEBUG test(other){alive}: cleanup() +DLGLOBAL DEBUG test(other){alive}: scene forgets other +DLGLOBAL DEBUG test(other){alive}: removing reference other.other[0] -> _branch0 +DLGLOBAL DEBUG test(_branch0){alive}: Received Event EV_OTHER_GONE +DLGLOBAL DEBUG 5 (root.alive(),_branch1.cleanup(),other.alive(),other.cleanup(),_branch0.alive()) +DLGLOBAL DEBUG test(_branch0){alive}: alive(EV_OTHER_GONE) +DLGLOBAL DEBUG 6 (root.alive(),_branch1.cleanup(),other.alive(),other.cleanup(),_branch0.alive(),_branch0.other_gone()) +DLGLOBAL DEBUG test(_branch0){alive}: EV_OTHER_GONE: Dropped reference _branch0.other[0] = other +DLGLOBAL DEBUG 5 (root.alive(),_branch1.cleanup(),other.alive(),other.cleanup(),_branch0.alive()) +DLGLOBAL DEBUG test(_branch0){alive}: Terminating (cause = OSMO_FSM_TERM_REGULAR) +DLGLOBAL DEBUG test(_branch0){alive}: pre_term() +DLGLOBAL DEBUG test(__twig0b){alive}: Terminating (cause = OSMO_FSM_TERM_PARENT) +DLGLOBAL DEBUG test(__twig0b){alive}: pre_term() +DLGLOBAL DEBUG test(__twig0b){alive}: Removing from parent test(_branch0) +DLGLOBAL DEBUG 6 (root.alive(),_branch1.cleanup(),other.alive(),other.cleanup(),_branch0.alive(),__twig0b.cleanup()) +DLGLOBAL DEBUG test(__twig0b){alive}: cleanup() +DLGLOBAL DEBUG test(__twig0b){alive}: scene forgets __twig0b +DLGLOBAL DEBUG test(_branch0){alive}: Received Event EV_CHILD_GONE +DLGLOBAL DEBUG 7 (root.alive(),_branch1.cleanup(),other.alive(),other.cleanup(),2*_branch0.alive(),__twig0b.cleanup()) +DLGLOBAL DEBUG test(_branch0){alive}: alive(EV_CHILD_GONE) +DLGLOBAL DEBUG 8 (root.alive(),_branch1.cleanup(),other.alive(),other.cleanup(),2*_branch0.alive(),__twig0b.cleanup(),_branch0.child_gone()) +DLGLOBAL DEBUG test(_branch0){alive}: EV_CHILD_GONE: Dropped reference _branch0.child[1] = __twig0b +DLGLOBAL DEBUG test(_branch0){alive}: still exists: child[0] +DLGLOBAL DEBUG 7 (root.alive(),_branch1.cleanup(),other.alive(),other.cleanup(),2*_branch0.alive(),__twig0b.cleanup()) +DLGLOBAL DEBUG 6 (root.alive(),_branch1.cleanup(),other.alive(),other.cleanup(),_branch0.alive(),__twig0b.cleanup()) +DLGLOBAL DEBUG test(__twig0b){alive}: cleanup() done +DLGLOBAL DEBUG 5 (root.alive(),_branch1.cleanup(),other.alive(),other.cleanup(),_branch0.alive()) +DLGLOBAL DEBUG test(__twig0b){alive}: Freeing instance +DLGLOBAL DEBUG test(__twig0b){alive}: Deallocated +DLGLOBAL DEBUG test(__twig0a){alive}: Terminating (cause = OSMO_FSM_TERM_PARENT) +DLGLOBAL DEBUG test(__twig0a){alive}: pre_term() +DLGLOBAL DEBUG test(__twig0a){alive}: Removing from parent test(_branch0) +DLGLOBAL DEBUG 6 (root.alive(),_branch1.cleanup(),other.alive(),other.cleanup(),_branch0.alive(),__twig0a.cleanup()) +DLGLOBAL DEBUG test(__twig0a){alive}: cleanup() +DLGLOBAL DEBUG test(__twig0a){alive}: scene forgets __twig0a +DLGLOBAL DEBUG test(__twig0a){alive}: removing reference __twig0a.other[0] -> other +DLGLOBAL DEBUG test(other){alive}: Received Event EV_OTHER_GONE +DLGLOBAL DEBUG 7 (root.alive(),_branch1.cleanup(),2*other.alive(),other.cleanup(),_branch0.alive(),__twig0a.cleanup()) +DLGLOBAL DEBUG test(other){alive}: alive(EV_OTHER_GONE) +DLGLOBAL DEBUG 8 (root.alive(),_branch1.cleanup(),2*other.alive(),other.cleanup(),_branch0.alive(),__twig0a.cleanup(),other.other_gone()) +DLGLOBAL DEBUG 7 (root.alive(),_branch1.cleanup(),2*other.alive(),other.cleanup(),_branch0.alive(),__twig0a.cleanup()) +DLGLOBAL DEBUG 6 (root.alive(),_branch1.cleanup(),other.alive(),other.cleanup(),_branch0.alive(),__twig0a.cleanup()) +DLGLOBAL DEBUG test(_branch0){alive}: Received Event EV_CHILD_GONE +DLGLOBAL DEBUG 7 (root.alive(),_branch1.cleanup(),other.alive(),other.cleanup(),2*_branch0.alive(),__twig0a.cleanup()) +DLGLOBAL DEBUG test(_branch0){alive}: alive(EV_CHILD_GONE) +DLGLOBAL DEBUG 8 (root.alive(),_branch1.cleanup(),other.alive(),other.cleanup(),2*_branch0.alive(),__twig0a.cleanup(),_branch0.child_gone()) +DLGLOBAL DEBUG test(_branch0){alive}: EV_CHILD_GONE: Dropped reference _branch0.child[0] = __twig0a +DLGLOBAL DEBUG test(_branch0){alive}: No more children +DLGLOBAL DEBUG 7 (root.alive(),_branch1.cleanup(),other.alive(),other.cleanup(),2*_branch0.alive(),__twig0a.cleanup()) +DLGLOBAL DEBUG test(_branch0){alive}: Ignoring trigger to terminate: already terminating +DLGLOBAL DEBUG 6 (root.alive(),_branch1.cleanup(),other.alive(),other.cleanup(),_branch0.alive(),__twig0a.cleanup()) +DLGLOBAL DEBUG test(__twig0a){alive}: cleanup() done +DLGLOBAL DEBUG 5 (root.alive(),_branch1.cleanup(),other.alive(),other.cleanup(),_branch0.alive()) +DLGLOBAL DEBUG test(__twig0a){alive}: Freeing instance +DLGLOBAL DEBUG test(__twig0a){alive}: Deallocated +DLGLOBAL DEBUG test(_branch0){alive}: Removing from parent test(root) +DLGLOBAL DEBUG 6 (root.alive(),_branch1.cleanup(),other.alive(),other.cleanup(),_branch0.alive(),_branch0.cleanup()) +DLGLOBAL DEBUG test(_branch0){alive}: cleanup() +DLGLOBAL DEBUG test(_branch0){alive}: scene forgets _branch0 +DLGLOBAL DEBUG test(root){alive}: Received Event EV_CHILD_GONE +DLGLOBAL DEBUG 7 (2*root.alive(),_branch1.cleanup(),other.alive(),other.cleanup(),_branch0.alive(),_branch0.cleanup()) +DLGLOBAL DEBUG test(root){alive}: alive(EV_CHILD_GONE) +DLGLOBAL DEBUG 8 (2*root.alive(),_branch1.cleanup(),other.alive(),other.cleanup(),_branch0.alive(),_branch0.cleanup(),root.child_gone()) +DLGLOBAL DEBUG test(root){alive}: EV_CHILD_GONE: Dropped reference root.child[0] = _branch0 +DLGLOBAL DEBUG test(root){alive}: still exists: child[1] +DLGLOBAL DEBUG 7 (2*root.alive(),_branch1.cleanup(),other.alive(),other.cleanup(),_branch0.alive(),_branch0.cleanup()) +DLGLOBAL DEBUG 6 (root.alive(),_branch1.cleanup(),other.alive(),other.cleanup(),_branch0.alive(),_branch0.cleanup()) +DLGLOBAL DEBUG test(_branch0){alive}: cleanup() done +DLGLOBAL DEBUG 5 (root.alive(),_branch1.cleanup(),other.alive(),other.cleanup(),_branch0.alive()) +DLGLOBAL DEBUG test(_branch0){alive}: Freeing instance +DLGLOBAL DEBUG test(_branch0){alive}: Deallocated +DLGLOBAL DEBUG test(root){alive}: Received Event EV_CHILD_GONE +DLGLOBAL DEBUG 6 (2*root.alive(),_branch1.cleanup(),other.alive(),other.cleanup(),_branch0.alive()) +DLGLOBAL DEBUG test(root){alive}: alive(EV_CHILD_GONE) +DLGLOBAL DEBUG test(root){alive}: EV_CHILD_GONE with NULL data, must be a parent_term event. Ignore. +DLGLOBAL DEBUG 5 (root.alive(),_branch1.cleanup(),other.alive(),other.cleanup(),_branch0.alive()) +DLGLOBAL DEBUG 4 (root.alive(),_branch1.cleanup(),other.alive(),other.cleanup()) +DLGLOBAL DEBUG test(other){alive}: cleanup() done +DLGLOBAL DEBUG 3 (root.alive(),_branch1.cleanup(),other.alive()) +DLGLOBAL DEBUG test(other){alive}: Freeing instance +DLGLOBAL DEBUG test(other){alive}: Deallocated +DLGLOBAL DEBUG 2 (root.alive(),_branch1.cleanup()) +DLGLOBAL DEBUG test(root){alive}: Received Event EV_CHILD_GONE +DLGLOBAL DEBUG 3 (2*root.alive(),_branch1.cleanup()) +DLGLOBAL DEBUG test(root){alive}: alive(EV_CHILD_GONE) +DLGLOBAL DEBUG 4 (2*root.alive(),_branch1.cleanup(),root.child_gone()) +DLGLOBAL DEBUG test(root){alive}: EV_CHILD_GONE: Dropped reference root.child[1] = _branch1 +DLGLOBAL DEBUG test(root){alive}: No more children +DLGLOBAL DEBUG 3 (2*root.alive(),_branch1.cleanup()) +DLGLOBAL DEBUG test(root){alive}: Ignoring trigger to terminate: already terminating +DLGLOBAL DEBUG 2 (root.alive(),_branch1.cleanup()) +DLGLOBAL DEBUG test(_branch1){alive}: cleanup() done +DLGLOBAL DEBUG 1 (root.alive()) +DLGLOBAL DEBUG test(_branch1){alive}: Freeing instance +DLGLOBAL DEBUG test(_branch1){alive}: Deallocated +DLGLOBAL DEBUG 2 (root.alive(),root.cleanup()) +DLGLOBAL DEBUG test(root){alive}: cleanup() +DLGLOBAL DEBUG test(root){alive}: scene forgets root +DLGLOBAL DEBUG test(root){alive}: cleanup() done +DLGLOBAL DEBUG 1 (root.alive()) +DLGLOBAL DEBUG test(root){alive}: Freeing instance +DLGLOBAL DEBUG test(root){alive}: Deallocated +DLGLOBAL DEBUG 0 (-) +DLGLOBAL DEBUG --- after destroy-event cascade: +DLGLOBAL DEBUG --- all deallocated. +*** loop_ctx contains 33 blocks, deallocating. +DLGLOBAL DEBUG scene_alloc() +DLGLOBAL DEBUG test(root){alive}: Allocated +DLGLOBAL DEBUG test(root){alive}: Allocated +DLGLOBAL DEBUG test(root){alive}: is child of test(root) +DLGLOBAL DEBUG test(_branch0){alive}: Allocated +DLGLOBAL DEBUG test(_branch0){alive}: is child of test(_branch0) +DLGLOBAL DEBUG test(_branch0){alive}: Allocated +DLGLOBAL DEBUG test(_branch0){alive}: is child of test(_branch0) +DLGLOBAL DEBUG test(root){alive}: Allocated +DLGLOBAL DEBUG test(root){alive}: is child of test(root) +DLGLOBAL DEBUG test(_branch1){alive}: Allocated +DLGLOBAL DEBUG test(_branch1){alive}: is child of test(_branch1) +DLGLOBAL DEBUG test(_branch1){alive}: Allocated +DLGLOBAL DEBUG test(_branch1){alive}: is child of test(_branch1) +DLGLOBAL DEBUG test(other){alive}: Allocated +DLGLOBAL DEBUG test(_branch0){alive}: _branch0.other[0] = other +DLGLOBAL DEBUG test(other){alive}: other.other[0] = _branch0 +DLGLOBAL DEBUG test(__twig0a){alive}: __twig0a.other[0] = other +DLGLOBAL DEBUG test(other){alive}: other.other[1] = __twig0a +DLGLOBAL DEBUG test(_branch1){alive}: _branch1.other[0] = other +DLGLOBAL DEBUG test(other){alive}: other.other[1] = _branch1 +DLGLOBAL DEBUG test(__twig1a){alive}: __twig1a.other[0] = root +DLGLOBAL DEBUG test(root){alive}: root.other[0] = __twig1a +DLGLOBAL DEBUG ------ before term cascade, got: +DLGLOBAL DEBUG root +DLGLOBAL DEBUG _branch0 +DLGLOBAL DEBUG __twig0a +DLGLOBAL DEBUG __twig0b +DLGLOBAL DEBUG _branch1 +DLGLOBAL DEBUG __twig1a +DLGLOBAL DEBUG __twig1b +DLGLOBAL DEBUG other +DLGLOBAL DEBUG --- +DLGLOBAL DEBUG --- term at _branch0 +DLGLOBAL DEBUG test(_branch0){alive}: Terminating (cause = OSMO_FSM_TERM_REGULAR) +DLGLOBAL DEBUG test(_branch0){alive}: pre_term() +DLGLOBAL DEBUG test(__twig0b){alive}: Terminating (cause = OSMO_FSM_TERM_PARENT) +DLGLOBAL DEBUG test(__twig0b){alive}: pre_term() +DLGLOBAL DEBUG test(__twig0b){alive}: Removing from parent test(_branch0) +DLGLOBAL DEBUG 1 (__twig0b.cleanup()) +DLGLOBAL DEBUG test(__twig0b){alive}: cleanup() +DLGLOBAL DEBUG test(__twig0b){alive}: scene forgets __twig0b +DLGLOBAL DEBUG test(_branch0){alive}: Received Event EV_CHILD_GONE +DLGLOBAL DEBUG 2 (__twig0b.cleanup(),_branch0.alive()) +DLGLOBAL DEBUG test(_branch0){alive}: alive(EV_CHILD_GONE) +DLGLOBAL DEBUG 3 (__twig0b.cleanup(),_branch0.alive(),_branch0.child_gone()) +DLGLOBAL DEBUG test(_branch0){alive}: EV_CHILD_GONE: Dropped reference _branch0.child[1] = __twig0b +DLGLOBAL DEBUG test(_branch0){alive}: still exists: child[0] +DLGLOBAL DEBUG 2 (__twig0b.cleanup(),_branch0.alive()) +DLGLOBAL DEBUG 1 (__twig0b.cleanup()) +DLGLOBAL DEBUG test(__twig0b){alive}: cleanup() done +DLGLOBAL DEBUG 0 (-) +DLGLOBAL DEBUG test(__twig0b){alive}: Freeing instance +DLGLOBAL DEBUG test(__twig0b){alive}: Deallocated +DLGLOBAL DEBUG test(__twig0a){alive}: Terminating (cause = OSMO_FSM_TERM_PARENT) +DLGLOBAL DEBUG test(__twig0a){alive}: pre_term() +DLGLOBAL DEBUG test(__twig0a){alive}: Removing from parent test(_branch0) +DLGLOBAL DEBUG 1 (__twig0a.cleanup()) +DLGLOBAL DEBUG test(__twig0a){alive}: cleanup() +DLGLOBAL DEBUG test(__twig0a){alive}: scene forgets __twig0a +DLGLOBAL DEBUG test(__twig0a){alive}: removing reference __twig0a.other[0] -> other +DLGLOBAL DEBUG test(other){alive}: Received Event EV_OTHER_GONE +DLGLOBAL DEBUG 2 (__twig0a.cleanup(),other.alive()) +DLGLOBAL DEBUG test(other){alive}: alive(EV_OTHER_GONE) +DLGLOBAL DEBUG 3 (__twig0a.cleanup(),other.alive(),other.other_gone()) +DLGLOBAL DEBUG 2 (__twig0a.cleanup(),other.alive()) +DLGLOBAL DEBUG 1 (__twig0a.cleanup()) +DLGLOBAL DEBUG test(_branch0){alive}: Received Event EV_CHILD_GONE +DLGLOBAL DEBUG 2 (__twig0a.cleanup(),_branch0.alive()) +DLGLOBAL DEBUG test(_branch0){alive}: alive(EV_CHILD_GONE) +DLGLOBAL DEBUG 3 (__twig0a.cleanup(),_branch0.alive(),_branch0.child_gone()) +DLGLOBAL DEBUG test(_branch0){alive}: EV_CHILD_GONE: Dropped reference _branch0.child[0] = __twig0a +DLGLOBAL DEBUG test(_branch0){alive}: No more children +DLGLOBAL DEBUG 2 (__twig0a.cleanup(),_branch0.alive()) +DLGLOBAL DEBUG test(_branch0){alive}: Ignoring trigger to terminate: already terminating +DLGLOBAL DEBUG 1 (__twig0a.cleanup()) +DLGLOBAL DEBUG test(__twig0a){alive}: cleanup() done +DLGLOBAL DEBUG 0 (-) +DLGLOBAL DEBUG test(__twig0a){alive}: Freeing instance +DLGLOBAL DEBUG test(__twig0a){alive}: Deallocated +DLGLOBAL DEBUG test(_branch0){alive}: Removing from parent test(root) +DLGLOBAL DEBUG 1 (_branch0.cleanup()) +DLGLOBAL DEBUG test(_branch0){alive}: cleanup() +DLGLOBAL DEBUG test(_branch0){alive}: scene forgets _branch0 +DLGLOBAL DEBUG test(_branch0){alive}: removing reference _branch0.other[0] -> other +DLGLOBAL DEBUG test(other){alive}: Received Event EV_OTHER_GONE +DLGLOBAL DEBUG 2 (_branch0.cleanup(),other.alive()) +DLGLOBAL DEBUG test(other){alive}: alive(EV_OTHER_GONE) +DLGLOBAL DEBUG 3 (_branch0.cleanup(),other.alive(),other.other_gone()) +DLGLOBAL DEBUG test(other){alive}: EV_OTHER_GONE: Dropped reference other.other[0] = _branch0 +DLGLOBAL DEBUG 2 (_branch0.cleanup(),other.alive()) +DLGLOBAL DEBUG test(other){alive}: Terminating (cause = OSMO_FSM_TERM_REGULAR) +DLGLOBAL DEBUG test(other){alive}: pre_term() +DLGLOBAL DEBUG 3 (_branch0.cleanup(),other.alive(),other.cleanup()) +DLGLOBAL DEBUG test(other){alive}: cleanup() +DLGLOBAL DEBUG test(other){alive}: scene forgets other +DLGLOBAL DEBUG test(other){alive}: removing reference other.other[1] -> _branch1 +DLGLOBAL DEBUG test(_branch1){alive}: Received Event EV_OTHER_GONE +DLGLOBAL DEBUG 4 (_branch0.cleanup(),other.alive(),other.cleanup(),_branch1.alive()) +DLGLOBAL DEBUG test(_branch1){alive}: alive(EV_OTHER_GONE) +DLGLOBAL DEBUG 5 (_branch0.cleanup(),other.alive(),other.cleanup(),_branch1.alive(),_branch1.other_gone()) +DLGLOBAL DEBUG test(_branch1){alive}: EV_OTHER_GONE: Dropped reference _branch1.other[0] = other +DLGLOBAL DEBUG 4 (_branch0.cleanup(),other.alive(),other.cleanup(),_branch1.alive()) +DLGLOBAL DEBUG test(_branch1){alive}: Terminating (cause = OSMO_FSM_TERM_REGULAR) +DLGLOBAL DEBUG test(_branch1){alive}: pre_term() +DLGLOBAL DEBUG test(__twig1b){alive}: Terminating (cause = OSMO_FSM_TERM_PARENT) +DLGLOBAL DEBUG test(__twig1b){alive}: pre_term() +DLGLOBAL DEBUG test(__twig1b){alive}: Removing from parent test(_branch1) +DLGLOBAL DEBUG 5 (_branch0.cleanup(),other.alive(),other.cleanup(),_branch1.alive(),__twig1b.cleanup()) +DLGLOBAL DEBUG test(__twig1b){alive}: cleanup() +DLGLOBAL DEBUG test(__twig1b){alive}: scene forgets __twig1b +DLGLOBAL DEBUG test(_branch1){alive}: Received Event EV_CHILD_GONE +DLGLOBAL DEBUG 6 (_branch0.cleanup(),other.alive(),other.cleanup(),2*_branch1.alive(),__twig1b.cleanup()) +DLGLOBAL DEBUG test(_branch1){alive}: alive(EV_CHILD_GONE) +DLGLOBAL DEBUG 7 (_branch0.cleanup(),other.alive(),other.cleanup(),2*_branch1.alive(),__twig1b.cleanup(),_branch1.child_gone()) +DLGLOBAL DEBUG test(_branch1){alive}: EV_CHILD_GONE: Dropped reference _branch1.child[1] = __twig1b +DLGLOBAL DEBUG test(_branch1){alive}: still exists: child[0] +DLGLOBAL DEBUG 6 (_branch0.cleanup(),other.alive(),other.cleanup(),2*_branch1.alive(),__twig1b.cleanup()) +DLGLOBAL DEBUG 5 (_branch0.cleanup(),other.alive(),other.cleanup(),_branch1.alive(),__twig1b.cleanup()) +DLGLOBAL DEBUG test(__twig1b){alive}: cleanup() done +DLGLOBAL DEBUG 4 (_branch0.cleanup(),other.alive(),other.cleanup(),_branch1.alive()) +DLGLOBAL DEBUG test(__twig1b){alive}: Freeing instance +DLGLOBAL DEBUG test(__twig1b){alive}: Deallocated +DLGLOBAL DEBUG test(__twig1a){alive}: Terminating (cause = OSMO_FSM_TERM_PARENT) +DLGLOBAL DEBUG test(__twig1a){alive}: pre_term() +DLGLOBAL DEBUG test(__twig1a){alive}: Removing from parent test(_branch1) +DLGLOBAL DEBUG 5 (_branch0.cleanup(),other.alive(),other.cleanup(),_branch1.alive(),__twig1a.cleanup()) +DLGLOBAL DEBUG test(__twig1a){alive}: cleanup() +DLGLOBAL DEBUG test(__twig1a){alive}: scene forgets __twig1a +DLGLOBAL DEBUG test(__twig1a){alive}: removing reference __twig1a.other[0] -> root +DLGLOBAL DEBUG test(root){alive}: Received Event EV_OTHER_GONE +DLGLOBAL DEBUG 6 (_branch0.cleanup(),other.alive(),other.cleanup(),_branch1.alive(),__twig1a.cleanup(),root.alive()) +DLGLOBAL DEBUG test(root){alive}: alive(EV_OTHER_GONE) +DLGLOBAL DEBUG 7 (_branch0.cleanup(),other.alive(),other.cleanup(),_branch1.alive(),__twig1a.cleanup(),root.alive(),root.other_gone()) +DLGLOBAL DEBUG test(root){alive}: EV_OTHER_GONE: Dropped reference root.other[0] = __twig1a +DLGLOBAL DEBUG 6 (_branch0.cleanup(),other.alive(),other.cleanup(),_branch1.alive(),__twig1a.cleanup(),root.alive()) +DLGLOBAL DEBUG test(root){alive}: Terminating (cause = OSMO_FSM_TERM_REGULAR) +DLGLOBAL DEBUG test(root){alive}: pre_term() +DLGLOBAL DEBUG test(_branch1){alive}: Ignoring trigger to terminate: already terminating +DLGLOBAL ERROR test(root){alive}: Internal error while terminating child FSMs: a child FSM is stuck +DLGLOBAL DEBUG 7 (_branch0.cleanup(),other.alive(),other.cleanup(),_branch1.alive(),__twig1a.cleanup(),root.alive(),root.cleanup()) +DLGLOBAL DEBUG test(root){alive}: cleanup() +DLGLOBAL DEBUG test(root){alive}: scene forgets root +DLGLOBAL DEBUG test(root){alive}: cleanup() done +DLGLOBAL DEBUG 6 (_branch0.cleanup(),other.alive(),other.cleanup(),_branch1.alive(),__twig1a.cleanup(),root.alive()) +DLGLOBAL DEBUG test(root){alive}: Freeing instance +DLGLOBAL DEBUG test(root){alive}: Deallocated +DLGLOBAL DEBUG 5 (_branch0.cleanup(),other.alive(),other.cleanup(),_branch1.alive(),__twig1a.cleanup()) +DLGLOBAL DEBUG test(_branch1){alive}: Received Event EV_CHILD_GONE +DLGLOBAL DEBUG 6 (_branch0.cleanup(),other.alive(),other.cleanup(),2*_branch1.alive(),__twig1a.cleanup()) +DLGLOBAL DEBUG test(_branch1){alive}: alive(EV_CHILD_GONE) +DLGLOBAL DEBUG 7 (_branch0.cleanup(),other.alive(),other.cleanup(),2*_branch1.alive(),__twig1a.cleanup(),_branch1.child_gone()) +DLGLOBAL DEBUG test(_branch1){alive}: EV_CHILD_GONE: Dropped reference _branch1.child[0] = __twig1a +DLGLOBAL DEBUG test(_branch1){alive}: No more children +DLGLOBAL DEBUG 6 (_branch0.cleanup(),other.alive(),other.cleanup(),2*_branch1.alive(),__twig1a.cleanup()) +DLGLOBAL DEBUG test(_branch1){alive}: Ignoring trigger to terminate: already terminating +DLGLOBAL DEBUG 5 (_branch0.cleanup(),other.alive(),other.cleanup(),_branch1.alive(),__twig1a.cleanup()) +DLGLOBAL DEBUG test(__twig1a){alive}: cleanup() done +DLGLOBAL DEBUG 4 (_branch0.cleanup(),other.alive(),other.cleanup(),_branch1.alive()) +DLGLOBAL DEBUG test(__twig1a){alive}: Freeing instance +DLGLOBAL DEBUG test(__twig1a){alive}: Deallocated +DLGLOBAL DEBUG test(_branch1){alive}: Removing from parent test(root) +DLGLOBAL DEBUG 5 (_branch0.cleanup(),other.alive(),other.cleanup(),_branch1.alive(),_branch1.cleanup()) +DLGLOBAL DEBUG test(_branch1){alive}: cleanup() +DLGLOBAL DEBUG test(_branch1){alive}: scene forgets _branch1 +DLGLOBAL DEBUG test(root){alive}: Received Event EV_CHILD_GONE +DLGLOBAL DEBUG 6 (_branch0.cleanup(),other.alive(),other.cleanup(),_branch1.alive(),_branch1.cleanup(),root.alive()) +DLGLOBAL DEBUG test(root){alive}: alive(EV_CHILD_GONE) +DLGLOBAL DEBUG 7 (_branch0.cleanup(),other.alive(),other.cleanup(),_branch1.alive(),_branch1.cleanup(),root.alive(),root.child_gone()) +DLGLOBAL DEBUG test(root){alive}: EV_CHILD_GONE: Dropped reference root.child[1] = _branch1 +DLGLOBAL DEBUG test(root){alive}: still exists: child[0] +DLGLOBAL DEBUG 6 (_branch0.cleanup(),other.alive(),other.cleanup(),_branch1.alive(),_branch1.cleanup(),root.alive()) +DLGLOBAL DEBUG 5 (_branch0.cleanup(),other.alive(),other.cleanup(),_branch1.alive(),_branch1.cleanup()) +DLGLOBAL DEBUG test(_branch1){alive}: cleanup() done +DLGLOBAL DEBUG 4 (_branch0.cleanup(),other.alive(),other.cleanup(),_branch1.alive()) +DLGLOBAL DEBUG test(_branch1){alive}: Freeing instance +DLGLOBAL DEBUG test(_branch1){alive}: Deallocated +DLGLOBAL DEBUG test(root){alive}: Received Event EV_CHILD_GONE +DLGLOBAL DEBUG 5 (_branch0.cleanup(),other.alive(),other.cleanup(),_branch1.alive(),root.alive()) +DLGLOBAL DEBUG test(root){alive}: alive(EV_CHILD_GONE) +DLGLOBAL DEBUG test(root){alive}: EV_CHILD_GONE with NULL data, must be a parent_term event. Ignore. +DLGLOBAL DEBUG 4 (_branch0.cleanup(),other.alive(),other.cleanup(),_branch1.alive()) +DLGLOBAL DEBUG 3 (_branch0.cleanup(),other.alive(),other.cleanup()) +DLGLOBAL DEBUG test(other){alive}: cleanup() done +DLGLOBAL DEBUG 2 (_branch0.cleanup(),other.alive()) +DLGLOBAL DEBUG test(other){alive}: Freeing instance +DLGLOBAL DEBUG test(other){alive}: Deallocated +DLGLOBAL DEBUG 1 (_branch0.cleanup()) +DLGLOBAL DEBUG test(root){alive}: Received Event EV_CHILD_GONE +DLGLOBAL DEBUG 2 (_branch0.cleanup(),root.alive()) +DLGLOBAL DEBUG test(root){alive}: alive(EV_CHILD_GONE) +DLGLOBAL DEBUG 3 (_branch0.cleanup(),root.alive(),root.child_gone()) +DLGLOBAL DEBUG test(root){alive}: EV_CHILD_GONE: Dropped reference root.child[0] = _branch0 +DLGLOBAL DEBUG test(root){alive}: No more children +DLGLOBAL DEBUG 2 (_branch0.cleanup(),root.alive()) +DLGLOBAL DEBUG test(root){alive}: Ignoring trigger to terminate: already terminating +DLGLOBAL DEBUG 1 (_branch0.cleanup()) +DLGLOBAL DEBUG test(_branch0){alive}: cleanup() done +DLGLOBAL DEBUG 0 (-) +DLGLOBAL DEBUG test(_branch0){alive}: Freeing instance +DLGLOBAL DEBUG test(_branch0){alive}: Deallocated +DLGLOBAL DEBUG test(root){alive}: Received Event EV_CHILD_GONE +DLGLOBAL DEBUG 1 (root.alive()) +DLGLOBAL DEBUG test(root){alive}: alive(EV_CHILD_GONE) +DLGLOBAL DEBUG test(root){alive}: EV_CHILD_GONE with NULL data, must be a parent_term event. Ignore. +DLGLOBAL DEBUG 0 (-) +DLGLOBAL DEBUG --- after term cascade: +DLGLOBAL DEBUG --- all deallocated. +*** loop_ctx contains 33 blocks, deallocating. +DLGLOBAL DEBUG scene_alloc() +DLGLOBAL DEBUG test(root){alive}: Allocated +DLGLOBAL DEBUG test(root){alive}: Allocated +DLGLOBAL DEBUG test(root){alive}: is child of test(root) +DLGLOBAL DEBUG test(_branch0){alive}: Allocated +DLGLOBAL DEBUG test(_branch0){alive}: is child of test(_branch0) +DLGLOBAL DEBUG test(_branch0){alive}: Allocated +DLGLOBAL DEBUG test(_branch0){alive}: is child of test(_branch0) +DLGLOBAL DEBUG test(root){alive}: Allocated +DLGLOBAL DEBUG test(root){alive}: is child of test(root) +DLGLOBAL DEBUG test(_branch1){alive}: Allocated +DLGLOBAL DEBUG test(_branch1){alive}: is child of test(_branch1) +DLGLOBAL DEBUG test(_branch1){alive}: Allocated +DLGLOBAL DEBUG test(_branch1){alive}: is child of test(_branch1) +DLGLOBAL DEBUG test(other){alive}: Allocated +DLGLOBAL DEBUG test(_branch0){alive}: _branch0.other[0] = other +DLGLOBAL DEBUG test(other){alive}: other.other[0] = _branch0 +DLGLOBAL DEBUG test(__twig0a){alive}: __twig0a.other[0] = other +DLGLOBAL DEBUG test(other){alive}: other.other[1] = __twig0a +DLGLOBAL DEBUG test(_branch1){alive}: _branch1.other[0] = other +DLGLOBAL DEBUG test(other){alive}: other.other[1] = _branch1 +DLGLOBAL DEBUG test(__twig1a){alive}: __twig1a.other[0] = root +DLGLOBAL DEBUG test(root){alive}: root.other[0] = __twig1a +DLGLOBAL DEBUG ------ before destroy-event cascade, got: +DLGLOBAL DEBUG root +DLGLOBAL DEBUG _branch0 +DLGLOBAL DEBUG __twig0a +DLGLOBAL DEBUG __twig0b +DLGLOBAL DEBUG _branch1 +DLGLOBAL DEBUG __twig1a +DLGLOBAL DEBUG __twig1b +DLGLOBAL DEBUG other +DLGLOBAL DEBUG --- +DLGLOBAL DEBUG --- destroy-event at _branch0 +DLGLOBAL DEBUG test(_branch0){alive}: Received Event EV_DESTROY +DLGLOBAL DEBUG 1 (_branch0.alive()) +DLGLOBAL DEBUG test(_branch0){alive}: alive(EV_DESTROY) +DLGLOBAL DEBUG test(_branch0){alive}: Terminating (cause = OSMO_FSM_TERM_REGULAR) +DLGLOBAL DEBUG test(_branch0){alive}: pre_term() +DLGLOBAL DEBUG test(__twig0b){alive}: Terminating (cause = OSMO_FSM_TERM_PARENT) +DLGLOBAL DEBUG test(__twig0b){alive}: pre_term() +DLGLOBAL DEBUG test(__twig0b){alive}: Removing from parent test(_branch0) +DLGLOBAL DEBUG 2 (_branch0.alive(),__twig0b.cleanup()) +DLGLOBAL DEBUG test(__twig0b){alive}: cleanup() +DLGLOBAL DEBUG test(__twig0b){alive}: scene forgets __twig0b +DLGLOBAL DEBUG test(_branch0){alive}: Received Event EV_CHILD_GONE +DLGLOBAL DEBUG 3 (2*_branch0.alive(),__twig0b.cleanup()) +DLGLOBAL DEBUG test(_branch0){alive}: alive(EV_CHILD_GONE) +DLGLOBAL DEBUG 4 (2*_branch0.alive(),__twig0b.cleanup(),_branch0.child_gone()) +DLGLOBAL DEBUG test(_branch0){alive}: EV_CHILD_GONE: Dropped reference _branch0.child[1] = __twig0b +DLGLOBAL DEBUG test(_branch0){alive}: still exists: child[0] +DLGLOBAL DEBUG 3 (2*_branch0.alive(),__twig0b.cleanup()) +DLGLOBAL DEBUG 2 (_branch0.alive(),__twig0b.cleanup()) +DLGLOBAL DEBUG test(__twig0b){alive}: cleanup() done +DLGLOBAL DEBUG 1 (_branch0.alive()) +DLGLOBAL DEBUG test(__twig0b){alive}: Freeing instance +DLGLOBAL DEBUG test(__twig0b){alive}: Deallocated +DLGLOBAL DEBUG test(__twig0a){alive}: Terminating (cause = OSMO_FSM_TERM_PARENT) +DLGLOBAL DEBUG test(__twig0a){alive}: pre_term() +DLGLOBAL DEBUG test(__twig0a){alive}: Removing from parent test(_branch0) +DLGLOBAL DEBUG 2 (_branch0.alive(),__twig0a.cleanup()) +DLGLOBAL DEBUG test(__twig0a){alive}: cleanup() +DLGLOBAL DEBUG test(__twig0a){alive}: scene forgets __twig0a +DLGLOBAL DEBUG test(__twig0a){alive}: removing reference __twig0a.other[0] -> other +DLGLOBAL DEBUG test(other){alive}: Received Event EV_OTHER_GONE +DLGLOBAL DEBUG 3 (_branch0.alive(),__twig0a.cleanup(),other.alive()) +DLGLOBAL DEBUG test(other){alive}: alive(EV_OTHER_GONE) +DLGLOBAL DEBUG 4 (_branch0.alive(),__twig0a.cleanup(),other.alive(),other.other_gone()) +DLGLOBAL DEBUG 3 (_branch0.alive(),__twig0a.cleanup(),other.alive()) +DLGLOBAL DEBUG 2 (_branch0.alive(),__twig0a.cleanup()) +DLGLOBAL DEBUG test(_branch0){alive}: Received Event EV_CHILD_GONE +DLGLOBAL DEBUG 3 (2*_branch0.alive(),__twig0a.cleanup()) +DLGLOBAL DEBUG test(_branch0){alive}: alive(EV_CHILD_GONE) +DLGLOBAL DEBUG 4 (2*_branch0.alive(),__twig0a.cleanup(),_branch0.child_gone()) +DLGLOBAL DEBUG test(_branch0){alive}: EV_CHILD_GONE: Dropped reference _branch0.child[0] = __twig0a +DLGLOBAL DEBUG test(_branch0){alive}: No more children +DLGLOBAL DEBUG 3 (2*_branch0.alive(),__twig0a.cleanup()) +DLGLOBAL DEBUG test(_branch0){alive}: Ignoring trigger to terminate: already terminating +DLGLOBAL DEBUG 2 (_branch0.alive(),__twig0a.cleanup()) +DLGLOBAL DEBUG test(__twig0a){alive}: cleanup() done +DLGLOBAL DEBUG 1 (_branch0.alive()) +DLGLOBAL DEBUG test(__twig0a){alive}: Freeing instance +DLGLOBAL DEBUG test(__twig0a){alive}: Deallocated +DLGLOBAL DEBUG test(_branch0){alive}: Removing from parent test(root) +DLGLOBAL DEBUG 2 (_branch0.alive(),_branch0.cleanup()) +DLGLOBAL DEBUG test(_branch0){alive}: cleanup() +DLGLOBAL DEBUG test(_branch0){alive}: scene forgets _branch0 +DLGLOBAL DEBUG test(_branch0){alive}: removing reference _branch0.other[0] -> other +DLGLOBAL DEBUG test(other){alive}: Received Event EV_OTHER_GONE +DLGLOBAL DEBUG 3 (_branch0.alive(),_branch0.cleanup(),other.alive()) +DLGLOBAL DEBUG test(other){alive}: alive(EV_OTHER_GONE) +DLGLOBAL DEBUG 4 (_branch0.alive(),_branch0.cleanup(),other.alive(),other.other_gone()) +DLGLOBAL DEBUG test(other){alive}: EV_OTHER_GONE: Dropped reference other.other[0] = _branch0 +DLGLOBAL DEBUG 3 (_branch0.alive(),_branch0.cleanup(),other.alive()) +DLGLOBAL DEBUG test(other){alive}: Terminating (cause = OSMO_FSM_TERM_REGULAR) +DLGLOBAL DEBUG test(other){alive}: pre_term() +DLGLOBAL DEBUG 4 (_branch0.alive(),_branch0.cleanup(),other.alive(),other.cleanup()) +DLGLOBAL DEBUG test(other){alive}: cleanup() +DLGLOBAL DEBUG test(other){alive}: scene forgets other +DLGLOBAL DEBUG test(other){alive}: removing reference other.other[1] -> _branch1 +DLGLOBAL DEBUG test(_branch1){alive}: Received Event EV_OTHER_GONE +DLGLOBAL DEBUG 5 (_branch0.alive(),_branch0.cleanup(),other.alive(),other.cleanup(),_branch1.alive()) +DLGLOBAL DEBUG test(_branch1){alive}: alive(EV_OTHER_GONE) +DLGLOBAL DEBUG 6 (_branch0.alive(),_branch0.cleanup(),other.alive(),other.cleanup(),_branch1.alive(),_branch1.other_gone()) +DLGLOBAL DEBUG test(_branch1){alive}: EV_OTHER_GONE: Dropped reference _branch1.other[0] = other +DLGLOBAL DEBUG 5 (_branch0.alive(),_branch0.cleanup(),other.alive(),other.cleanup(),_branch1.alive()) +DLGLOBAL DEBUG test(_branch1){alive}: Terminating (cause = OSMO_FSM_TERM_REGULAR) +DLGLOBAL DEBUG test(_branch1){alive}: pre_term() +DLGLOBAL DEBUG test(__twig1b){alive}: Terminating (cause = OSMO_FSM_TERM_PARENT) +DLGLOBAL DEBUG test(__twig1b){alive}: pre_term() +DLGLOBAL DEBUG test(__twig1b){alive}: Removing from parent test(_branch1) +DLGLOBAL DEBUG 6 (_branch0.alive(),_branch0.cleanup(),other.alive(),other.cleanup(),_branch1.alive(),__twig1b.cleanup()) +DLGLOBAL DEBUG test(__twig1b){alive}: cleanup() +DLGLOBAL DEBUG test(__twig1b){alive}: scene forgets __twig1b +DLGLOBAL DEBUG test(_branch1){alive}: Received Event EV_CHILD_GONE +DLGLOBAL DEBUG 7 (_branch0.alive(),_branch0.cleanup(),other.alive(),other.cleanup(),2*_branch1.alive(),__twig1b.cleanup()) +DLGLOBAL DEBUG test(_branch1){alive}: alive(EV_CHILD_GONE) +DLGLOBAL DEBUG 8 (_branch0.alive(),_branch0.cleanup(),other.alive(),other.cleanup(),2*_branch1.alive(),__twig1b.cleanup(),_branch1.child_gone( +DLGLOBAL DEBUG test(_branch1){alive}: EV_CHILD_GONE: Dropped reference _branch1.child[1] = __twig1b +DLGLOBAL DEBUG test(_branch1){alive}: still exists: child[0] +DLGLOBAL DEBUG 7 (_branch0.alive(),_branch0.cleanup(),other.alive(),other.cleanup(),2*_branch1.alive(),__twig1b.cleanup()) +DLGLOBAL DEBUG 6 (_branch0.alive(),_branch0.cleanup(),other.alive(),other.cleanup(),_branch1.alive(),__twig1b.cleanup()) +DLGLOBAL DEBUG test(__twig1b){alive}: cleanup() done +DLGLOBAL DEBUG 5 (_branch0.alive(),_branch0.cleanup(),other.alive(),other.cleanup(),_branch1.alive()) +DLGLOBAL DEBUG test(__twig1b){alive}: Freeing instance +DLGLOBAL DEBUG test(__twig1b){alive}: Deallocated +DLGLOBAL DEBUG test(__twig1a){alive}: Terminating (cause = OSMO_FSM_TERM_PARENT) +DLGLOBAL DEBUG test(__twig1a){alive}: pre_term() +DLGLOBAL DEBUG test(__twig1a){alive}: Removing from parent test(_branch1) +DLGLOBAL DEBUG 6 (_branch0.alive(),_branch0.cleanup(),other.alive(),other.cleanup(),_branch1.alive(),__twig1a.cleanup()) +DLGLOBAL DEBUG test(__twig1a){alive}: cleanup() +DLGLOBAL DEBUG test(__twig1a){alive}: scene forgets __twig1a +DLGLOBAL DEBUG test(__twig1a){alive}: removing reference __twig1a.other[0] -> root +DLGLOBAL DEBUG test(root){alive}: Received Event EV_OTHER_GONE +DLGLOBAL DEBUG 7 (_branch0.alive(),_branch0.cleanup(),other.alive(),other.cleanup(),_branch1.alive(),__twig1a.cleanup(),root.alive()) +DLGLOBAL DEBUG test(root){alive}: alive(EV_OTHER_GONE) +DLGLOBAL DEBUG 8 (_branch0.alive(),_branch0.cleanup(),other.alive(),other.cleanup(),_branch1.alive(),__twig1a.cleanup(),root.alive(),root.othe +DLGLOBAL DEBUG test(root){alive}: EV_OTHER_GONE: Dropped reference root.other[0] = __twig1a +DLGLOBAL DEBUG 7 (_branch0.alive(),_branch0.cleanup(),other.alive(),other.cleanup(),_branch1.alive(),__twig1a.cleanup(),root.alive()) +DLGLOBAL DEBUG test(root){alive}: Terminating (cause = OSMO_FSM_TERM_REGULAR) +DLGLOBAL DEBUG test(root){alive}: pre_term() +DLGLOBAL DEBUG test(_branch1){alive}: Ignoring trigger to terminate: already terminating +DLGLOBAL ERROR test(root){alive}: Internal error while terminating child FSMs: a child FSM is stuck +DLGLOBAL DEBUG 8 (_branch0.alive(),_branch0.cleanup(),other.alive(),other.cleanup(),_branch1.alive(),__twig1a.cleanup(),root.alive(),root.clea +DLGLOBAL DEBUG test(root){alive}: cleanup() +DLGLOBAL DEBUG test(root){alive}: scene forgets root +DLGLOBAL DEBUG test(root){alive}: cleanup() done +DLGLOBAL DEBUG 7 (_branch0.alive(),_branch0.cleanup(),other.alive(),other.cleanup(),_branch1.alive(),__twig1a.cleanup(),root.alive()) +DLGLOBAL DEBUG test(root){alive}: Freeing instance +DLGLOBAL DEBUG test(root){alive}: Deallocated +DLGLOBAL DEBUG 6 (_branch0.alive(),_branch0.cleanup(),other.alive(),other.cleanup(),_branch1.alive(),__twig1a.cleanup()) +DLGLOBAL DEBUG test(_branch1){alive}: Received Event EV_CHILD_GONE +DLGLOBAL DEBUG 7 (_branch0.alive(),_branch0.cleanup(),other.alive(),other.cleanup(),2*_branch1.alive(),__twig1a.cleanup()) +DLGLOBAL DEBUG test(_branch1){alive}: alive(EV_CHILD_GONE) +DLGLOBAL DEBUG 8 (_branch0.alive(),_branch0.cleanup(),other.alive(),other.cleanup(),2*_branch1.alive(),__twig1a.cleanup(),_branch1.child_gone( +DLGLOBAL DEBUG test(_branch1){alive}: EV_CHILD_GONE: Dropped reference _branch1.child[0] = __twig1a +DLGLOBAL DEBUG test(_branch1){alive}: No more children +DLGLOBAL DEBUG 7 (_branch0.alive(),_branch0.cleanup(),other.alive(),other.cleanup(),2*_branch1.alive(),__twig1a.cleanup()) +DLGLOBAL DEBUG test(_branch1){alive}: Ignoring trigger to terminate: already terminating +DLGLOBAL DEBUG 6 (_branch0.alive(),_branch0.cleanup(),other.alive(),other.cleanup(),_branch1.alive(),__twig1a.cleanup()) +DLGLOBAL DEBUG test(__twig1a){alive}: cleanup() done +DLGLOBAL DEBUG 5 (_branch0.alive(),_branch0.cleanup(),other.alive(),other.cleanup(),_branch1.alive()) +DLGLOBAL DEBUG test(__twig1a){alive}: Freeing instance +DLGLOBAL DEBUG test(__twig1a){alive}: Deallocated +DLGLOBAL DEBUG test(_branch1){alive}: Removing from parent test(root) +DLGLOBAL DEBUG 6 (_branch0.alive(),_branch0.cleanup(),other.alive(),other.cleanup(),_branch1.alive(),_branch1.cleanup()) +DLGLOBAL DEBUG test(_branch1){alive}: cleanup() +DLGLOBAL DEBUG test(_branch1){alive}: scene forgets _branch1 +DLGLOBAL DEBUG test(root){alive}: Received Event EV_CHILD_GONE +DLGLOBAL DEBUG 7 (_branch0.alive(),_branch0.cleanup(),other.alive(),other.cleanup(),_branch1.alive(),_branch1.cleanup(),root.alive()) +DLGLOBAL DEBUG test(root){alive}: alive(EV_CHILD_GONE) +DLGLOBAL DEBUG 8 (_branch0.alive(),_branch0.cleanup(),other.alive(),other.cleanup(),_branch1.alive(),_branch1.cleanup(),root.alive(),root.chil +DLGLOBAL DEBUG test(root){alive}: EV_CHILD_GONE: Dropped reference root.child[1] = _branch1 +DLGLOBAL DEBUG test(root){alive}: still exists: child[0] +DLGLOBAL DEBUG 7 (_branch0.alive(),_branch0.cleanup(),other.alive(),other.cleanup(),_branch1.alive(),_branch1.cleanup(),root.alive()) +DLGLOBAL DEBUG 6 (_branch0.alive(),_branch0.cleanup(),other.alive(),other.cleanup(),_branch1.alive(),_branch1.cleanup()) +DLGLOBAL DEBUG test(_branch1){alive}: cleanup() done +DLGLOBAL DEBUG 5 (_branch0.alive(),_branch0.cleanup(),other.alive(),other.cleanup(),_branch1.alive()) +DLGLOBAL DEBUG test(_branch1){alive}: Freeing instance +DLGLOBAL DEBUG test(_branch1){alive}: Deallocated +DLGLOBAL DEBUG test(root){alive}: Received Event EV_CHILD_GONE +DLGLOBAL DEBUG 6 (_branch0.alive(),_branch0.cleanup(),other.alive(),other.cleanup(),_branch1.alive(),root.alive()) +DLGLOBAL DEBUG test(root){alive}: alive(EV_CHILD_GONE) +DLGLOBAL DEBUG test(root){alive}: EV_CHILD_GONE with NULL data, must be a parent_term event. Ignore. +DLGLOBAL DEBUG 5 (_branch0.alive(),_branch0.cleanup(),other.alive(),other.cleanup(),_branch1.alive()) +DLGLOBAL DEBUG 4 (_branch0.alive(),_branch0.cleanup(),other.alive(),other.cleanup()) +DLGLOBAL DEBUG test(other){alive}: cleanup() done +DLGLOBAL DEBUG 3 (_branch0.alive(),_branch0.cleanup(),other.alive()) +DLGLOBAL DEBUG test(other){alive}: Freeing instance +DLGLOBAL DEBUG test(other){alive}: Deallocated +DLGLOBAL DEBUG 2 (_branch0.alive(),_branch0.cleanup()) +DLGLOBAL DEBUG test(root){alive}: Received Event EV_CHILD_GONE +DLGLOBAL DEBUG 3 (_branch0.alive(),_branch0.cleanup(),root.alive()) +DLGLOBAL DEBUG test(root){alive}: alive(EV_CHILD_GONE) +DLGLOBAL DEBUG 4 (_branch0.alive(),_branch0.cleanup(),root.alive(),root.child_gone()) +DLGLOBAL DEBUG test(root){alive}: EV_CHILD_GONE: Dropped reference root.child[0] = _branch0 +DLGLOBAL DEBUG test(root){alive}: No more children +DLGLOBAL DEBUG 3 (_branch0.alive(),_branch0.cleanup(),root.alive()) +DLGLOBAL DEBUG test(root){alive}: Ignoring trigger to terminate: already terminating +DLGLOBAL DEBUG 2 (_branch0.alive(),_branch0.cleanup()) +DLGLOBAL DEBUG test(_branch0){alive}: cleanup() done +DLGLOBAL DEBUG 1 (_branch0.alive()) +DLGLOBAL DEBUG test(_branch0){alive}: Freeing instance +DLGLOBAL DEBUG test(_branch0){alive}: Deallocated +DLGLOBAL DEBUG test(root){alive}: Received Event EV_CHILD_GONE +DLGLOBAL DEBUG 2 (_branch0.alive(),root.alive()) +DLGLOBAL DEBUG test(root){alive}: alive(EV_CHILD_GONE) +DLGLOBAL DEBUG test(root){alive}: EV_CHILD_GONE with NULL data, must be a parent_term event. Ignore. +DLGLOBAL DEBUG 1 (_branch0.alive()) +DLGLOBAL DEBUG 0 (-) +DLGLOBAL DEBUG --- after destroy-event cascade: +DLGLOBAL DEBUG --- all deallocated. +*** loop_ctx contains 33 blocks, deallocating. +DLGLOBAL DEBUG scene_alloc() +DLGLOBAL DEBUG test(root){alive}: Allocated +DLGLOBAL DEBUG test(root){alive}: Allocated +DLGLOBAL DEBUG test(root){alive}: is child of test(root) +DLGLOBAL DEBUG test(_branch0){alive}: Allocated +DLGLOBAL DEBUG test(_branch0){alive}: is child of test(_branch0) +DLGLOBAL DEBUG test(_branch0){alive}: Allocated +DLGLOBAL DEBUG test(_branch0){alive}: is child of test(_branch0) +DLGLOBAL DEBUG test(root){alive}: Allocated +DLGLOBAL DEBUG test(root){alive}: is child of test(root) +DLGLOBAL DEBUG test(_branch1){alive}: Allocated +DLGLOBAL DEBUG test(_branch1){alive}: is child of test(_branch1) +DLGLOBAL DEBUG test(_branch1){alive}: Allocated +DLGLOBAL DEBUG test(_branch1){alive}: is child of test(_branch1) +DLGLOBAL DEBUG test(other){alive}: Allocated +DLGLOBAL DEBUG test(_branch0){alive}: _branch0.other[0] = other +DLGLOBAL DEBUG test(other){alive}: other.other[0] = _branch0 +DLGLOBAL DEBUG test(__twig0a){alive}: __twig0a.other[0] = other +DLGLOBAL DEBUG test(other){alive}: other.other[1] = __twig0a +DLGLOBAL DEBUG test(_branch1){alive}: _branch1.other[0] = other +DLGLOBAL DEBUG test(other){alive}: other.other[1] = _branch1 +DLGLOBAL DEBUG test(__twig1a){alive}: __twig1a.other[0] = root +DLGLOBAL DEBUG test(root){alive}: root.other[0] = __twig1a +DLGLOBAL DEBUG ------ before term cascade, got: +DLGLOBAL DEBUG root +DLGLOBAL DEBUG _branch0 +DLGLOBAL DEBUG __twig0a +DLGLOBAL DEBUG __twig0b +DLGLOBAL DEBUG _branch1 +DLGLOBAL DEBUG __twig1a +DLGLOBAL DEBUG __twig1b +DLGLOBAL DEBUG other +DLGLOBAL DEBUG --- +DLGLOBAL DEBUG --- term at __twig0a +DLGLOBAL DEBUG test(__twig0a){alive}: Terminating (cause = OSMO_FSM_TERM_REGULAR) +DLGLOBAL DEBUG test(__twig0a){alive}: pre_term() +DLGLOBAL DEBUG test(__twig0a){alive}: Removing from parent test(_branch0) +DLGLOBAL DEBUG 1 (__twig0a.cleanup()) +DLGLOBAL DEBUG test(__twig0a){alive}: cleanup() +DLGLOBAL DEBUG test(__twig0a){alive}: scene forgets __twig0a +DLGLOBAL DEBUG test(__twig0a){alive}: removing reference __twig0a.other[0] -> other +DLGLOBAL DEBUG test(other){alive}: Received Event EV_OTHER_GONE +DLGLOBAL DEBUG 2 (__twig0a.cleanup(),other.alive()) +DLGLOBAL DEBUG test(other){alive}: alive(EV_OTHER_GONE) +DLGLOBAL DEBUG 3 (__twig0a.cleanup(),other.alive(),other.other_gone()) +DLGLOBAL DEBUG 2 (__twig0a.cleanup(),other.alive()) +DLGLOBAL DEBUG 1 (__twig0a.cleanup()) +DLGLOBAL DEBUG test(_branch0){alive}: Received Event EV_CHILD_GONE +DLGLOBAL DEBUG 2 (__twig0a.cleanup(),_branch0.alive()) +DLGLOBAL DEBUG test(_branch0){alive}: alive(EV_CHILD_GONE) +DLGLOBAL DEBUG 3 (__twig0a.cleanup(),_branch0.alive(),_branch0.child_gone()) +DLGLOBAL DEBUG test(_branch0){alive}: EV_CHILD_GONE: Dropped reference _branch0.child[0] = __twig0a +DLGLOBAL DEBUG test(_branch0){alive}: still exists: child[1] +DLGLOBAL DEBUG 2 (__twig0a.cleanup(),_branch0.alive()) +DLGLOBAL DEBUG 1 (__twig0a.cleanup()) +DLGLOBAL DEBUG test(__twig0a){alive}: cleanup() done +DLGLOBAL DEBUG 0 (-) +DLGLOBAL DEBUG test(__twig0a){alive}: Freeing instance +DLGLOBAL DEBUG test(__twig0a){alive}: Deallocated +DLGLOBAL DEBUG test(_branch0){alive}: Received Event EV_CHILD_GONE +DLGLOBAL DEBUG 1 (_branch0.alive()) +DLGLOBAL DEBUG test(_branch0){alive}: alive(EV_CHILD_GONE) +DLGLOBAL DEBUG test(_branch0){alive}: EV_CHILD_GONE with NULL data, must be a parent_term event. Ignore. +DLGLOBAL DEBUG 0 (-) +DLGLOBAL DEBUG --- after term cascade: +DLGLOBAL DEBUG root +DLGLOBAL DEBUG _branch0 +DLGLOBAL DEBUG __twig0b +DLGLOBAL DEBUG _branch1 +DLGLOBAL DEBUG __twig1a +DLGLOBAL DEBUG __twig1b +DLGLOBAL DEBUG other +DLGLOBAL DEBUG --- 7 objects remain. cleaning up +*** loop_ctx contains 5 blocks, deallocating. +DLGLOBAL DEBUG test(root){alive}: Terminating (cause = OSMO_FSM_TERM_ERROR) +DLGLOBAL DEBUG test(root){alive}: pre_term() +DLGLOBAL DEBUG test(_branch1){alive}: Terminating (cause = OSMO_FSM_TERM_PARENT) +DLGLOBAL DEBUG test(_branch1){alive}: pre_term() +DLGLOBAL DEBUG test(__twig1b){alive}: Terminating (cause = OSMO_FSM_TERM_PARENT) +DLGLOBAL DEBUG test(__twig1b){alive}: pre_term() +DLGLOBAL DEBUG test(__twig1b){alive}: Removing from parent test(_branch1) +DLGLOBAL DEBUG 1 (__twig1b.cleanup()) +DLGLOBAL DEBUG test(__twig1b){alive}: cleanup() +DLGLOBAL DEBUG test(__twig1b){alive}: scene forgets __twig1b +DLGLOBAL DEBUG test(_branch1){alive}: Received Event EV_CHILD_GONE +DLGLOBAL DEBUG 2 (__twig1b.cleanup(),_branch1.alive()) +DLGLOBAL DEBUG test(_branch1){alive}: alive(EV_CHILD_GONE) +DLGLOBAL DEBUG 3 (__twig1b.cleanup(),_branch1.alive(),_branch1.child_gone()) +DLGLOBAL DEBUG test(_branch1){alive}: EV_CHILD_GONE: Dropped reference _branch1.child[1] = __twig1b +DLGLOBAL DEBUG test(_branch1){alive}: still exists: child[0] +DLGLOBAL DEBUG 2 (__twig1b.cleanup(),_branch1.alive()) +DLGLOBAL DEBUG 1 (__twig1b.cleanup()) +DLGLOBAL DEBUG test(__twig1b){alive}: cleanup() done +DLGLOBAL DEBUG 0 (-) +DLGLOBAL DEBUG test(__twig1b){alive}: Freeing instance +DLGLOBAL DEBUG test(__twig1b){alive}: Deallocated +DLGLOBAL DEBUG test(__twig1a){alive}: Terminating (cause = OSMO_FSM_TERM_PARENT) +DLGLOBAL DEBUG test(__twig1a){alive}: pre_term() +DLGLOBAL DEBUG test(__twig1a){alive}: Removing from parent test(_branch1) +DLGLOBAL DEBUG 1 (__twig1a.cleanup()) +DLGLOBAL DEBUG test(__twig1a){alive}: cleanup() +DLGLOBAL DEBUG test(__twig1a){alive}: scene forgets __twig1a +DLGLOBAL DEBUG test(__twig1a){alive}: removing reference __twig1a.other[0] -> root +DLGLOBAL DEBUG test(root){alive}: Received Event EV_OTHER_GONE +DLGLOBAL DEBUG 2 (__twig1a.cleanup(),root.alive()) +DLGLOBAL DEBUG test(root){alive}: alive(EV_OTHER_GONE) +DLGLOBAL DEBUG 3 (__twig1a.cleanup(),root.alive(),root.other_gone()) +DLGLOBAL DEBUG test(root){alive}: EV_OTHER_GONE: Dropped reference root.other[0] = __twig1a +DLGLOBAL DEBUG 2 (__twig1a.cleanup(),root.alive()) +DLGLOBAL DEBUG test(root){alive}: Ignoring trigger to terminate: already terminating +DLGLOBAL DEBUG 1 (__twig1a.cleanup()) +DLGLOBAL DEBUG test(_branch1){alive}: Received Event EV_CHILD_GONE +DLGLOBAL DEBUG 2 (__twig1a.cleanup(),_branch1.alive()) +DLGLOBAL DEBUG test(_branch1){alive}: alive(EV_CHILD_GONE) +DLGLOBAL DEBUG 3 (__twig1a.cleanup(),_branch1.alive(),_branch1.child_gone()) +DLGLOBAL DEBUG test(_branch1){alive}: EV_CHILD_GONE: Dropped reference _branch1.child[0] = __twig1a +DLGLOBAL DEBUG test(_branch1){alive}: No more children +DLGLOBAL DEBUG 2 (__twig1a.cleanup(),_branch1.alive()) +DLGLOBAL DEBUG test(_branch1){alive}: Ignoring trigger to terminate: already terminating +DLGLOBAL DEBUG 1 (__twig1a.cleanup()) +DLGLOBAL DEBUG test(__twig1a){alive}: cleanup() done +DLGLOBAL DEBUG 0 (-) +DLGLOBAL DEBUG test(__twig1a){alive}: Freeing instance +DLGLOBAL DEBUG test(__twig1a){alive}: Deallocated +DLGLOBAL DEBUG test(_branch1){alive}: Removing from parent test(root) +DLGLOBAL DEBUG 1 (_branch1.cleanup()) +DLGLOBAL DEBUG test(_branch1){alive}: cleanup() +DLGLOBAL DEBUG test(_branch1){alive}: scene forgets _branch1 +DLGLOBAL DEBUG test(_branch1){alive}: removing reference _branch1.other[0]