From df93ebb8f682e3b10b5a0f932edea85408fbe4bd Mon Sep 17 00:00:00 2001 From: Aleric Inglewood Date: Sat, 9 Mar 2013 04:20:54 +0100 Subject: [PATCH] Bug fix for AIStateMachine. When a state machine is aborted after it switched to bs_initialize, but before it executed initialize_impl(), then we should set the state back to bs_reset and abort cleanly by switching to bs_killed and then handle that. Before the state was set to bs_abort, resulting in calling abort_impl(), finish_impl(), the call back and unref() for an not initialized state machine! (detected by the assert that makes sure that ref()/unref() are called in balance). --- indra/aistatemachine/aistatemachine.cpp | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/indra/aistatemachine/aistatemachine.cpp b/indra/aistatemachine/aistatemachine.cpp index 230cd1422..dedec3bd7 100644 --- a/indra/aistatemachine/aistatemachine.cpp +++ b/indra/aistatemachine/aistatemachine.cpp @@ -489,7 +489,15 @@ void AIStateMachine::multiplex(event_type event) if (LL_UNLIKELY(late_abort)) { // abort() was called from a child state machine, from another thread, while we were already scheduled to run normally from an engine. - state = bs_abort; + // What we want to do here is pretend we detected the abort at the end of the *previous* run. + // If the state is bs_multiplex then the previous state was either bs_initialize or bs_multiplex, + // both of which would have switched to bs_abort: we set the state to bs_abort instead and just + // continue this run. + // However, if the state is bs_initialize we can't switch to bs_killed because that state isn't + // handled in the switch below; it's only handled when exiting multiplex() directly after it is set. + // Therefore, in that case we have to set the state BACK to bs_reset and run it again. This duplicated + // run of bs_reset is not a problem because it happens to be a NoOp. + state = (state == bs_initialize) ? bs_reset : bs_abort; #ifdef CWDEBUG Dout(dc::statemachine, "Late abort detected! Running state " << state_str(state) << " instead [" << (void*)this << "]"); #endif @@ -507,7 +515,8 @@ void AIStateMachine::multiplex(event_type event) switch(state) { case bs_reset: - // We're just being kick started to get into the right thread. + // We're just being kick started to get into the right thread + // (possibly for the second time when a late abort was detected, but that's ok: we do nothing here). break; case bs_initialize: ref();