Rewrite of AIStateMachine, version 2.

This commit is contained in:
Aleric Inglewood
2013-03-01 00:59:06 +01:00
parent 4851cc174e
commit c4dceaf3e9
22 changed files with 1375 additions and 974 deletions

File diff suppressed because it is too large Load Diff

View File

@@ -2,7 +2,7 @@
* @file aistatemachine.h * @file aistatemachine.h
* @brief State machine base class * @brief State machine base class
* *
* Copyright (c) 2010, Aleric Inglewood. * Copyright (c) 2010 - 2013, Aleric Inglewood.
* *
* This program is free software: you can redistribute it and/or modify * 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 * it under the terms of the GNU General Public License as published by
@@ -26,397 +26,278 @@
* *
* 01/03/2010 * 01/03/2010
* Initial version, written by Aleric Inglewood @ SL * Initial version, written by Aleric Inglewood @ SL
*
* 28/02/2013
* Rewritten from scratch to fully support threading.
*/ */
#ifndef AISTATEMACHINE_H #ifndef AISTATEMACHINE_H
#define AISTATEMACHINE_H #define AISTATEMACHINE_H
#include "aithreadsafe.h" #include "aithreadsafe.h"
#include "lltimer.h" #include <llpointer.h>
#include <list>
#include <boost/signals2.hpp> #include <boost/signals2.hpp>
//! class AIStateMachine;
// A AIStateMachine is a base class that allows derived classes to
// go through asynchronous states, while the code still appears to class AIEngine
// be more or less sequential. {
// private:
// These state machine objects can be reused to build more complex struct QueueElementComp;
// objects. class QueueElement {
// private:
// It is important to note that each state has a duality: the object LLPointer<AIStateMachine> mStateMachine;
// can have a state that will cause a corresponding function to be U64 mRuntime;
// called; and often that function will end with changing the state
// again, to signal that it was handled. It is easy to confuse the public:
// function of a state with the state at the end of the function. QueueElement(AIStateMachine* statemachine) : mStateMachine(statemachine), mRuntime(0) { }
// For example, the state "initialize" could cause the member friend bool operator==(QueueElement const& e1, QueueElement const& e2) { return e1.mStateMachine == e2.mStateMachine; }
// function 'init()' to be called, and at the end one would be friend bool operator!=(QueueElement const& e1, QueueElement const& e2) { return e1.mStateMachine != e2.mStateMachine; }
// inclined to set the state to "initialized". However, this is the friend struct QueueElementComp;
// wrong approach: the correct use of state names does reflect the
// functions that will be called, never the function that just was AIStateMachine const& statemachine(void) const { return *mStateMachine; }
// called. AIStateMachine& statemachine(void) { return *mStateMachine; }
// void add(U64 count) { mRuntime += count; }
// Each (derived) class goes through a series of states as follows: };
// struct QueueElementComp {
// Creation bool operator()(QueueElement const& e1, QueueElement const& e2) const { return e1.mRuntime < e2.mRuntime; }
// | };
// v
// (idle) <----. Idle until run() is called. public:
// | | typedef std::list<QueueElement> queued_type;
// Initialize | Calls initialize_impl(). struct engine_state_type {
// | | queued_type list;
// | (idle) | Idle until cont() or set_state() is called. bool waiting;
// | | ^ | engine_state_type(void) : waiting(false) { }
// v v | | };
// .-------. | |
// | Run |_, | Call multiplex_impl() until idle(), abort() or finish() is called. private:
// '-------' | AIThreadSafeSimpleDC<engine_state_type, LLCondition> mEngineState;
// | | | typedef AIAccessConst<engine_state_type, LLCondition> engine_state_type_crat;
// v | | typedef AIAccess<engine_state_type, LLCondition> engine_state_type_rat;
// Abort | | Calls abort_impl(). typedef AIAccess<engine_state_type, LLCondition> engine_state_type_wat;
// | | | char const* mName;
// v v |
// Finish | Calls finish_impl() (which may call kill()) or static U64 sMaxCount;
// | | | the callback function passed to run(), if any,
// | v | public:
// | Callback | which may call kill() and/or run(). AIEngine(char const* name) : mName(name) { }
// | | | |
// | | `-----' void add(AIStateMachine* state_machine);
// v v
// Killed Delete the statemachine (all statemachines must be allocated with new). void mainloop(void);
// void threadloop(void);
// Each state causes corresponding code to be called. void wake_up(void);
// Finish cleans up whatever is done by Initialize. void flush(void);
// Abort should clean up additional things done by Run.
// char const* name(void) const { return mName; }
// The Run state is entered by calling run().
// static void setMaxCount(F32 StateMachineMaxTime);
// While the base class is in the Run 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 state Run. extern AIEngine gMainThreadEngine;
// extern AIEngine gStateMachineThreadEngine;
// A derived class can exit the Run state by calling one of two methods:
// abort() in case of failure, or finish() in case of success. class AIStateMachine : public LLThreadSafeRefCount
// Respectively these set the state to Abort and Finish. {
// public:
// finish_impl may call kill() for a (default) destruction upon finish. typedef U32 state_type; //!< The type of run_state
// Even in that case the callback (passed to run()) may call run() again,
// which overrides the request for a default kill. Or, if finish_impl protected:
// doesn't call kill() the callback may call kill() to request the // The type of event that causes multiplex() to be called.
// destruction of the state machine object. enum event_type {
// schedule_run,
// State machines are run from the "idle" part of the viewer main loop. normal_run,
// Often a state machine has nothing to do however. In that case it can insert_abort
// call the method idle(). This will stop the state machine until };
// external code changes it's state (by calling set_state()), or calls // The type of mState
// cont() to continue with the last state.
//
// The methods of the derived class call set_state() to change their
// own state within the bs_run state, or by calling either abort()
// or finish().
//
// Restarting a finished state machine can also be done by calling run(),
// which will cause a re-initialize.
//
// Derived classes should implement the following constants:
//
// static state_type const min_state = first_state;
// static state_type const max_state = last_state + 1;
//
// Where first_state should be equal to BaseClass::max_state.
// These should represent the minimum and (one past) the maximum
// values of mRunState.
//
// virtual void initialize_impl(void)
//
// Initializes the derived class.
//
// virtual void multiplex_impl(void);
//
// This method should handle mRunState in a switch.
// For example:
//
// switch(mRunState)
// {
// case foo:
// handle_foo();
// break;
// case wait_state:
// if (still_waiting())
// {
// idle();
// break;
// }
// set_state(working);
// /*fall-through*/
// case working:
// do_work();
// if (failure())
// abort();
// break;
// case etc:
// etc();
// finish();
// break;
// }
//
// virtual void abort_impl(void);
//
// A call to this method should bring the object to a state
// where finish_impl() can be called.
//
// virtual void finish_impl(void);
//
// Should cleanup whatever init_impl() did, or any of the
// states of the object where multiplex_impl() calls finish().
// Call kill() from here to make that the default behavior
// (state machine is deleted unless the callback calls run()).
//
// virtual char const* state_str_impl(state_type run_state);
//
// Should return a stringified value of run_state.
//
class AIStateMachine {
//! The type of mState
enum base_state_type { enum base_state_type {
bs_initialize, bs_reset, // Idle state before run() is called. Reference count is zero (except for a possible external LLPointer).
bs_run, bs_initialize, // State after run() and before/during initialize_impl().
bs_multiplex, // State after initialize_impl() before finish() or abort().
bs_abort, bs_abort,
bs_finish, bs_finish,
bs_callback, bs_callback,
bs_killed bs_killed
}; };
//! The type of mActive
enum active_type {
as_idle, // State machine is on neither list.
as_queued, // State machine is on continued_statemachines list.
as_active // State machine is on active_statemachines list.
};
//! Type of continued_statemachines.
typedef std::vector<AIStateMachine*> continued_statemachines_type;
//! Type of sContinuedStateMachinesAndMainloopEnabled.
struct csme_type
{
continued_statemachines_type continued_statemachines;
bool mainloop_enabled;
};
public: public:
typedef U32 state_type; //!< The type of mRunState
//! Integral value equal to the state with the lowest value.
static state_type const min_state = bs_initialize;
//! Integral value one more than the state with the highest value.
static state_type const max_state = bs_killed + 1; static state_type const max_state = bs_killed + 1;
protected:
struct multiplex_state_type {
base_state_type base_state;
AIEngine* current_engine; // Current engine.
multiplex_state_type(void) : base_state(bs_reset), current_engine(NULL) { }
};
struct sub_state_type {
state_type run_state;
state_type advance_state;
bool reset;
bool need_run;
bool idle;
bool skip_idle;
bool aborted;
bool finished;
};
private: private:
base_state_type mState; //!< State of the base class. // Base state.
bool mIdle; //!< True if this state machine is not running. AIThreadSafeSimpleDC<multiplex_state_type> mState;
bool mAborted; //!< True after calling abort() and before calling run(). typedef AIAccessConst<multiplex_state_type> multiplex_state_type_crat;
active_type mActive; //!< Whether statemachine is idle, queued to be added to the active list, or already on the active list. typedef AIAccess<multiplex_state_type> multiplex_state_type_rat;
S64 mSleep; //!< Non-zero while the state machine is sleeping. typedef AIAccess<multiplex_state_type> multiplex_state_type_wat;
LLMutex mIdleActive; //!< Used for atomic operations on the pair mIdle / mActive. // Sub state.
#ifdef SHOW_ASSERT AIThreadSafeSimpleDC<sub_state_type> mSubState;
AIThreadID mContThread; //!< Thread that last called locked_cont(). typedef AIAccessConst<sub_state_type> sub_state_type_crat;
bool mCalledThreadUnsafeIdle; //!< Set to true when idle() is called. typedef AIAccess<sub_state_type> sub_state_type_rat;
#endif typedef AIAccess<sub_state_type> sub_state_type_wat;
// Mutex protecting everything below and making sure only one thread runs the state machine at a time.
LLMutex mMultiplexMutex;
S64 mSleep; //!< Non-zero while the state machine is sleeping.
// Callback facilities. // Callback facilities.
// From within an other state machine: // From within an other state machine:
AIStateMachine* mParent; //!< The parent object that started this state machine, or NULL if there isn't any. LLPointer<AIStateMachine> mParent; // The parent object that started this state machine, or NULL if there isn't any.
state_type mNewParentState; //!< The state at which the parent should continue upon a successful finish. state_type mNewParentState; // The state at which the parent should continue upon a successful finish.
bool mAbortParent; //!< If true, abort parent on abort(). Otherwise continue as normal. bool mAbortParent; // If true, abort parent on abort(). Otherwise continue as normal.
bool mOnAbortSignalParent; //!< If true and mAbortParent is false, change state of parent even on abort. bool mOnAbortSignalParent; // If true and mAbortParent is false, change state of parent even on abort.
// From outside a state machine: // From outside a state machine:
struct callback_type { struct callback_type {
typedef boost::signals2::signal<void (bool)> signal_type; typedef boost::signals2::signal<void (bool)> signal_type;
callback_type(signal_type::slot_type const& slot) { connection = signal.connect(slot); } callback_type(signal_type::slot_type const& slot) { connection = signal.connect(slot); }
~callback_type() { connection.disconnect(); } ~callback_type() { connection.disconnect(); }
void callback(bool success) const { signal(success); } void callback(bool success) const { signal(success); }
private: private:
boost::signals2::connection connection; boost::signals2::connection connection;
signal_type signal; signal_type signal;
}; };
callback_type* mCallback; //!< Pointer to signal/connection, or NULL when not connected. callback_type* mCallback; // Pointer to signal/connection, or NULL when not connected.
static U64 sMaxCount; //!< Number of cpu clocks below which we start a new state machine within the same frame. // Engine stuff.
static AIThreadSafeDC<csme_type> sContinuedStateMachinesAndMainloopEnabled; //!< Read/write locked variable pair. AIEngine* mDefaultEngine; // Default engine.
AIEngine* mYieldEngine; // Requested engine.
protected:
LLMutex mSetStateLock; //!< For critical areas in set_state() and locked_cont().
//! State of the derived class. Only valid if mState == bs_run. Call set_state to change.
volatile state_type mRunState;
public:
//! Create a non-running state machine.
AIStateMachine(void) : mState(bs_initialize), mIdle(true), mAborted(true), mActive(as_idle), mSleep(0), mParent(NULL), mCallback(NULL)
#ifdef SHOW_ASSERT #ifdef SHOW_ASSERT
, mContThread(AIThreadID::none), mCalledThreadUnsafeIdle(false) // Debug stuff.
AIThreadID mThreadId; // The thread currently running multiplex().
base_state_type mDebugLastState; // The previous state that multiplex() had a normal run with.
bool mDebugShouldRun; // Set if we found evidence that we should indeed call multiplex_impl().
bool mDebugAborted; // True when abort() was called.
bool mDebugContPending; // True while cont() was called by not handled yet.
bool mDebugSetStatePending; // True while set_state() was called by not handled yet.
bool mDebugAdvanceStatePending; // True while advance_state() was called by not handled yet.
#endif #endif
{ }
public:
AIStateMachine(void) : mCallback(NULL), mDefaultEngine(NULL), mYieldEngine(NULL)
#ifdef SHOW_ASSERT
, mDebugLastState(bs_killed), mDebugShouldRun(false), mDebugAborted(false), mDebugContPending(false),
mDebugSetStatePending(false), mDebugAdvanceStatePending(false)
#endif
{ }
protected: protected:
//! The user should call 'kill()', not delete a AIStateMachine (derived) directly. // The user should call finish() (or abort(), or kill() from the call back when finish_impl() calls run()), not delete a class derived from AIStateMachine directly.
virtual ~AIStateMachine() { llassert((mState == bs_killed && mActive == as_idle) || mState == bs_initialize); } virtual ~AIStateMachine() { llassert(multiplex_state_type_rat(mState)->base_state == bs_killed); }
public: public:
//! Halt the state machine until cont() is called (not thread-safe). // These functions may be called directly after creation, or from within finish_impl(), or from the call back function.
void idle(void); void run(LLPointer<AIStateMachine> parent, state_type new_parent_state, bool abort_parent = true, bool on_abort_signal_parent = true, AIEngine* default_engine = &gMainThreadEngine);
void run(callback_type::signal_type::slot_type const& slot, AIEngine* default_engine = &gMainThreadEngine);
void run(void) { run(NULL, 0, false, true, mDefaultEngine); }
//! Halt the state machine until cont() is called, provided it is still in 'current_run_state'. // This function may only be called from the call back function (and cancels a call to run() from finish_impl()).
void idle(state_type current_run_state);
//! Temporarily halt the state machine.
void yield_frame(unsigned int frames) { mSleep = -(S64)frames; }
//! Temporarily halt the state machine.
void yield_ms(unsigned int ms) { mSleep = get_clock_count() + calc_clock_frequency() * ms / 1000; }
//! Continue running after calling idle.
void cont(void)
{
mSetStateLock.lock();
// Ignore calls to cont() if the statemachine isn't idle. See comments in set_state().
// Calling cont() twice or after calling set_state(), without first calling idle(), is an error.
if (mState != bs_run || !mIdle) { llassert(mState != bs_run || !mContThread.equals_current_thread()); mSetStateLock.unlock(); return; }
locked_cont();
}
private:
void locked_cont(void);
public:
//---------------------------------------
// Changing the state.
//! Change state to <code>bs_run</code>. May only be called after creation or after returning from finish().
// If <code>parent</code> is non-NULL, change the parent state machine's state to <code>new_parent_state</code>
// upon finish, or in the case of an abort and when <code>abort_parent</code> is true, call parent->abort() instead.
void run(AIStateMachine* parent, state_type new_parent_state, bool abort_parent = true, bool on_abort_signal_parent = true);
//! Change state to 'bs_run'. May only be called after creation or after returning from finish().
// Does not cause a callback.
void run(void) { run(NULL, 0, false); }
//! The same as above, but pass the result of a boost::bind with _1.
//
// Here _1, if present, will be replaced with a bool indicating success.
//
// For example:
//
// <code>
// struct Foo { void callback(AIStateMachineDerived* ptr, bool success); };
// ...
// AIStateMachineDerived* magic = new AIStateMachineDerived; // Deleted by callback
// // Call foo_ptr->callback(magic, _1) on finish.
// state_machine->run(boost::bind(&Foo::callback, foo_ptr, magic, _1));
// </code>
//
// or
//
// <code>
// struct Foo { void callback(bool success, AIStateMachineDerived const& magic); };
// ...
// AIStateMachineDerived magic;
// // Call foo_ptr->callback(_1, magic) on finish.
// magic.run(boost::bind(&Foo::callback, foo_ptr, _1, magic));
// </code>
//
// or
//
// <code>
// static void callback(void* userdata);
// ...
// AIStateMachineDerived magic;
// // Call callback(userdata) on finish.
// magic.run(boost::bind(&callback, userdata));
// </code>
void run(callback_type::signal_type::slot_type const& slot);
//! Change state to 'bs_abort'. May only be called while in the bs_run state.
void abort(void);
//! Change state to 'bs_finish'. May only be called while in the bs_run state.
void finish(void);
//! Refine state while in the bs_run state. May only be called while in the bs_run state.
void set_state(state_type run_state);
//! Change state to 'bs_killed'. May only be called while in the bs_finish state.
void kill(void); void kill(void);
//--------------------------------------- protected:
// Other. // This function can be called from initialize_impl() and multiplex_impl() (both called from within multiplex()).
void set_state(state_type new_state); // Run this state the NEXT loop.
//! Called whenever the StateMachineMaxTime setting is changed. // These functions can only be called from within multiplex_impl().
static void setMaxCount(F32 StateMachineMaxTime); void idle(void); // Go idle unless cont() or advance_state() were called since the start of the current loop, or until they are called.
void finish(void); // Mark that the state machine finished and schedule the call back.
//--------------------------------------- void yield(void); // Yield to give CPU to other state machines, but do not go idle.
// Accessors. void yield(AIEngine* engine); // Yield to give CPU to other state machines, but do not go idle. Continue running from engine 'engine'.
void yield_frame(unsigned int frames); // Run from the main-thread engine after at least 'frames' frames have passed.
//! Return true if state machine was aborted (can be used in finish_impl). void yield_ms(unsigned int ms); // Run from the main-thread engine after roughly 'ms' miliseconds have passed.
bool aborted(void) const { return mAborted; }
//! Return true if the derived class is running (also when we are idle).
bool running(void) const { return mState == bs_run; }
//! Return true if it's safe to call abort.
bool abortable(void) const { return mState == bs_run || mState == bs_initialize; }
//! Return true if the derived class is running but idle.
bool waiting(void) const { return mState == bs_run && mIdle; }
// Use some safebool idiom (http://www.artima.com/cppsource/safebool.html) rather than operator bool.
typedef volatile state_type AIStateMachine::* const bool_type;
//! Return true if state machine successfully finished.
operator bool_type() const { return ((mState == bs_initialize || mState == bs_callback) && !mAborted) ? &AIStateMachine::mRunState : 0; }
//! Return a stringified state, for debugging purposes.
char const* state_str(state_type state);
private:
static void add_continued_statemachines(AIReadAccess<csme_type>& csme_r);
static void dowork(void);
void multiplex(U64 current_time);
public: public:
//! Call this once per frame to give the statemachines CPU cycles. // This function can be called from multiplex_imp(), but also by a child state machine and
static void mainloop(void) // therefore by any thread. The child state machine should use an LLPointer<AIStateMachine>
// to access this state machine.
void abort(void); // Abort the state machine (unsuccessful finish).
// These are the only two functions that can be called by any thread at any moment.
// Those threads should use an LLPointer<AIStateMachine> to access this state machine.
void cont(void); // Guarantee at least one full run of multiplex() after this function is called. Cancels the last call to idle().
void advance_state(state_type new_state); // Guarantee at least one full run of multiplex() after this function is called
// iff new_state is larger than the last state that was processed.
public:
// Accessors.
// Return true if the derived class is running (also when we are idle).
bool running(void) const { return multiplex_state_type_crat(mState)->base_state == bs_multiplex; }
// Return true if the derived class is running and idle.
bool waiting(void) const
{ {
{ multiplex_state_type_crat state_r(mState);
AIReadAccess<csme_type> csme_r(sContinuedStateMachinesAndMainloopEnabled, true); return state_r->base_state == bs_multiplex && sub_state_type_crat(mSubState)->idle;
if (!csme_r->mainloop_enabled) }
return; // Return true if the derived class is running and idle or already being aborted.
if (!csme_r->continued_statemachines.empty()) bool waiting_or_aborting(void) const
add_continued_statemachines(csme_r); {
} multiplex_state_type_crat state_r(mState);
dowork(); return state_r->base_state == bs_abort || ( state_r->base_state == bs_multiplex && sub_state_type_crat(mSubState)->idle);
}
// Return true if are added to the engine.
bool active(AIEngine const* engine) const { return multiplex_state_type_crat(mState)->current_engine == engine; }
bool aborted(void) const { return sub_state_type_crat(mSubState)->aborted; }
// Use some safebool idiom (http://www.artima.com/cppsource/safebool.html) rather than operator bool.
typedef state_type AIStateMachine::* const bool_type;
// Return true if state machine successfully finished.
operator bool_type() const
{
sub_state_type_crat sub_state_r(mSubState);
return (sub_state_r->finished && !sub_state_r->aborted) ? &AIStateMachine::mNewParentState : 0;
} }
//! Abort all running state machines and then run mainloop until all state machines are idle (called when application is exiting). // Return stringified state, for debugging purposes.
static void flush(void); char const* state_str(base_state_type state);
#ifdef CWDEBUG
char const* event_str(event_type event);
#endif
protected: protected:
//---------------------------------------
// Derived class implementations.
// Handle initializing the object.
virtual void initialize_impl(void) = 0; virtual void initialize_impl(void) = 0;
virtual void multiplex_impl(state_type run_state) = 0;
// Handle mRunState. virtual void abort_impl(void) { }
virtual void multiplex_impl(void) = 0; virtual void finish_impl(void) { }
// Handle aborting from current bs_run state.
virtual void abort_impl(void) = 0;
// Handle cleaning up from initialization (or post abort) state.
virtual void finish_impl(void) = 0;
// Implemenation of state_str for run states.
virtual char const* state_str_impl(state_type run_state) const = 0; virtual char const* state_str_impl(state_type run_state) const = 0;
private:
void reset(void); // Called from run() to (re)initialize a (re)start.
void multiplex(event_type event); // Called from AIEngine to step through the states (and from reset() to kick start the state machine).
state_type begin_loop(base_state_type base_state); // Called from multiplex() at the start of a loop.
void callback(void); // Called when the state machine finished.
bool sleep(U64 current_time) // Count frames if necessary and return true when the state machine is still sleeping.
{
if (mSleep == 0)
return false;
else if (mSleep < 0)
++mSleep;
else if ((U64)mSleep >= current_time)
mSleep = 0;
return mSleep != 0;
}
friend class AIEngine; // Calls multiplex().
}; };
// This case be used in state_str_impl. // This can be used in state_str_impl.
#define AI_CASE_RETURN(x) do { case x: return #x; } while(0) #define AI_CASE_RETURN(x) do { case x: return #x; } while(0)
#endif #endif

View File

@@ -88,15 +88,15 @@ void AIStateMachineThreadBase::initialize_impl(void)
set_state(start_thread); set_state(start_thread);
} }
void AIStateMachineThreadBase::multiplex_impl(void) void AIStateMachineThreadBase::multiplex_impl(state_type run_state)
{ {
switch(mRunState) switch(run_state)
{ {
case start_thread: case start_thread:
mThread = Thread::allocate(mImpl); mThread = Thread::allocate(mImpl);
// Set next state. // Set next state.
set_state(wait_stopped); set_state(wait_stopped);
idle(wait_stopped); // Wait till the thread returns. idle(); // Wait till the thread returns.
mThread->start(); mThread->start();
break; break;
case wait_stopped: case wait_stopped:
@@ -179,12 +179,8 @@ bool AIThreadImpl::thread_done(bool result)
{ {
// If state_machine_thread is non-NULL then AIThreadImpl::abort_impl wasn't called, // If state_machine_thread is non-NULL then AIThreadImpl::abort_impl wasn't called,
// which means the state machine still exists. In fact, it should be in the waiting() state. // which means the state machine still exists. In fact, it should be in the waiting() state.
// It can also happen that the state machine is being aborted right now (but it will still exist). // It can also happen that the state machine is being aborted right now.
// (Note that waiting() and running() aren't strictly thread-safe (we should really lock llassert(state_machine_thread->waiting_or_aborting());
// mSetStateLock here) but by first calling waiting() and then running(), and assuming that
// changing an int from the value 1 to the value 2 is atomic, this will work since the
// only possible transition is from waiting to not running).
llassert(state_machine_thread->waiting() || !state_machine_thread->running());
state_machine_thread->schedule_abort(!result); state_machine_thread->schedule_abort(!result);
// Note that if the state machine is not running (being aborted, ie - hanging in abort_impl // Note that if the state machine is not running (being aborted, ie - hanging in abort_impl
// waiting for the lock on mStateMachineThread) then this is simply ignored. // waiting for the lock on mStateMachineThread) then this is simply ignored.

View File

@@ -89,19 +89,8 @@ class HelloWorld : public AIStateMachine {
// Handle initializing the object. // Handle initializing the object.
/*virtual*/ void initialize_impl(void); /*virtual*/ void initialize_impl(void);
// Handle mRunState. // Handle run_state.
/*virtual*/ void multiplex_impl(void); /*virtual*/ void multiplex_impl(state_type run_state);
// Handle aborting from current bs_run state.
/*virtual*/ void abort_impl(void) { }
// Handle cleaning up from initialization (or post abort) state.
/*virtual*/ void finish_impl(void)
{
// Kill object by default.
// This can be overridden by calling run() from the callback function.
kill();
}
// Implemenation of state_str for run states. // Implemenation of state_str for run states.
/*virtual*/ char const* state_str_impl(state_type run_state) const /*virtual*/ char const* state_str_impl(state_type run_state) const
@@ -123,9 +112,9 @@ void HelloWorld::initialize_impl(void)
set_state(HelloWorld_start); set_state(HelloWorld_start);
} }
void HelloWorld::multiplex_impl(void) void HelloWorld::multiplex_impl(state_type run_state)
{ {
switch (mRunState) switch (run_state)
{ {
case HelloWorld_start: case HelloWorld_start:
{ {
@@ -177,28 +166,30 @@ class AIStateMachineThreadBase : public AIStateMachine {
// The actual thread (derived from LLThread). // The actual thread (derived from LLThread).
class Thread; class Thread;
protected:
typedef AIStateMachine direct_base_type;
// The states of this state machine. // The states of this state machine.
enum thread_state_type { enum thread_state_type {
start_thread = AIStateMachine::max_state, // Start the thread (if necessary create it first). start_thread = direct_base_type::max_state, // Start the thread (if necessary create it first).
wait_stopped // Wait till the thread is stopped. wait_stopped // Wait till the thread is stopped.
}; };
public:
static state_type const max_state = wait_stopped + 1;
protected: protected:
AIStateMachineThreadBase(AIThreadImpl* impl) : mImpl(impl) { } AIStateMachineThreadBase(AIThreadImpl* impl) : mImpl(impl) { ref(); /* Never call delete */ }
private: private:
// Handle initializing the object. // Handle initializing the object.
/*virtual*/ void initialize_impl(void); /*virtual*/ void initialize_impl(void);
// Handle mRunState. // Handle mRunState.
/*virtual*/ void multiplex_impl(void); /*virtual*/ void multiplex_impl(state_type run_state);
// Handle aborting from current bs_run state. // Handle aborting from current bs_run state.
/*virtual*/ void abort_impl(void); /*virtual*/ void abort_impl(void);
// Handle cleaning up from initialization (or post abort) state.
/*virtual*/ void finish_impl(void) { }
// Implemenation of state_str for run states. // Implemenation of state_str for run states.
/*virtual*/ char const* state_str_impl(state_type run_state) const; /*virtual*/ char const* state_str_impl(state_type run_state) const;

View File

@@ -31,11 +31,6 @@
#include "linden_common.h" #include "linden_common.h"
#include "aitimer.h" #include "aitimer.h"
enum timer_state_type {
AITimer_start = AIStateMachine::max_state,
AITimer_expired
};
char const* AITimer::state_str_impl(state_type run_state) const char const* AITimer::state_str_impl(state_type run_state) const
{ {
switch(run_state) switch(run_state)
@@ -43,6 +38,7 @@ char const* AITimer::state_str_impl(state_type run_state) const
AI_CASE_RETURN(AITimer_start); AI_CASE_RETURN(AITimer_start);
AI_CASE_RETURN(AITimer_expired); AI_CASE_RETURN(AITimer_expired);
} }
llassert(false);
return "UNKNOWN STATE"; return "UNKNOWN STATE";
} }
@@ -54,12 +50,12 @@ void AITimer::initialize_impl(void)
void AITimer::expired(void) void AITimer::expired(void)
{ {
set_state(AITimer_expired); advance_state(AITimer_expired);
} }
void AITimer::multiplex_impl(void) void AITimer::multiplex_impl(state_type run_state)
{ {
switch (mRunState) switch (run_state)
{ {
case AITimer_start: case AITimer_start:
{ {
@@ -79,18 +75,3 @@ void AITimer::abort_impl(void)
{ {
mFrameTimer.cancel(); mFrameTimer.cancel();
} }
void AITimer::finish_impl(void)
{
// Kill object by default.
// This can be overridden by calling run() from the callback function.
kill();
}
void AIPersistentTimer::finish_impl(void)
{
// Don't kill object by default.
if (aborted())
kill();
// Callback function should always call kill() or run().
}

View File

@@ -59,6 +59,18 @@
// just reuse the old ones (call the same callback). // just reuse the old ones (call the same callback).
// //
class AITimer : public AIStateMachine { class AITimer : public AIStateMachine {
protected:
// The base class of this state machine.
typedef AIStateMachine direct_base_type;
// The different states of the state machine.
enum timer_state_type {
AITimer_start = direct_base_type::max_state,
AITimer_expired
};
public:
static state_type const max_state = AITimer_expired + 1;
private: private:
AIFrameTimer mFrameTimer; //!< The actual timer that this object wraps. AIFrameTimer mFrameTimer; //!< The actual timer that this object wraps.
F64 mInterval; //!< Input variable: interval after which the event will be generated, in seconds. F64 mInterval; //!< Input variable: interval after which the event will be generated, in seconds.
@@ -90,14 +102,11 @@ class AITimer : public AIStateMachine {
/*virtual*/ void initialize_impl(void); /*virtual*/ void initialize_impl(void);
// Handle mRunState. // Handle mRunState.
/*virtual*/ void multiplex_impl(void); /*virtual*/ void multiplex_impl(state_type run_state);
// Handle aborting from current bs_run state. // Handle aborting from current bs_run state.
/*virtual*/ void abort_impl(void); /*virtual*/ void abort_impl(void);
// Handle cleaning up from initialization (or post abort) state.
/*virtual*/ void finish_impl(void);
// Implemenation of state_str for run states. // Implemenation of state_str for run states.
/*virtual*/ char const* state_str_impl(state_type run_state) const; /*virtual*/ char const* state_str_impl(state_type run_state) const;
@@ -106,12 +115,4 @@ class AITimer : public AIStateMachine {
void expired(void); void expired(void);
}; };
// Same as above but does not delete itself automatically by default after use.
// Call kill() on it yourself (from the callback function) when you're done with it!
class AIPersistentTimer : public AITimer {
protected:
// Handle cleaning up from initialization (or post abort) state.
/*virtual*/ void finish_impl(void);
};
#endif #endif

View File

@@ -51,6 +51,11 @@ void AIThreadID::set_current_thread_id(void)
} }
#ifndef LL_DARWIN #ifndef LL_DARWIN
void AIThreadID::clear(void)
{
mID = undefinedID;
}
void AIThreadID::reset(void) void AIThreadID::reset(void)
{ {
mID = lCurrentThread; mID = lCurrentThread;

View File

@@ -63,6 +63,7 @@ public:
static void set_main_thread_id(void); // Called once to set sMainThreadID. static void set_main_thread_id(void); // Called once to set sMainThreadID.
static void set_current_thread_id(void); // Called once for every thread to set lCurrentThread. static void set_current_thread_id(void); // Called once for every thread to set lCurrentThread.
#ifndef LL_DARWIN #ifndef LL_DARWIN
LL_COMMON_API void clear(void);
LL_COMMON_API void reset(void); LL_COMMON_API void reset(void);
LL_COMMON_API bool equals_current_thread(void) const; LL_COMMON_API bool equals_current_thread(void) const;
LL_COMMON_API static bool in_main_thread(void); LL_COMMON_API static bool in_main_thread(void);
@@ -74,6 +75,7 @@ public:
static apr_os_thread_t getCurrentThread_inline(void) { return lCurrentThread; } static apr_os_thread_t getCurrentThread_inline(void) { return lCurrentThread; }
#else #else
// Both variants are inline on OS X. // Both variants are inline on OS X.
void clear(void) { mID = undefinedID; }
void reset(void) { mID = apr_os_thread_current(); } void reset(void) { mID = apr_os_thread_current(); }
void reset_inline(void) { mID = apr_os_thread_current(); } void reset_inline(void) { mID = apr_os_thread_current(); }
bool equals_current_thread(void) const { return apr_os_thread_equal(mID, apr_os_thread_current()); } bool equals_current_thread(void) const { return apr_os_thread_equal(mID, apr_os_thread_current()); }

View File

@@ -479,9 +479,6 @@ public:
// Only for use by AITHREADSAFESIMPLE, see below. // Only for use by AITHREADSAFESIMPLE, see below.
AIThreadSafeSimple(T* object) { llassert(object == AIThreadSafeBits<T>::ptr()); } AIThreadSafeSimple(T* object) { llassert(object == AIThreadSafeBits<T>::ptr()); }
// If MUTEX is a LLCondition then this can be used to wake up the waiting thread.
void signal() { mMutex.signal(); }
#if LL_DEBUG #if LL_DEBUG
// Can only be locked when there still exists an AIAccess object that // Can only be locked when there still exists an AIAccess object that
// references this object and will access it upon destruction. // references this object and will access it upon destruction.
@@ -622,6 +619,8 @@ struct AIAccessConst
// If MUTEX is an LLCondition, then this can be used to wait for a signal. // If MUTEX is an LLCondition, then this can be used to wait for a signal.
void wait() { this->mWrapper.mMutex.wait(); } void wait() { this->mWrapper.mMutex.wait(); }
// If MUTEX is a LLCondition then this can be used to wake up the waiting thread.
void signal() { this->mWrapper.mMutex.signal(); }
protected: protected:
AIThreadSafeSimple<T, MUTEX>& mWrapper; //!< Reference to the object that we provide access to. AIThreadSafeSimple<T, MUTEX>& mWrapper; //!< Reference to the object that we provide access to.

View File

@@ -418,7 +418,8 @@ void cleanupCurl(void)
stopCurlThread(); stopCurlThread();
if (CurlMultiHandle::getTotalMultiHandles() != 0) if (CurlMultiHandle::getTotalMultiHandles() != 0)
llwarns << "Not all CurlMultiHandle objects were destroyed!" << llendl; llwarns << "Not all CurlMultiHandle objects were destroyed!" << llendl;
AIStateMachine::flush(); gMainThreadEngine.flush(); // Not really related to curl, but why not.
gStateMachineThreadEngine.flush();
clearCommandQueue(); clearCommandQueue();
Stats::print(); Stats::print();
ssl_cleanup(); ssl_cleanup();

View File

@@ -77,14 +77,14 @@ void AICurlEasyRequestStateMachine::initialize_impl(void)
// CURL-THREAD // CURL-THREAD
void AICurlEasyRequestStateMachine::added_to_multi_handle(AICurlEasyRequest_wat&) void AICurlEasyRequestStateMachine::added_to_multi_handle(AICurlEasyRequest_wat&)
{ {
set_state(AICurlEasyRequestStateMachine_added); advance_state(AICurlEasyRequestStateMachine_added);
} }
// CURL-THREAD // CURL-THREAD
void AICurlEasyRequestStateMachine::finished(AICurlEasyRequest_wat&) void AICurlEasyRequestStateMachine::finished(AICurlEasyRequest_wat&)
{ {
mFinished = true; mFinished = true;
set_state(AICurlEasyRequestStateMachine_finished); advance_state(AICurlEasyRequestStateMachine_finished);
} }
// CURL-THREAD // CURL-THREAD
@@ -93,7 +93,7 @@ void AICurlEasyRequestStateMachine::removed_from_multi_handle(AICurlEasyRequest_
llassert(mFinished || mTimedOut); // If we neither finished nor timed out, then why is this being removed? llassert(mFinished || mTimedOut); // If we neither finished nor timed out, then why is this being removed?
// Note that allowing this would cause an assertion later on for removing // Note that allowing this would cause an assertion later on for removing
// a BufferedCurlEasyRequest with a still active Responder. // a BufferedCurlEasyRequest with a still active Responder.
set_state(mFinished ? AICurlEasyRequestStateMachine_removed_after_finished : AICurlEasyRequestStateMachine_removed); advance_state(mFinished ? AICurlEasyRequestStateMachine_removed_after_finished : AICurlEasyRequestStateMachine_removed);
} }
// CURL-THREAD // CURL-THREAD
@@ -102,7 +102,7 @@ void AICurlEasyRequestStateMachine::bad_file_descriptor(AICurlEasyRequest_wat&)
if (!mFinished) if (!mFinished)
{ {
mFinished = true; mFinished = true;
set_state(AICurlEasyRequestStateMachine_bad_file_descriptor); advance_state(AICurlEasyRequestStateMachine_bad_file_descriptor);
} }
} }
@@ -114,17 +114,14 @@ void AICurlEasyRequestStateMachine::queued_for_removal(AICurlEasyRequest_wat&)
} }
#endif #endif
void AICurlEasyRequestStateMachine::multiplex_impl(void) void AICurlEasyRequestStateMachine::multiplex_impl(state_type run_state)
{ {
mSetStateLock.lock(); switch (run_state)
state_type current_state = mRunState;
mSetStateLock.unlock();
switch (current_state)
{ {
case AICurlEasyRequestStateMachine_addRequest: case AICurlEasyRequestStateMachine_addRequest:
{ {
set_state(AICurlEasyRequestStateMachine_waitAdded); set_state(AICurlEasyRequestStateMachine_waitAdded);
idle(AICurlEasyRequestStateMachine_waitAdded); // Wait till AICurlEasyRequestStateMachine::added_to_multi_handle() is called. idle(); // Wait till AICurlEasyRequestStateMachine::added_to_multi_handle() is called.
// Only AFTER going idle, add request to curl thread; this is needed because calls to set_state() are // Only AFTER going idle, add request to curl thread; this is needed because calls to set_state() are
// ignored when the statemachine is not idle, and theoretically the callbacks could be called // ignored when the statemachine is not idle, and theoretically the callbacks could be called
// immediately after this call. // immediately after this call.
@@ -145,7 +142,7 @@ void AICurlEasyRequestStateMachine::multiplex_impl(void)
// Set an inactivity timer. // Set an inactivity timer.
// This shouldn't really be necessary, except in the case of a bug // This shouldn't really be necessary, except in the case of a bug
// in libcurl; but lets be sure and set a timer for inactivity. // in libcurl; but lets be sure and set a timer for inactivity.
mTimer = new AIPersistentTimer; // Do not delete timer upon expiration. mTimer = new AITimer;
mTimer->setInterval(mTotalDelayTimeout); mTimer->setInterval(mTotalDelayTimeout);
mTimer->run(this, AICurlEasyRequestStateMachine_timedOut, false, false); mTimer->run(this, AICurlEasyRequestStateMachine_timedOut, false, false);
} }
@@ -155,7 +152,7 @@ void AICurlEasyRequestStateMachine::multiplex_impl(void)
{ {
// The request was added to the multi handle. This is a no-op, which is good cause // The request was added to the multi handle. This is a no-op, which is good cause
// this state might be skipped anyway ;). // this state might be skipped anyway ;).
idle(current_state); // Wait for the next event. idle(); // Wait for the next event.
// The state at this point is one of // The state at this point is one of
// 1) AICurlEasyRequestStateMachine_added (idle) // 1) AICurlEasyRequestStateMachine_added (idle)
@@ -176,7 +173,7 @@ void AICurlEasyRequestStateMachine::multiplex_impl(void)
llassert(mAdded); llassert(mAdded);
mAdded = false; mAdded = false;
mCurlEasyRequest.removeRequest(); mCurlEasyRequest.removeRequest();
idle(current_state); // Wait till AICurlEasyRequestStateMachine::removed_from_multi_handle() is called. idle(); // Wait till AICurlEasyRequestStateMachine::removed_from_multi_handle() is called.
break; break;
} }
case AICurlEasyRequestStateMachine_finished: case AICurlEasyRequestStateMachine_finished:
@@ -199,9 +196,9 @@ void AICurlEasyRequestStateMachine::multiplex_impl(void)
easy_request_w->processOutput(); easy_request_w->processOutput();
} }
if (current_state == AICurlEasyRequestStateMachine_finished) if (run_state == AICurlEasyRequestStateMachine_finished)
{ {
idle(current_state); // Wait till AICurlEasyRequestStateMachine::removed_from_multi_handle() is called. idle(); // Wait till AICurlEasyRequestStateMachine::removed_from_multi_handle() is called.
break; break;
} }
@@ -261,17 +258,14 @@ void AICurlEasyRequestStateMachine::finish_impl(void)
} }
if (mTimer) if (mTimer)
{ {
// Note that even if the timer expired, it wasn't deleted because we used AIPersistentTimer; so mTimer is still valid.
// Stop the timer, if it's still running. // Stop the timer, if it's still running.
if (!mHandled) if (!mHandled)
mTimer->abort(); mTimer->abort();
} }
// Auto clean up ourselves.
kill();
} }
AICurlEasyRequestStateMachine::AICurlEasyRequestStateMachine(void) : AICurlEasyRequestStateMachine::AICurlEasyRequestStateMachine(void) :
mTimer(NULL), mTotalDelayTimeout(AIHTTPTimeoutPolicy::getDebugSettingsCurlTimeout().getTotalDelay()) mTotalDelayTimeout(AIHTTPTimeoutPolicy::getDebugSettingsCurlTimeout().getTotalDelay())
{ {
Dout(dc::statemachine, "Calling AICurlEasyRequestStateMachine(void) [" << (void*)this << "] [" << (void*)mCurlEasyRequest.get() << "]"); Dout(dc::statemachine, "Calling AICurlEasyRequestStateMachine(void) [" << (void*)this << "] [" << (void*)mCurlEasyRequest.get() << "]");
AICurlInterface::Stats::AICurlEasyRequestStateMachine_count++; AICurlInterface::Stats::AICurlEasyRequestStateMachine_count++;

View File

@@ -62,7 +62,7 @@ class AICurlEasyRequestStateMachine : public AIStateMachine, public AICurlEasyHa
bool mTimedOut; // Set if the expiration timer timed out. bool mTimedOut; // Set if the expiration timer timed out.
bool mFinished; // Set by the curl thread to signal it finished. bool mFinished; // Set by the curl thread to signal it finished.
bool mHandled; // Set when we processed the received data. bool mHandled; // Set when we processed the received data.
AITimer* mTimer; // Expiration timer. LLPointer<AITimer> mTimer; // Expiration timer.
F32 mTotalDelayTimeout; // The time out value for mTimer. F32 mTotalDelayTimeout; // The time out value for mTimer.
public: public:
@@ -99,7 +99,7 @@ class AICurlEasyRequestStateMachine : public AIStateMachine, public AICurlEasyHa
/*virtual*/ void initialize_impl(void); /*virtual*/ void initialize_impl(void);
// Handle mRunState. // Handle mRunState.
/*virtual*/ void multiplex_impl(void); /*virtual*/ void multiplex_impl(state_type run_state);
// Handle aborting from current bs_run state. // Handle aborting from current bs_run state.
/*virtual*/ void abort_impl(void); /*virtual*/ void abort_impl(void);

View File

@@ -487,7 +487,8 @@ void BlockingResponder::wait(void)
// We're the main thread, so we have to give AIStateMachine CPU cycles. // We're the main thread, so we have to give AIStateMachine CPU cycles.
while (!mFinished) while (!mFinished)
{ {
AIStateMachine::mainloop(); // AIFIXME: this can probably be removed once curl is detached from the main thread.
gMainThreadEngine.mainloop();
ms_sleep(10); ms_sleep(10);
} }
} }

View File

@@ -236,6 +236,9 @@ extern BOOL gRandomizeFramerate;
extern BOOL gPeriodicSlowFrame; extern BOOL gPeriodicSlowFrame;
extern BOOL gDebugGL; extern BOOL gDebugGL;
extern void startEngineThread(void);
extern void stopEngineThread(void);
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
// All from the last globals push... // All from the last globals push...
const F32 DEFAULT_AFK_TIMEOUT = 5.f * 60.f; // time with no input before user flagged as Away From Keyboard const F32 DEFAULT_AFK_TIMEOUT = 5.f * 60.f; // time with no input before user flagged as Away From Keyboard
@@ -654,7 +657,7 @@ bool LLAppViewer::init()
mAlloc.setProfilingEnabled(gSavedSettings.getBOOL("MemProfiling")); mAlloc.setProfilingEnabled(gSavedSettings.getBOOL("MemProfiling"));
AIStateMachine::setMaxCount(gSavedSettings.getU32("StateMachineMaxTime")); AIEngine::setMaxCount(gSavedSettings.getU32("StateMachineMaxTime"));
{ {
AIHTTPTimeoutPolicy policy_tmp( AIHTTPTimeoutPolicy policy_tmp(
@@ -1804,6 +1807,7 @@ bool LLAppViewer::cleanup()
llinfos << "Message system deleted." << llendflush; llinfos << "Message system deleted." << llendflush;
LLApp::stopErrorThread(); // The following call is not thread-safe. Have to stop all threads. LLApp::stopErrorThread(); // The following call is not thread-safe. Have to stop all threads.
stopEngineThread();
AICurlInterface::cleanupCurl(); AICurlInterface::cleanupCurl();
// Cleanup settings last in case other classes reference them. // Cleanup settings last in case other classes reference them.
@@ -1880,6 +1884,9 @@ bool LLAppViewer::initThreads()
LLWatchdog::getInstance()->init(watchdog_killer_callback); LLWatchdog::getInstance()->init(watchdog_killer_callback);
} }
// State machine thread.
startEngineThread();
AICurlInterface::startCurlThread(gSavedSettings.getU32("CurlMaxTotalConcurrentConnections"), AICurlInterface::startCurlThread(gSavedSettings.getU32("CurlMaxTotalConcurrentConnections"),
gSavedSettings.getU32("CurlConcurrentConnectionsPerHost"), gSavedSettings.getU32("CurlConcurrentConnectionsPerHost"),
gSavedSettings.getBOOL("NoVerifySSLCert")); gSavedSettings.getBOOL("NoVerifySSLCert"));
@@ -3823,7 +3830,7 @@ void LLAppViewer::idle()
{ {
LLFastTimer t(FTM_STATEMACHINE); LLFastTimer t(FTM_STATEMACHINE);
AIStateMachine::mainloop(); gMainThreadEngine.mainloop();
} }
// Must wait until both have avatar object and mute list, so poll // Must wait until both have avatar object and mute list, so poll

View File

@@ -1351,17 +1351,17 @@ void AIMeshUpload::initialize_impl()
set_state(AIMeshUpload_start); set_state(AIMeshUpload_start);
} }
void AIMeshUpload::multiplex_impl() void AIMeshUpload::multiplex_impl(state_type run_state)
{ {
switch (mRunState) switch (run_state)
{ {
case AIMeshUpload_start: case AIMeshUpload_start:
mMeshUpload.run(this, AIMeshUpload_threadFinished); mMeshUpload.run(this, AIMeshUpload_threadFinished);
idle(AIMeshUpload_start); // Wait till the thread finished. idle(); // Wait till the thread finished.
break; break;
case AIMeshUpload_threadFinished: case AIMeshUpload_threadFinished:
mMeshUpload->postRequest(mWholeModelUploadURL, this); mMeshUpload->postRequest(mWholeModelUploadURL, this);
idle(AIMeshUpload_threadFinished); // Wait till the responder finished. idle(); // Wait till the responder finished.
break; break;
case AIMeshUpload_responderFinished: case AIMeshUpload_responderFinished:
finish(); finish();
@@ -1402,14 +1402,6 @@ void LLMeshUploadThread::postRequest(std::string& whole_model_upload_url, AIMesh
} }
} }
void AIMeshUpload::abort_impl()
{
}
void AIMeshUpload::finish_impl()
{
}
void dump_llsd_to_file(const LLSD& content, std::string filename) void dump_llsd_to_file(const LLSD& content, std::string filename)
{ {
if (gSavedSettings.getBOOL("MeshUploadLogXML")) if (gSavedSettings.getBOOL("MeshUploadLogXML"))

View File

@@ -445,11 +445,9 @@ public:
protected: protected:
// Implement AIStateMachine. // Implement AIStateMachine.
/*virtual*/ const char* state_str_impl(state_type) const; /*virtual*/ const char* state_str_impl(state_type run_state) const;
/*virtual*/ void initialize_impl(); /*virtual*/ void initialize_impl();
/*virtual*/ void multiplex_impl(); /*virtual*/ void multiplex_impl(state_type run_state);
/*virtual*/ void abort_impl();
/*virtual*/ void finish_impl();
}; };
class LLMeshRepository class LLMeshRepository

View File

@@ -136,7 +136,7 @@ static bool handleTerrainScaleChanged(const LLSD& inputvalue)
bool handleStateMachineMaxTimeChanged(const LLSD& newvalue) bool handleStateMachineMaxTimeChanged(const LLSD& newvalue)
{ {
F32 StateMachineMaxTime = newvalue.asFloat(); F32 StateMachineMaxTime = newvalue.asFloat();
AIStateMachine::setMaxCount(StateMachineMaxTime); AIEngine::setMaxCount(StateMachineMaxTime);
return true; return true;
} }

View File

@@ -76,7 +76,6 @@ typedef AIAccess<AIRegisteredStateMachines> registered_statemachines_wat;
// static // static
void AIEvent::Register(AIEvents event, AIStateMachine* statemachine, bool one_shot) void AIEvent::Register(AIEvents event, AIStateMachine* statemachine, bool one_shot)
{ {
statemachine->idle();
registered_statemachines_wat registered_statemachines_w(registered_statemachines_list[event]); registered_statemachines_wat registered_statemachines_w(registered_statemachines_list[event]);
registered_statemachines_w->Register(statemachine, one_shot); registered_statemachines_w->Register(statemachine, one_shot);
} }

View File

@@ -49,19 +49,20 @@ class AIInventoryFetchDescendentsObserver : public LLInventoryFetchDescendentsOb
protected: protected:
/*virtual*/ void done() /*virtual*/ void done()
{ {
mStateMachine->set_state(AIFetchInventoryFolder_folderCompleted); mStateMachine->advance_state(AIFetchInventoryFolder_folderCompleted);
delete this; delete this;
} }
private: private:
AIStateMachine* mStateMachine; LLPointer<AIStateMachine> mStateMachine;
}; };
AIInventoryFetchDescendentsObserver::AIInventoryFetchDescendentsObserver(AIStateMachine* statemachine, LLUUID const& folder) : AIInventoryFetchDescendentsObserver::AIInventoryFetchDescendentsObserver(AIStateMachine* statemachine, LLUUID const& folder) :
mStateMachine(statemachine), mStateMachine(statemachine),
LLInventoryFetchDescendentsObserver(folder) LLInventoryFetchDescendentsObserver(folder)
{ {
mStateMachine->idle(); // Call idle() on the parent state machine before passing it.
llassert(mStateMachine->waiting());
startFetch(); startFetch();
if(isFinished()) if(isFinished())
{ {
@@ -97,14 +98,15 @@ void AIFetchInventoryFolder::initialize_impl(void)
set_state(AIFetchInventoryFolder_checkFolderExists); set_state(AIFetchInventoryFolder_checkFolderExists);
if (!gInventory.isInventoryUsable()) if (!gInventory.isInventoryUsable())
{ {
// This immediately calls this->idle(), and then when the event occurs cont(). idle();
// This calls this->cont() when the event occurs.
AIEvent::Register(AIEvent::LLInventoryModel_mIsAgentInvUsable_true, this); AIEvent::Register(AIEvent::LLInventoryModel_mIsAgentInvUsable_true, this);
} }
} }
void AIFetchInventoryFolder::multiplex_impl(void) void AIFetchInventoryFolder::multiplex_impl(state_type run_state)
{ {
switch (mRunState) switch (run_state)
{ {
case AIFetchInventoryFolder_checkFolderExists: case AIFetchInventoryFolder_checkFolderExists:
{ {
@@ -172,6 +174,7 @@ void AIFetchInventoryFolder::multiplex_impl(void)
} }
case AIFetchInventoryFolder_fetchDescendents: case AIFetchInventoryFolder_fetchDescendents:
{ {
idle(); // Wait till the state is set to AIFetchInventoryFolder_folderCompleted.
// This sets the state to AIFetchInventoryFolder_folderCompleted once the folder is complete. // This sets the state to AIFetchInventoryFolder_folderCompleted once the folder is complete.
new AIInventoryFetchDescendentsObserver(this, mFolderUUID); new AIInventoryFetchDescendentsObserver(this, mFolderUUID);
break; break;
@@ -193,10 +196,6 @@ void AIFetchInventoryFolder::multiplex_impl(void)
} }
} }
void AIFetchInventoryFolder::abort_impl(void)
{
}
void AIFetchInventoryFolder::finish_impl(void) void AIFetchInventoryFolder::finish_impl(void)
{ {
if (mNeedNotifyObservers) if (mNeedNotifyObservers)

View File

@@ -138,10 +138,7 @@ class AIFetchInventoryFolder : public AIStateMachine {
/*virtual*/ void initialize_impl(void); /*virtual*/ void initialize_impl(void);
// Handle mRunState. // Handle mRunState.
/*virtual*/ void multiplex_impl(void); /*virtual*/ void multiplex_impl(state_type run_state);
// Handle aborting from current bs_run state.
/*virtual*/ void abort_impl(void);
// Handle cleaning up from initialization (or post abort) state. // Handle cleaning up from initialization (or post abort) state.
/*virtual*/ void finish_impl(void); /*virtual*/ void finish_impl(void);

View File

@@ -66,7 +66,7 @@ char const* AIFilePicker::state_str_impl(state_type run_state) const
return "UNKNOWN STATE"; return "UNKNOWN STATE";
} }
AIFilePicker::AIFilePicker(void) : mPluginManager(NULL), mAutoKill(false), mCanceled(false) AIFilePicker::AIFilePicker(void) : mPluginManager(NULL), mCanceled(false)
{ {
} }
@@ -345,7 +345,7 @@ void AIFilePicker::initialize_impl(void)
set_state(AIFilePicker_initialize_plugin); set_state(AIFilePicker_initialize_plugin);
} }
void AIFilePicker::multiplex_impl(void) void AIFilePicker::multiplex_impl(state_type run_state)
{ {
mPluginManager->update(); // Give the plugin some CPU for it's messages. mPluginManager->update(); // Give the plugin some CPU for it's messages.
LLPluginClassBasic* plugin = mPluginManager->getPlugin(); LLPluginClassBasic* plugin = mPluginManager->getPlugin();
@@ -355,7 +355,7 @@ void AIFilePicker::multiplex_impl(void)
abort(); abort();
return; return;
} }
switch (mRunState) switch (run_state)
{ {
case AIFilePicker_initialize_plugin: case AIFilePicker_initialize_plugin:
{ {
@@ -430,10 +430,6 @@ void AIFilePicker::multiplex_impl(void)
} }
} }
void AIFilePicker::abort_impl(void)
{
}
void AIFilePicker::finish_impl(void) void AIFilePicker::finish_impl(void)
{ {
if (mPluginManager) if (mPluginManager)
@@ -442,12 +438,6 @@ void AIFilePicker::finish_impl(void)
mPluginManager = NULL; mPluginManager = NULL;
} }
mFilter.clear(); // Check that open is called before calling run (again). mFilter.clear(); // Check that open is called before calling run (again).
if (mAutoKill)
{
// The default behavior is to delete the plugin. This can be overridden in
// the callback by calling run() again.
kill();
}
} }
// This function is called when a new message is received from the plugin. // This function is called when a new message is received from the plugin.
@@ -467,7 +457,7 @@ void AIFilePicker::receivePluginMessage(const LLPluginMessage &message)
if (message_name == "canceled") if (message_name == "canceled")
{ {
LL_DEBUGS("Plugin") << "received message \"canceled\"" << LL_ENDL; LL_DEBUGS("Plugin") << "received message \"canceled\"" << LL_ENDL;
set_state(AIFilePicker_canceled); advance_state(AIFilePicker_canceled);
} }
else if (message_name == "done") else if (message_name == "done")
{ {
@@ -478,7 +468,7 @@ void AIFilePicker::receivePluginMessage(const LLPluginMessage &message)
{ {
mFilenames.push_back(*filename); mFilenames.push_back(*filename);
} }
set_state(AIFilePicker_done); advance_state(AIFilePicker_done);
} }
else else
{ {

View File

@@ -136,8 +136,8 @@ new AIFilePicker
which sets the state to AIFilePicker_canceled or AIFilePicker_done which sets the state to AIFilePicker_canceled or AIFilePicker_done
respectively, causing a call to AIStateMachine::finish(), which calls respectively, causing a call to AIStateMachine::finish(), which calls
AIFilePicker::finish_impl which destroys the plugin (mPluginBase), AIFilePicker::finish_impl which destroys the plugin (mPluginBase),
the plugin manager (mPluginManager) and calls AIStateMachine::kill() the plugin manager (mPluginManager) after which the state machine
causing the AIFilePicker to be deleted. calls unref() causing the AIFilePicker to be deleted.
*/ */
@@ -155,7 +155,7 @@ public:
AIFilePicker(void); AIFilePicker(void);
// Create a dynamically created AIFilePicker object. // Create a dynamically created AIFilePicker object.
static AIFilePicker* create(bool auto_kill = true) { AIFilePicker* filepicker = new AIFilePicker; filepicker->mAutoKill = auto_kill; return filepicker; } static AIFilePicker* create(void) { AIFilePicker* filepicker = new AIFilePicker; return filepicker; }
// The starting directory that the user will be in when the file picker opens // The starting directory that the user will be in when the file picker opens
// will be the same as the directory used the last time the file picker was // will be the same as the directory used the last time the file picker was
@@ -191,7 +191,6 @@ private:
typedef std::map<std::string, std::string> context_map_type; //!< Type of mContextMap. typedef std::map<std::string, std::string> context_map_type; //!< Type of mContextMap.
static AIThreadSafeSimpleDC<context_map_type> sContextMap; //!< Map context (ie, "snapshot" or "image") to last used folder. static AIThreadSafeSimpleDC<context_map_type> sContextMap; //!< Map context (ie, "snapshot" or "image") to last used folder.
std::string mContext; //!< Some key to indicate the context (remembers the folder per key). std::string mContext; //!< Some key to indicate the context (remembers the folder per key).
bool mAutoKill; //!< True if the default behavior is to delete itself after being finished.
// Input variables (cache variable between call to open and run). // Input variables (cache variable between call to open and run).
open_type mOpenType; //!< Set to whether opening a filepicker to select for saving one file, for loading one file, or loading multiple files. open_type mOpenType; //!< Set to whether opening a filepicker to select for saving one file, for loading one file, or loading multiple files.
@@ -215,10 +214,7 @@ protected:
/*virtual*/ void initialize_impl(void); /*virtual*/ void initialize_impl(void);
// Handle mRunState. // Handle mRunState.
/*virtual*/ void multiplex_impl(void); /*virtual*/ void multiplex_impl(state_type run_state);
// Handle aborting from current bs_run state.
/*virtual*/ void abort_impl(void);
// Handle cleaning up from initialization (or post abort) state. // Handle cleaning up from initialization (or post abort) state.
/*virtual*/ void finish_impl(void); /*virtual*/ void finish_impl(void);