Files
SingularityViewer/indra/aistatemachine/aistatemachine.cpp
2014-08-25 17:58:45 +02:00

1419 lines
48 KiB
C++

/**
* @file aistatemachine.cpp
* @brief Implementation of AIStateMachine
*
* Copyright (c) 2010 - 2013, Aleric Inglewood.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* There are special exceptions to the terms and conditions of the GPL as
* it is applied to this Source Code. View the full text of the exception
* in the file doc/FLOSS-exception.txt in this software distribution.
*
* CHANGELOG
* and additional copyright holders.
*
* 01/03/2010
* Initial version, written by Aleric Inglewood @ SL
*
* 28/02/2013
* Rewritten from scratch to fully support threading.
*/
#include "linden_common.h"
#include "aistatemachine.h"
#include "aicondition.h"
#include "lltimer.h"
//==================================================================
// Overview
// A AIStateMachine is a base class that allows derived classes to
// go through asynchronous states, while the code still appears to
// be more or less sequential.
//
// These state machine objects can be reused to build more complex
// objects.
//
// It is important to note that each state has a duality: the object
// can have a state that will cause a corresponding function to be
// called; and often that function will end with changing the state
// again, to signal that it was handled. It is easy to confuse the
// function of a state with the state at the end of the function.
// For example, the state "initialize" could cause the member
// function 'init()' to be called, and at the end one would be
// inclined to set the state to "initialized". However, this is the
// wrong approach: the correct use of state names does reflect the
// functions that will be called, never the function that just was
// called.
//
// Each (derived) class goes through a series of states as follows:
//
// Creation
// |
// v
// (idle) <----. Idle until run() is called.
// | |
// Initialize | Calls initialize_impl().
// | |
// | (idle) | Idle until cont() or advance_state() is called.
// | | ^ |
// v v | |
// .-----------. |
// | Multiplex | | Call multiplex_impl() until idle(), abort() or finish() is called.
// '-----------' |
// | | |
// v | |
// Abort | | Calls abort_impl().
// | | |
// v v |
// Finish | Calls finish_impl(), which may call run().
// | | |
// | v |
// | Callback | which may call kill() and/or run().
// | | | |
// | | `-----'
// v v
// Killed Delete the statemachine (all statemachines must be allocated with new).
//
// Each state causes corresponding code to be called.
// Finish cleans up whatever is done by Initialize.
// Abort should clean up additional things done while running.
//
// The running state is entered by calling run().
//
// While the base class is in the bs_multiplex state, it is the derived class
// that goes through different states. The state variable of the derived
// class is only valid while the base class is in the bs_multiplex state.
//
// A derived class can exit the bs_multiplex state by calling one of two methods:
// abort() in case of failure, or finish() in case of success.
// Respectively these set the state to bs_abort and bs_finish.
//
// The methods of the derived class call set_state() to change their
// own state within the bs_multiplex state, or by calling either abort()
// or finish().
//
// Restarting a finished state machine can be done by calling run(),
// which will cause a re-initialize. The default is to destruct the
// state machine once the last LLPointer to it is deleted.
//
//==================================================================
// Declaration
// Every state machine is (indirectly) derived from AIStateMachine.
// For example:
#ifdef EXAMPLE_CODE // undefined
class HelloWorld : public AIStateMachine {
protected:
// The base class of this state machine.
typedef AIStateMachine direct_base_type;
// The different states of the state machine.
enum hello_world_state_type {
HelloWorld_start = direct_base_type::max_state,
HelloWorld_done,
};
public:
static state_type const max_state = HelloWorld_done + 1; // One beyond the largest state.
public:
// The derived class must have a default constructor.
HelloWorld();
protected:
// The destructor must be protected.
/*virtual*/ ~HelloWorld();
protected:
// The following virtual functions must be implemented:
// Handle initializing the object.
/*virtual*/ void initialize_impl(void);
// Handle mRunState.
/*virtual*/ void multiplex_impl(state_type run_state);
// Handle aborting from current bs_multiplex state (the default AIStateMachine::abort_impl() does nothing).
/*virtual*/ void abort_impl(void);
// Handle cleaning up from initialization (or post abort) state (the default AIStateMachine::finish_impl() does nothing).
/*virtual*/ void finish_impl(void);
// Return human readable string for run_state.
/*virtual*/ char const* state_str_impl(state_type run_state) const;
};
// In the .cpp file:
char const* HelloWorld::state_str_impl(state_type run_state) const
{
switch(run_state)
{
// A complete listing of hello_world_state_type.
AI_CASE_RETURN(HelloWorld_start);
AI_CASE_RETURN(HelloWorld_done);
}
#if directly_derived_from_AIStateMachine
llassert(false);
return "UNKNOWN STATE";
#else
llassert(run_state < direct_base_type::max_state);
return direct_base_type::state_str_impl(run_state);
#endif
}
#endif // EXAMPLE_CODE
//==================================================================
// Life cycle: creation, initialization, running and destruction
// Any thread may create a state machine object, initialize it by calling
// it's initializing member function and call one of the 'run' methods,
// which might or might not immediately start to execute the state machine.
#ifdef EXAMPLE_CODE
HelloWorld* hello_world = new HelloWorld;
hello_world->init(...); // A custom initialization function.
hello_world->run(...); // One of the run() functions.
// hello_world might be destructed here.
// You can avoid possible destruction by using an LLPointer<HelloWorld>
// instead of HelloWorld*.
#endif // EXAMPLE_CODE
// The call to run() causes a call to initialize_impl(), which MUST call
// set_state() at least once (only the last call is used).
// Upon return from initialize_impl(), multiplex_impl() will be called
// with that state.
// multiplex_impl() may never reentrant (cause itself to be called).
// multiplex_impl() should end by callling either one of:
// idle(), yield*(), finish() [or abort()].
// Leaving multiplex_impl() without calling any of those might result in an
// immediate reentry, which could lead to 100% CPU usage unless the state
// is changed with set_state().
// If multiplex_impl() calls finish() then finish_impl() will be called [if it
// calls abort() then abort_impl() will called, followed by finish_impl()].
// Upon return from multiplex_impl(), and if finish() [or abort()] was called,
// the call back passed to run() will be called.
// Upon return from the call back, the state machine object might be destructed
// (see below).
// If idle() was called, and the state was (still) current_state,
// then multiplex_impl() will not be called again until the state is
// advanced, or cont() is called.
//
// If the call back function does not call run(), then the state machine is
// deleted when the last LLPointer<> reference is deleted.
// If kill() is called after run() was called, then the call to run() is ignored.
//==================================================================
// Aborting
// If abort() is called before initialize_impl() is entered, then the state
// machine is destructed after the last LLPointer<> reference to it is
// deleted (if any). Note that this is only possible when a child state
// machine is aborted before the parent even runs.
//
// If abort() is called inside its initialize_impl() that initialize_impl()
// should return immediately after.
// if idle(), abort() or finish() are called inside its multiplex_impl() then
// that multiplex_impl() should return immediately after.
//
//==================================================================
// Thread safety
// Only one thread can "run" a state machine at a time; can call 'multiplex_impl'.
//
// Only from inside multiplex_impl (set_state also from initialize_impl), any of the
// following functions can be called:
//
// - set_state(new_state) --> Force the state to new_state. This voids any previous call to set_state() or idle().
// - idle() --> If there was no call to advance_state() since the last call to set_state(current_state))
// then go idle (do nothing until cont() or advance_state() is called). If the current
// state is not current_state, then multiplex_impl shall be reentered immediately upon return.
// - finish() --> Disables any scheduled runs.
// --> finish_impl --> [optional] kill()
// --> call back
// --> [optional] delete
// --> [optional] reset, upon return from multiplex_impl, call initialize_impl and start again at the top of multiplex.
// - yield([engine]) --> give CPU to other state machines before running again, run next from a state machine engine.
// If no engine is passed, the state machine will run in it's default engine (as set during construction).
// - yield_frame()/yield_ms() --> yield(&gMainThreadEngine)
//
// the following function may be called from multiplex_impl() of any state machine (and thus by any thread):
//
// - abort() --> abort_impl
// --> finish()
//
// while the following functions may be called from anywhere (and any thread):
//
// - cont() --> schedules a run if there was no call to set_state() or advance_state() since the last call to idle().
// - advance_state(new_state) --> sets the state to new_state, if the new_state > current_state, and schedules a run (and voids the last call to idle()).
//
// In the above "scheduling a run" means calling multiplex_impl(), but the same holds for any *_impl()
// and the call back: Whenever one of those have to be called, thread_safe_impl() is called to
// determine if the current state machine allows that function to be called by the current thread,
// and if not - by which thread it should be called then (either main thread, or a special state machine
// thread). If thread switching is necessary, the call is literally scheduled in a queue of one
// of those two, otherwise it is run immediately.
//
// However, since only one thread at a time may be calling any *_impl function (except thread_safe_impl())
// or the call back function, it is possible that at the moment scheduling is necessary another thread
// is already running one of those functions. In that case thread_safe_impl() does not consider the
// current thread, but rather the running thread and does not do any scheduling if the running thread
// is ok, rather marks the need to continue running which should be picked up upon return from
// whatever the running thread is calling.
void AIEngine::add(AIStateMachine* state_machine)
{
Dout(dc::statemachine(state_machine->mSMDebug), "Adding state machine [" << (void*)state_machine << "] to " << mName);
engine_state_type_wat engine_state_w(mEngineState);
engine_state_w->list.push_back(QueueElement(state_machine));
if (engine_state_w->waiting)
{
engine_state_w.signal();
}
}
extern void print_statemachine_diagnostics(U64 total_clocks, U64 max_delta, AIEngine::queued_type::const_reference slowest_state_machine);
// MAIN-THREAD
void AIEngine::mainloop(void)
{
queued_type::iterator queued_element, end;
{
engine_state_type_wat engine_state_w(mEngineState);
end = engine_state_w->list.end();
queued_element = engine_state_w->list.begin();
}
U64 total_clocks = 0;
#ifndef LL_RELEASE_FOR_DOWNLOAD
U64 max_delta = 0;
queued_type::value_type slowest_element(NULL);
#endif
while (queued_element != end)
{
AIStateMachine& state_machine(queued_element->statemachine());
U64 start = get_clock_count();
if (!state_machine.sleep(start))
{
state_machine.multiplex(AIStateMachine::normal_run);
}
U64 delta = get_clock_count() - start;
state_machine.add(delta);
total_clocks += delta;
#ifndef LL_RELEASE_FOR_DOWNLOAD
if (delta > max_delta)
{
max_delta = delta;
slowest_element = *queued_element;
}
#endif
bool active = state_machine.active(this); // This locks mState shortly, so it must be called before locking mEngineState because add() locks mEngineState while holding mState.
engine_state_type_wat engine_state_w(mEngineState);
if (!active)
{
Dout(dc::statemachine(state_machine.mSMDebug), "Erasing state machine [" << (void*)&state_machine << "] from " << mName);
engine_state_w->list.erase(queued_element++);
}
else
{
++queued_element;
}
if (total_clocks >= sMaxCount)
{
#ifndef LL_RELEASE_FOR_DOWNLOAD
print_statemachine_diagnostics(total_clocks, max_delta, slowest_element);
#endif
Dout(dc::statemachine, "Sorting " << engine_state_w->list.size() << " state machines.");
engine_state_w->list.sort(QueueElementComp());
break;
}
}
}
void AIEngine::flush(void)
{
engine_state_type_wat engine_state_w(mEngineState);
DoutEntering(dc::statemachine, "AIEngine::flush [" << mName << "]: calling force_killed() on " << engine_state_w->list.size() << " state machines.");
for (queued_type::iterator iter = engine_state_w->list.begin(); iter != engine_state_w->list.end(); ++iter)
{
// To avoid an assertion in ~AIStateMachine.
iter->statemachine().force_killed();
}
engine_state_w->list.clear();
}
// static
U64 AIEngine::sMaxCount;
// static
void AIEngine::setMaxCount(F32 StateMachineMaxTime)
{
llassert(AIThreadID::in_main_thread());
Dout(dc::statemachine, "(Re)calculating AIStateMachine::sMaxCount");
sMaxCount = calc_clock_frequency() * StateMachineMaxTime / 1000;
}
#ifdef CWDEBUG
char const* AIStateMachine::event_str(event_type event)
{
switch(event)
{
AI_CASE_RETURN(initial_run);
AI_CASE_RETURN(schedule_run);
AI_CASE_RETURN(normal_run);
AI_CASE_RETURN(insert_abort);
}
llassert(false);
return "UNKNOWN EVENT";
}
#endif
void AIStateMachine::multiplex(event_type event)
{
// If this fails then you are using a pointer to a state machine instead of an LLPointer.
llassert(event == initial_run || getNumRefs() > 0);
DoutEntering(dc::statemachine(mSMDebug), "AIStateMachine::multiplex(" << event_str(event) << ") [" << (void*)this << "]");
base_state_type state;
state_type run_state;
// Critical area of mState.
{
multiplex_state_type_rat state_r(mState);
// If another thread is already running multiplex() then it will pick up
// our need to run (by us having set need_run), so there is no need to run
// ourselves.
llassert(!mMultiplexMutex.isSelfLocked()); // We may never enter recursively!
if (!mMultiplexMutex.tryLock())
{
Dout(dc::statemachine(mSMDebug), "Leaving because it is already being run [" << (void*)this << "]");
return;
}
//===========================================
// Start of critical area of mMultiplexMutex.
// If another thread already called begin_loop() since we needed a run,
// then we must not schedule a run because that could lead to running
// the same state twice. Note that if need_run was reset in the mean
// time and then set again, then it can't hurt to schedule a run since
// we should indeed run, again.
if (event == schedule_run && !sub_state_type_rat(mSubState)->need_run)
{
Dout(dc::statemachine(mSMDebug), "Leaving because it was already being run [" << (void*)this << "]");
return;
}
// We're at the beginning of multiplex, about to actually run it.
// Make a copy of the states.
run_state = begin_loop((state = state_r->base_state));
}
// End of critical area of mState.
bool keep_looping;
bool destruct = false;
do
{
if (event == normal_run)
{
#ifdef CWDEBUG
if (state == bs_multiplex)
Dout(dc::statemachine(mSMDebug), "Running state bs_multiplex / " << state_str_impl(run_state) << " [" << (void*)this << "]");
else
Dout(dc::statemachine(mSMDebug), "Running state " << state_str(state) << " [" << (void*)this << "]");
#endif
#ifdef SHOW_ASSERT
// This debug code checks that each state machine steps precisely through each of it's states correctly.
if (state != bs_reset)
{
switch(mDebugLastState)
{
case bs_reset:
llassert(state == bs_initialize || state == bs_killed);
break;
case bs_initialize:
llassert(state == bs_multiplex || state == bs_abort);
break;
case bs_multiplex:
llassert(state == bs_multiplex || state == bs_finish || state == bs_abort);
break;
case bs_abort:
llassert(state == bs_finish);
break;
case bs_finish:
llassert(state == bs_callback);
break;
case bs_callback:
llassert(state == bs_killed || state == bs_reset);
break;
case bs_killed:
llassert(state == bs_killed);
break;
}
}
// More sanity checks.
if (state == bs_multiplex)
{
// set_state is only called from multiplex_impl and therefore synced with mMultiplexMutex.
mDebugShouldRun |= mDebugSetStatePending;
// Should we run at all?
llassert(mDebugShouldRun);
}
// Any previous reason to run is voided by actually running.
mDebugShouldRun = false;
#endif
mRunMutex.lock();
// Now we are actually running a single state.
// If abort() was called at any moment before, we execute that state instead.
bool const late_abort = (state == bs_multiplex || state == bs_initialize) && sub_state_type_rat(mSubState)->aborted;
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.
// 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(mSMDebug), "Late abort detected! Running state " << state_str(state) << " instead [" << (void*)this << "]");
#endif
}
#ifdef SHOW_ASSERT
mDebugLastState = state;
// Make sure we only call ref() once and in balance with unref().
if (state == bs_initialize)
{
// This -- and call to ref() (and the test when we're about to call unref()) -- is all done in the critical area of mMultiplexMutex.
llassert(!mDebugRefCalled);
mDebugRefCalled = true;
}
#endif
switch(state)
{
case bs_reset:
// 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();
initialize_impl();
break;
case bs_multiplex:
llassert(!mDebugAborted);
multiplex_impl(run_state);
break;
case bs_abort:
abort_impl();
break;
case bs_finish:
sub_state_type_wat(mSubState)->reset = false; // By default, halt state machines when finished.
finish_impl(); // Call run() from finish_impl() or the call back to restart from the beginning.
break;
case bs_callback:
callback();
break;
case bs_killed:
mRunMutex.unlock();
// bs_killed is handled when it is set. So, this must be a re-entry.
// We can only get here when being called by an engine that we were added to before we were killed.
// This should already be have been set to NULL to indicate that we want to be removed from that engine.
llassert(!multiplex_state_type_rat(mState)->current_engine);
// Do not call unref() twice.
return;
}
mRunMutex.unlock();
}
{
multiplex_state_type_wat state_w(mState);
//=================================
// Start of critical area of mState
// Unless the state is bs_multiplex or bs_killed, the state machine needs to keep calling multiplex().
bool need_new_run = true;
if (event == normal_run || event == insert_abort)
{
sub_state_type_rat sub_state_r(mSubState);
if (event == normal_run)
{
// Switch base state as function of sub state.
switch(state)
{
case bs_reset:
if (sub_state_r->aborted)
{
// We have been aborted before we could even initialize, no de-initialization is possible.
state_w->base_state = bs_killed;
// Stop running.
need_new_run = false;
}
else
{
// run() was called: call initialize_impl() next.
state_w->base_state = bs_initialize;
}
break;
case bs_initialize:
if (sub_state_r->aborted)
{
// initialize_impl() called abort.
state_w->base_state = bs_abort;
}
else
{
// Start actually running.
state_w->base_state = bs_multiplex;
// If the state is bs_multiplex we only need to run again when need_run was set again in the meantime or when this state machine isn't idle.
need_new_run = sub_state_r->need_run || !sub_state_r->idle;
}
break;
case bs_multiplex:
if (sub_state_r->aborted)
{
// abort() was called.
state_w->base_state = bs_abort;
}
else if (sub_state_r->finished)
{
// finish() was called.
state_w->base_state = bs_finish;
}
else
{
// Continue in bs_multiplex.
// If the state is bs_multiplex we only need to run again when need_run was set again in the meantime or when this state machine isn't idle.
need_new_run = sub_state_r->need_run || !sub_state_r->idle;
// If this fails then the run state didn't change and neither idle() nor yield() was called.
llassert_always(!(need_new_run && !sub_state_r->skip_idle && !mYieldEngine && sub_state_r->run_state == run_state));
}
break;
case bs_abort:
// After calling abort_impl(), call finish_impl().
state_w->base_state = bs_finish;
break;
case bs_finish:
// After finish_impl(), call the call back function.
state_w->base_state = bs_callback;
break;
case bs_callback:
if (sub_state_r->reset)
{
// run() was called (not followed by kill()).
state_w->base_state = bs_reset;
}
else
{
// After the call back, we're done.
state_w->base_state = bs_killed;
// Call unref().
destruct = true;
// Stop running.
need_new_run = false;
}
break;
default: // bs_killed
// We never get here.
break;
}
}
else // event == insert_abort
{
// We have been aborted, but we're idle. If we'd just schedule a new run below, it would re-run
// the last state before the abort is handled. What we really need is to pick up as if the abort
// was handled directly after returning from the last run. If we're not running anymore, then
// do nothing as the state machine already ran and things should be processed normally
// (in that case this is just a normal schedule which can't harm because we're can't accidently
// re-run an old run_state).
if (state_w->base_state == bs_multiplex) // Still running?
{
// See the switch above for case bs_multiplex.
llassert(sub_state_r->aborted);
// abort() was called.
state_w->base_state = bs_abort;
}
}
#ifdef CWDEBUG
if (state != state_w->base_state)
Dout(dc::statemachine(mSMDebug), "Base state changed from " << state_str(state) << " to " << state_str(state_w->base_state) <<
"; need_new_run = " << (need_new_run ? "true" : "false") << " [" << (void*)this << "]");
#endif
}
// Figure out in which engine we should run.
AIEngine* engine = mYieldEngine ? mYieldEngine : (state_w->current_engine ? state_w->current_engine : mDefaultEngine);
// And the current engine we're running in.
AIEngine* current_engine = (event == normal_run) ? state_w->current_engine : NULL;
// Immediately run again if yield() wasn't called and it's OK to run in this thread.
// Note that when it's OK to run in any engine (mDefaultEngine is NULL) then the last
// compare is also true when current_engine == NULL.
keep_looping = need_new_run && !mYieldEngine && engine == current_engine;
mYieldEngine = NULL;
if (keep_looping)
{
// Start a new loop.
run_state = begin_loop((state = state_w->base_state));
event = normal_run;
}
else
{
if (need_new_run)
{
// Add us to an engine if necessary.
if (engine != state_w->current_engine)
{
// engine can't be NULL here: it can only be NULL if mDefaultEngine is NULL.
engine->add(this);
// Mark that we're added to this engine, and at the same time, that we're not added to the previous one.
state_w->current_engine = engine;
}
#ifdef SHOW_ASSERT
// We are leaving the loop, but we're not idle. The statemachine should re-enter the loop again.
mDebugShouldRun = true;
#endif
}
else
{
// Remove this state machine from any engine,
// causing the engine to remove us.
state_w->current_engine = NULL;
}
#ifdef SHOW_ASSERT
// Mark that we stop running the loop.
mThreadId.clear();
if (destruct)
{
// We're about to call unref(). Make sure we call that in balance with ref()!
llassert(mDebugRefCalled);
mDebugRefCalled = false;
}
#endif
// End of critical area of mMultiplexMutex.
//=========================================
// Release the lock on mMultiplexMutex *first*, before releasing the lock on mState,
// to avoid to ever call the tryLock() and fail, while this thread isn't still
// BEFORE the critical area of mState!
mMultiplexMutex.unlock();
}
// Now it is safe to leave the critical area of mState as the tryLock won't fail anymore.
// (Or, if we didn't release mMultiplexMutex because keep_looping is true, then this
// end of the critical area of mState is equivalent to the first critical area in this
// function.
// End of critical area of mState.
//================================
}
}
while (keep_looping);
if (destruct)
{
unref();
}
}
AIStateMachine::state_type AIStateMachine::begin_loop(base_state_type base_state)
{
DoutEntering(dc::statemachine(mSMDebug), "AIStateMachine::begin_loop(" << state_str(base_state) << ") [" << (void*)this << "]");
sub_state_type_wat sub_state_w(mSubState);
// Honor a subsequent call to idle() (only necessary in bs_multiplex, but it doesn't hurt to reset this flag in other states too).
sub_state_w->skip_idle = false;
// Mark that we're about to honor all previous run requests.
sub_state_w->need_run = false;
// Honor previous calls to advance_state() (once run_state is initialized).
if (base_state == bs_multiplex && sub_state_w->advance_state > sub_state_w->run_state)
{
Dout(dc::statemachine(mSMDebug), "Copying advance_state to run_state, because it is larger [" << state_str_impl(sub_state_w->advance_state) << " > " << state_str_impl(sub_state_w->run_state) << "]");
sub_state_w->run_state = sub_state_w->advance_state;
}
#ifdef SHOW_ASSERT
else
{
// If advance_state wasn't honored then it isn't a reason to run.
// We're running anyway, but that should be because set_state() was called.
mDebugAdvanceStatePending = false;
}
#endif
sub_state_w->advance_state = 0;
#ifdef SHOW_ASSERT
// Mark that we're running the loop.
mThreadId.reset();
// This point marks handling cont().
mDebugShouldRun |= mDebugContPending;
mDebugContPending = false;
// This point also marks handling advance_state().
mDebugShouldRun |= mDebugAdvanceStatePending;
mDebugAdvanceStatePending = false;
#endif
// Make a copy of the state that we're about to run.
return sub_state_w->run_state;
}
void AIStateMachine::run(AIStateMachine* parent, state_type new_parent_state, bool abort_parent, bool on_abort_signal_parent, AIEngine* default_engine)
{
DoutEntering(dc::statemachine(mSMDebug), "AIStateMachine::run(" <<
(void*)parent << ", " <<
(parent ? parent->state_str_impl(new_parent_state) : "NA") <<
", abort_parent = " << (abort_parent ? "true" : "false") <<
", on_abort_signal_parent = " << (on_abort_signal_parent ? "true" : "false") <<
", default_engine = " << (default_engine ? default_engine->name() : "NULL") << ") [" << (void*)this << "]");
#ifdef SHOW_ASSERT
{
multiplex_state_type_rat state_r(mState);
// Can only be run when in one of these states.
llassert(state_r->base_state == bs_reset || state_r->base_state == bs_finish || state_r->base_state == bs_callback);
// Must be the first time we're being run, or we must be called from finish_impl or a callback function.
llassert(!(state_r->base_state == bs_reset && (mParent || mCallback)));
}
#endif
// Store the requested default engine.
mDefaultEngine = default_engine;
// Initialize sleep timer.
mSleep = 0;
// Allow NULL to be passed as parent to signal that we want to reuse the old one.
if (parent)
{
mParent = parent;
// In that case remove any old callback!
if (mCallback)
{
delete mCallback;
mCallback = NULL;
}
mNewParentState = new_parent_state;
mAbortParent = abort_parent;
mOnAbortSignalParent = on_abort_signal_parent;
}
// If abort_parent is requested then a parent must be provided.
llassert(!abort_parent || mParent);
// If a parent is provided, it must be running.
llassert(!mParent || mParent->running());
// Start from the beginning.
reset();
}
void AIStateMachine::run(callback_type::signal_type::slot_type const& slot, AIEngine* default_engine)
{
DoutEntering(dc::statemachine(mSMDebug), "AIStateMachine::run(<slot>, default_engine = " << default_engine->name() << ") [" << (void*)this << "]");
#ifdef SHOW_ASSERT
{
multiplex_state_type_rat state_r(mState);
// Can only be run when in one of these states.
llassert(state_r->base_state == bs_reset || state_r->base_state == bs_finish || state_r->base_state == bs_callback);
// Must be the first time we're being run, or we must be called from finish_impl or a callback function.
llassert(!(state_r->base_state == bs_reset && (mParent || mCallback)));
}
#endif
// Store the requested default engine.
mDefaultEngine = default_engine;
// Initialize sleep timer.
mSleep = 0;
// Clean up any old callbacks.
mParent = NULL;
if (mCallback)
{
delete mCallback;
mCallback = NULL;
}
// Create new call back.
mCallback = new callback_type(slot);
// Start from the beginning.
reset();
}
void AIStateMachine::callback(void)
{
DoutEntering(dc::statemachine(mSMDebug), "AIStateMachine::callback() [" << (void*)this << "]");
bool aborted = sub_state_type_rat(mSubState)->aborted;
if (mParent)
{
// It is possible that the parent is not running when the parent is in fact aborting and called
// abort on this object from it's abort_impl function. It that case we don't want to recursively
// call abort again (or change it's state).
if (mParent->running())
{
if (aborted && mAbortParent)
{
mParent->abort();
mParent = NULL;
}
else if (!aborted || mOnAbortSignalParent)
{
mParent->advance_state(mNewParentState);
}
}
}
if (mCallback)
{
mCallback->callback(!aborted);
if (multiplex_state_type_rat(mState)->base_state != bs_reset)
{
delete mCallback;
mCallback = NULL;
mParent = NULL;
}
}
else
{
// Not restarted by callback. Allow run() to be called later on.
mParent = NULL;
}
}
void AIStateMachine::force_killed(void)
{
multiplex_state_type_wat state_w(mState);
state_w->base_state = bs_killed;
}
void AIStateMachine::kill(void)
{
DoutEntering(dc::statemachine(mSMDebug), "AIStateMachine::kill() [" << (void*)this << "]");
#ifdef SHOW_ASSERT
{
multiplex_state_type_rat state_r(mState);
// kill() may only be called from the call back function.
llassert(state_r->base_state == bs_callback);
// May only be called by the thread that is holding mMultiplexMutex.
llassert(mThreadId.equals_current_thread());
}
#endif
sub_state_type_wat sub_state_w(mSubState);
// Void last call to run() (ie from finish_impl()), if any.
sub_state_w->reset = false;
}
void AIStateMachine::reset()
{
DoutEntering(dc::statemachine(mSMDebug), "AIStateMachine::reset() [" << (void*)this << "]");
#ifdef SHOW_ASSERT
mDebugAborted = false;
mDebugContPending = false;
mDebugSetStatePending = false;
mDebugRefCalled = false;
#endif
mRuntime = 0;
bool inside_multiplex;
{
multiplex_state_type_rat state_r(mState);
// reset() is only called from run(), which may only be called when just created, from finish_impl() or from the call back function.
llassert(state_r->base_state == bs_reset || state_r->base_state == bs_finish || state_r->base_state == bs_callback);
inside_multiplex = state_r->base_state != bs_reset;
}
{
sub_state_type_wat sub_state_w(mSubState);
// Reset.
sub_state_w->aborted = sub_state_w->finished = false;
// Signal that we want to start running from the beginning.
sub_state_w->reset = true;
// Start running.
sub_state_w->idle = false;
// We're not waiting for a condition.
sub_state_w->blocked = NULL;
// Keep running till we reach at least bs_multiplex.
sub_state_w->need_run = true;
}
if (!inside_multiplex)
{
// Kick start the state machine.
multiplex(initial_run);
}
}
void AIStateMachine::set_state(state_type new_state)
{
DoutEntering(dc::statemachine(mSMDebug), "AIStateMachine::set_state(" << state_str_impl(new_state) << ") [" << (void*)this << "]");
#ifdef SHOW_ASSERT
{
multiplex_state_type_rat state_r(mState);
// set_state() may only be called from initialize_impl() or multiplex_impl().
llassert(state_r->base_state == bs_initialize || state_r->base_state == bs_multiplex);
// May only be called by the thread that is holding mMultiplexMutex. If this fails, you probably called set_state() by accident instead of advance_state().
llassert(mThreadId.equals_current_thread());
}
#endif
sub_state_type_wat sub_state_w(mSubState);
// It should never happen that set_state() is called while we're blocked.
llassert(!sub_state_w->blocked);
// Force current state to the requested state.
sub_state_w->run_state = new_state;
// Void last call to advance_state.
sub_state_w->advance_state = 0;
// Void last call to idle(), if any.
sub_state_w->idle = false;
// Honor a subsequent call to idle().
sub_state_w->skip_idle = false;
#ifdef SHOW_ASSERT
// We should run. This can only be cancelled by a call to idle().
mDebugSetStatePending = true;
#endif
}
void AIStateMachine::advance_state(state_type new_state)
{
DoutEntering(dc::statemachine(mSMDebug), "AIStateMachine::advance_state(" << state_str_impl(new_state) << ") [" << (void*)this << "]");
{
sub_state_type_wat sub_state_w(mSubState);
// Ignore call to advance_state when the currently queued state is already greater or equal to the requested state.
if (sub_state_w->advance_state >= new_state)
{
Dout(dc::statemachine(mSMDebug), "Ignored, because " << state_str_impl(sub_state_w->advance_state) << " >= " << state_str_impl(new_state) << ".");
return;
}
// Ignore call to advance_state when the current state is greater than the requested state: the new state would be
// ignored in begin_loop(), as is already remarked there: an advanced state that is not honored is not a reason to run.
// This call might as well not have happened. Not returning here is a bug because that is effectively a cont(), while
// the state change is and should be being ignored: the statemachine would start running it's current state (again).
if (sub_state_w->run_state > new_state)
{
Dout(dc::statemachine(mSMDebug), "Ignored, because " << state_str_impl(sub_state_w->run_state) << " > " << state_str_impl(new_state) << " (current state).");
return;
}
// Increment state.
sub_state_w->advance_state = new_state;
// Void last call to idle(), if any.
sub_state_w->idle = false;
// Ignore a call to idle if it occurs before we leave multiplex_impl().
sub_state_w->skip_idle = true;
// No longer say we woke up when signalled() is called.
if (sub_state_w->blocked)
{
Dout(dc::statemachine(mSMDebug), "Removing statemachine from condition " << (void*)sub_state_w->blocked);
sub_state_w->blocked->remove(this);
sub_state_w->blocked = NULL;
}
// Mark that a re-entry of multiplex() is necessary.
sub_state_w->need_run = true;
#ifdef SHOW_ASSERT
// From this moment on.
mDebugAdvanceStatePending = true;
// If the new state is equal to the current state, then this should be considered to be a cont()
// because also equal states are ignored in begin_loop(). However, unlike a cont() we ignore a call
// to idle() when the statemachine is already running in this state (because that is a race condition
// and ignoring the idle() is the most logical thing to do then). Hence we treated this as a full
// fletched advance_state but need to tell the debug code that it's really also a cont().
if (sub_state_w->run_state == new_state)
{
// From this moment.
mDebugContPending = true;
}
#endif
}
if (!mMultiplexMutex.isSelfLocked())
{
multiplex(schedule_run);
}
}
void AIStateMachine::idle(void)
{
DoutEntering(dc::statemachine(mSMDebug), "AIStateMachine::idle() [" << (void*)this << "]");
#ifdef SHOW_ASSERT
{
multiplex_state_type_rat state_r(mState);
// idle() may only be called from initialize_impl() or multiplex_impl().
llassert(state_r->base_state == bs_multiplex || state_r->base_state == bs_initialize);
// May only be called by the thread that is holding mMultiplexMutex.
llassert(mThreadId.equals_current_thread());
}
// idle() following set_state() cancels the reason to run because of the call to set_state.
mDebugSetStatePending = false;
#endif
sub_state_type_wat sub_state_w(mSubState);
// As idle may only be called from within the state machine, it should never happen that the state machine is already idle.
llassert(!sub_state_w->idle);
// Ignore call to idle() when advance_state() was called since last call to set_state().
if (sub_state_w->skip_idle)
{
Dout(dc::statemachine(mSMDebug), "Ignored, because skip_idle is true (advance_state() was called last).");
return;
}
// Mark that we are idle.
sub_state_w->idle = true;
// Not sleeping (anymore).
mSleep = 0;
}
// This function is very much like idle().
void AIStateMachine::wait(AIConditionBase& condition)
{
DoutEntering(dc::statemachine(mSMDebug), "AIStateMachine::wait(" << (void*)&condition << ") [" << (void*)this << "]");
#ifdef SHOW_ASSERT
{
multiplex_state_type_rat state_r(mState);
// wait() may only be called multiplex_impl().
llassert(state_r->base_state == bs_multiplex);
// May only be called by the thread that is holding mMultiplexMutex.
llassert(mThreadId.equals_current_thread());
}
// wait() following set_state() cancels the reason to run because of the call to set_state.
mDebugSetStatePending = false;
#endif
sub_state_type_wat sub_state_w(mSubState);
// As wait() may only be called from within the state machine, it should never happen that the state machine is already idle.
llassert(!sub_state_w->idle);
// Ignore call to wait() when advance_state() was called since last call to set_state().
if (sub_state_w->skip_idle)
{
Dout(dc::statemachine(mSMDebug), "Ignored, because skip_idle is true (advance_state() was called last).");
return;
}
// Register ourselves with the condition object.
condition.wait(this);
// Mark that we are idle.
sub_state_w->idle = true;
// Mark that we are waiting for a condition.
sub_state_w->blocked = &condition;
// Not sleeping (anymore).
mSleep = 0;
}
void AIStateMachine::cont(void)
{
DoutEntering(dc::statemachine(mSMDebug), "AIStateMachine::cont() [" << (void*)this << "]");
{
sub_state_type_wat sub_state_w(mSubState);
// Void last call to idle(), if any.
sub_state_w->idle = false;
// No longer say we woke up when signalled() is called.
if (sub_state_w->blocked)
{
Dout(dc::statemachine(mSMDebug), "Removing statemachine from condition " << (void*)sub_state_w->blocked);
sub_state_w->blocked->remove(this);
sub_state_w->blocked = NULL;
}
// Mark that a re-entry of multiplex() is necessary.
sub_state_w->need_run = true;
#ifdef SHOW_ASSERT
// From this moment.
mDebugContPending = true;
#endif
}
if (!mMultiplexMutex.isSelfLocked())
{
multiplex(schedule_run);
}
}
// This function is very much like cont(), except that it has no effect when we are not in a blocked state.
// Returns true if the state machine was unblocked, false if it was already unblocked.
bool AIStateMachine::signalled(void)
{
DoutEntering(dc::statemachine(mSMDebug), "AIStateMachine::signalled() [" << (void*)this << "]");
{
sub_state_type_wat sub_state_w(mSubState);
// Test if we are blocked or not.
if (sub_state_w->blocked)
{
Dout(dc::statemachine(mSMDebug), "Removing statemachine from condition " << (void*)sub_state_w->blocked);
sub_state_w->blocked->remove(this);
sub_state_w->blocked = NULL;
}
else
{
return false;
}
// Void last call to wait().
sub_state_w->idle = false;
// Mark that a re-entry of multiplex() is necessary.
sub_state_w->need_run = true;
#ifdef SHOW_ASSERT
// From this moment.
mDebugContPending = true;
#endif
}
if (!mMultiplexMutex.isSelfLocked())
{
multiplex(schedule_run);
}
return true;
}
void AIStateMachine::abort(void)
{
DoutEntering(dc::statemachine(mSMDebug), "AIStateMachine::abort() [" << (void*)this << "]");
bool is_waiting = false;
{
multiplex_state_type_rat state_r(mState);
sub_state_type_wat sub_state_w(mSubState);
// Mark that we are aborted, iff we didn't already finish.
sub_state_w->aborted = !sub_state_w->finished;
// No longer say we woke up when signalled() is called.
if (sub_state_w->blocked)
{
Dout(dc::statemachine(mSMDebug), "Removing statemachine from condition " << (void*)sub_state_w->blocked);
sub_state_w->blocked->remove(this);
sub_state_w->blocked = NULL;
}
// Mark that a re-entry of multiplex() is necessary.
sub_state_w->need_run = true;
// Schedule a new run when this state machine is waiting.
is_waiting = state_r->base_state == bs_multiplex && sub_state_w->idle;
}
if (is_waiting && !mMultiplexMutex.isSelfLocked())
{
multiplex(insert_abort);
}
// Block until the current run finished.
if (!mRunMutex.tryLock())
{
llwarns << "AIStateMachine::abort() blocks because the statemachine is still executing code in another thread." << llendl;
mRunMutex.lock();
}
mRunMutex.unlock();
#ifdef SHOW_ASSERT
// When abort() returns, it may never run again.
mDebugAborted = true;
#endif
}
void AIStateMachine::finish(void)
{
DoutEntering(dc::statemachine(mSMDebug), "AIStateMachine::finish() [" << (void*)this << "]");
#ifdef SHOW_ASSERT
{
multiplex_state_type_rat state_r(mState);
// finish() may only be called from multiplex_impl().
llassert(state_r->base_state == bs_multiplex);
// May only be called by the thread that is holding mMultiplexMutex.
llassert(mThreadId.equals_current_thread());
}
#endif
sub_state_type_wat sub_state_w(mSubState);
// finish() should not be called when idle.
llassert(!sub_state_w->idle);
// Mark that we are finished.
sub_state_w->finished = true;
}
void AIStateMachine::yield(void)
{
DoutEntering(dc::statemachine(mSMDebug), "AIStateMachine::yield() [" << (void*)this << "]");
multiplex_state_type_rat state_r(mState);
// yield() may only be called from multiplex_impl().
llassert(state_r->base_state == bs_multiplex);
// May only be called by the thread that is holding mMultiplexMutex.
llassert(mThreadId.equals_current_thread());
// Set mYieldEngine to the best non-NUL value.
mYieldEngine = state_r->current_engine ? state_r->current_engine : (mDefaultEngine ? mDefaultEngine : &gStateMachineThreadEngine);
}
void AIStateMachine::yield(AIEngine* engine)
{
llassert(engine);
DoutEntering(dc::statemachine(mSMDebug), "AIStateMachine::yield(" << engine->name() << ") [" << (void*)this << "]");
#ifdef SHOW_ASSERT
{
multiplex_state_type_rat state_r(mState);
// yield() may only be called from multiplex_impl().
llassert(state_r->base_state == bs_multiplex);
// May only be called by the thread that is holding mMultiplexMutex.
llassert(mThreadId.equals_current_thread());
}
#endif
mYieldEngine = engine;
}
bool AIStateMachine::yield_if_not(AIEngine* engine)
{
if (engine && multiplex_state_type_rat(mState)->current_engine != engine)
{
yield(engine);
return true;
}
return false;
}
void AIStateMachine::yield_frame(unsigned int frames)
{
DoutEntering(dc::statemachine(mSMDebug), "AIStateMachine::yield_frame(" << frames << ") [" << (void*)this << "]");
mSleep = -(S64)frames;
// Sleeping is always done from the main thread.
yield(&gMainThreadEngine);
}
void AIStateMachine::yield_ms(unsigned int ms)
{
DoutEntering(dc::statemachine(mSMDebug), "AIStateMachine::yield_ms(" << ms << ") [" << (void*)this << "]");
mSleep = get_clock_count() + calc_clock_frequency() * ms / 1000;
// Sleeping is always done from the main thread.
yield(&gMainThreadEngine);
}
char const* AIStateMachine::state_str(base_state_type state)
{
switch(state)
{
AI_CASE_RETURN(bs_reset);
AI_CASE_RETURN(bs_initialize);
AI_CASE_RETURN(bs_multiplex);
AI_CASE_RETURN(bs_abort);
AI_CASE_RETURN(bs_finish);
AI_CASE_RETURN(bs_callback);
AI_CASE_RETURN(bs_killed);
}
llassert(false);
return "UNKNOWN BASE STATE";
}
AIEngine gMainThreadEngine("gMainThreadEngine");
AIEngine gStateMachineThreadEngine("gStateMachineThreadEngine");
// State Machine Thread main loop.
void AIEngine::threadloop(void)
{
queued_type::iterator queued_element, end;
{
engine_state_type_wat engine_state_w(mEngineState);
end = engine_state_w->list.end();
queued_element = engine_state_w->list.begin();
if (queued_element == end)
{
// Nothing to do. Wait till something is added to the queue again.
engine_state_w->waiting = true;
engine_state_w.wait();
engine_state_w->waiting = false;
return;
}
}
do
{
AIStateMachine& state_machine(queued_element->statemachine());
state_machine.multiplex(AIStateMachine::normal_run);
bool active = state_machine.active(this); // This locks mState shortly, so it must be called before locking mEngineState because add() locks mEngineState while holding mState.
engine_state_type_wat engine_state_w(mEngineState);
if (!active)
{
Dout(dc::statemachine(state_machine.mSMDebug), "Erasing state machine [" << (void*)&state_machine << "] from " << mName);
engine_state_w->list.erase(queued_element++);
}
else
{
++queued_element;
}
}
while (queued_element != end);
}
void AIEngine::wake_up(void)
{
engine_state_type_wat engine_state_w(mEngineState);
if (engine_state_w->waiting)
{
engine_state_w.signal();
}
}
//-----------------------------------------------------------------------------
// AIEngineThread
class AIEngineThread : public LLThread
{
public:
static AIEngineThread* sInstance;
bool volatile mRunning;
public:
// MAIN-THREAD
AIEngineThread(void);
virtual ~AIEngineThread();
protected:
virtual void run(void);
};
//static
AIEngineThread* AIEngineThread::sInstance;
AIEngineThread::AIEngineThread(void) : LLThread("AIEngineThread"), mRunning(true)
{
}
AIEngineThread::~AIEngineThread(void)
{
}
void AIEngineThread::run(void)
{
while(mRunning)
{
gStateMachineThreadEngine.threadloop();
}
}
void startEngineThread(void)
{
AIEngineThread::sInstance = new AIEngineThread;
AIEngineThread::sInstance->start();
}
void stopEngineThread(void)
{
AIEngineThread::sInstance->mRunning = false;
gStateMachineThreadEngine.wake_up();
int count = 401;
while(--count && !AIEngineThread::sInstance->isStopped())
{
ms_sleep(10);
}
llinfos << "State machine thread" << (!AIEngineThread::sInstance->isStopped() ? " not" : "") << " stopped after " << ((400 - count) * 10) << "ms." << llendl;
}