Added AIFetchInventoryFolder and more.

Added a new statemachine AIFetchInventoryFolder, which can be used
to fetch the contents of a folder by name or UUID.

Also added AIEvent (and one event,
AIEvent::LLInventoryModel_mIsAgentInvUsable_true, which is needed
for AIFetchInventoryFolder).

Fixed LLInventoryModel::sBackgroundFetchActive to correctly reflect
whether or not LLInventoryModel::backgroundFetch is added to
gIdleCallbacks.

Avoid duplicated entries in sFetchQueue.

Reset sFullFetchStarted in LLInventoryModel::stopBackgroundFetch to
allow for a renewed full fetch when some single-folder fetch stops it.

Added AIStateMachine::mQueued to make calling 'cont()' more robust:
calling cont() / idle() / cont() on a row would otherwise add a
statemachine twice to the active list, which would cause a crash
when it's killed.
This commit is contained in:
Aleric Inglewood
2011-05-22 22:01:11 +02:00
parent e1fb1fa6b7
commit 13560e292e
12 changed files with 603 additions and 11 deletions

View File

@@ -403,7 +403,8 @@ protected:
// Locking control.
LLMutex mMutex;
// For use by AIThreadSafeSimpleDC
friend struct AIRegisteredStateMachinesList;
// For use by AIThreadSafeSimpleDC and AIRegisteredStateMachinesList.
AIThreadSafeSimple(void) { }
AIThreadSafeSimple(AIAPRPool& parent) : mMutex(parent) { }

View File

@@ -34,6 +34,7 @@
#define LL_LLHUDEFFECTLOOKAT_H
#include "llhudeffect.h"
#include "llframetimer.h"
class LLViewerObject;
class LLVOAvatar;

View File

@@ -59,6 +59,7 @@
#include "llviewercontrol.h"
#include "llvoavatar.h"
#include "llsdutil.h"
#include "statemachine/aievent.h"
// <edit>
#include "llappviewer.h" // gLostItemsRoot
// </edit>
@@ -349,6 +350,13 @@ void LLInventoryModel::getDirectDescendentsOf(const LLUUID& cat_id,
items = get_ptr_in_map(mParentChildItemTree, cat_id);
}
// Same but just categories.
void LLInventoryModel::getDirectDescendentsOf(const LLUUID& cat_id,
cat_array_t*& categories) const
{
categories = get_ptr_in_map(mParentChildCategoryTree, cat_id);
}
// SJB: Added version to lock the arrays to catch potential logic bugs
void LLInventoryModel::lockDirectDescendentArrays(const LLUUID& cat_id,
cat_array_t*& categories,
@@ -1727,7 +1735,6 @@ void LLInventoryModel::startBackgroundFetch(const LLUUID& cat_id)
{
if (!sAllFoldersFetched)
{
sBackgroundFetchActive = TRUE;
if (cat_id.isNull())
{
if (!sFullFetchStarted)
@@ -1735,15 +1742,29 @@ void LLInventoryModel::startBackgroundFetch(const LLUUID& cat_id)
sFullFetchStarted = TRUE;
sFetchQueue.push_back(gInventoryLibraryRoot);
sFetchQueue.push_back(gAgent.getInventoryRootID());
gIdleCallbacks.addFunction(&LLInventoryModel::backgroundFetch, NULL);
if (!sBackgroundFetchActive)
{
sBackgroundFetchActive = TRUE;
gIdleCallbacks.addFunction(&LLInventoryModel::backgroundFetch, NULL);
}
}
}
else
{
// specific folder requests go to front of queue
if (sFetchQueue.empty() || sFetchQueue.front() != cat_id)
// Remove it from the queue first, to avoid getting it twice.
if (!sFetchQueue.empty() && sFetchQueue.front() != cat_id)
{
sFetchQueue.push_front(cat_id);
std::deque<LLUUID>::iterator old_entry = std::find(sFetchQueue.begin(), sFetchQueue.end(), cat_id);
if (old_entry != sFetchQueue.end())
{
sFetchQueue.erase(old_entry);
}
}
sFetchQueue.push_front(cat_id);
if (!sBackgroundFetchActive)
{
sBackgroundFetchActive = TRUE;
gIdleCallbacks.addFunction(&LLInventoryModel::backgroundFetch, NULL);
}
}
@@ -1753,9 +1774,12 @@ void LLInventoryModel::startBackgroundFetch(const LLUUID& cat_id)
//static
void LLInventoryModel::findLostItems()
{
sBackgroundFetchActive = TRUE;
sFetchQueue.push_back(LLUUID::null);
gIdleCallbacks.addFunction(&LLInventoryModel::backgroundFetch, NULL);
if (!sBackgroundFetchActive)
{
sBackgroundFetchActive = TRUE;
gIdleCallbacks.addFunction(&LLInventoryModel::backgroundFetch, NULL);
}
}
//static
@@ -1767,7 +1791,11 @@ void LLInventoryModel::stopBackgroundFetch()
gIdleCallbacks.deleteFunction(&LLInventoryModel::backgroundFetch, NULL);
sBulkFetchCount=0;
sMinTimeBetweenFetches=0.0f;
// sFullFetchStarted=FALSE;
if (!sAllFoldersFetched)
{
// We didn't finish this, so set it to FALSE in order to be able to start it again.
sFullFetchStarted=FALSE;
}
}
}
@@ -2941,6 +2969,7 @@ void LLInventoryModel::buildParentChildMap()
// root of the agent's inv found.
// The inv tree is built.
mIsAgentInvUsable = true;
AIEvent::trigger(AIEvent::LLInventoryModel_mIsAgentInvUsable_true);
}
}
llinfos << " finished buildParentChildMap " << llendl;

View File

@@ -38,6 +38,7 @@
#include "lluuid.h"
#include "llpermissionsflags.h"
#include "llstring.h"
#include "llhttpclient.h"
#include <map>
#include <set>
@@ -199,6 +200,9 @@ public:
void getDirectDescendentsOf(const LLUUID& cat_id,
cat_array_t*& categories,
item_array_t*& items) const;
// Same but only get categories.
void getDirectDescendentsOf(const LLUUID& cat_id,
cat_array_t*& categories) const;
// Starting with the object specified, add its descendents to the
// array provided, but do not add the inventory object specified

View File

@@ -73,6 +73,7 @@ class LLViewerPartSourceScript;
class LLViewerRegion;
class LLViewerObjectMedia;
class LLVOInventoryListener;
class LLPartSysData;
typedef enum e_object_update_type
{

View File

@@ -13,7 +13,9 @@ include(LLWindow)
include(LLUI)
include(LLRender)
include(LLImage)
include(LLCharacter)
include(LLInventory)
include(LLPrimitive)
include_directories(
${CMAKE_SOURCE_DIR}/newview
@@ -27,11 +29,16 @@ include_directories(
${LLUI_INCLUDE_DIRS}
${LLRENDER_INCLUDE_DIRS}
${LLIMAGE_INCLUDE_DIRS}
${LLCHARACTER_INCLUDE_DIRS}
${LLINVENTORY_INCLUDE_DIRS}
${LLPRIMITIVE_INCLUDE_DIRS}
)
set(statemachine_SOURCE_FILES
aistatemachine.cpp
aifilepicker.cpp
aifetchinventoryfolder.cpp
aievent.cpp
)
set(statemachine_HEADER_FILES
@@ -39,6 +46,8 @@ set(statemachine_HEADER_FILES
aistatemachine.h
aifilepicker.h
aidirpicker.h
aifetchinventoryfolder.h
aievent.h
)
set_source_files_properties(${statemachine_HEADER_FILES}

View File

@@ -0,0 +1,113 @@
/**
* @file aievent.cpp
* @brief Implementation of AIEvent.
*
* Copyright (c) 2011, 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.
*
* 19/05/2011
* Initial version, written by Aleric Inglewood @ SL
*/
#include "linden_common.h"
#include "aievent.h"
#include "aistatemachine.h"
#include <map>
// Additional information stored per registered statemachine in AIRegisteredStateMachines std::map.
struct AIRSData {
bool mOneShot;
AIRSData(void) { }
AIRSData(bool one_shot) : mOneShot(one_shot) { }
};
// A list of all statemachines registered for a particular event, and and API to work on it.
struct AIRegisteredStateMachines {
typedef std::map<AIStateMachine*, AIRSData> rsm_type;
rsm_type mRegisteredStateMachines;
void Register(AIStateMachine* statemachine, bool one_shot) { mRegisteredStateMachines[statemachine] = one_shot; }
void Unregister(AIStateMachine* statemachine) { mRegisteredStateMachines.erase(statemachine); }
void trigger(void);
};
// Inline this, because it's only called from one place.
inline void AIRegisteredStateMachines::trigger(void)
{
rsm_type::iterator sm = mRegisteredStateMachines.begin();
while(sm != mRegisteredStateMachines.end())
{
sm->first->cont(); // This is safe, cont() does never access mRegisteredStateMachines.
if (sm->second.mOneShot)
mRegisteredStateMachines.erase(sm++);
else
++sm;
}
}
// A list (array) with all AIRegisteredStateMachines maps, one for each event type.
struct AIRegisteredStateMachinesList {
AIThreadSafeSimple<AIRegisteredStateMachines> mRegisteredStateMachinesList[AIEvent::number_of_events];
AIRegisteredStateMachinesList(void);
AIThreadSafeSimple<AIRegisteredStateMachines>& operator[](AIEvent::AIEvents event) { return mRegisteredStateMachinesList[event]; }
};
AIRegisteredStateMachinesList::AIRegisteredStateMachinesList(void)
{
for (int event = 0; event < AIEvent::number_of_events; ++event)
{
new (&mRegisteredStateMachinesList[event]) AIRegisteredStateMachines;
}
}
// Instantiate the list with all AIRegisteredStateMachines maps.
static AIRegisteredStateMachinesList registered_statemachines_list;
//-----------------------------------------------------------------------------
// External API starts here.
// Each function obtains access to the thread-safe AIRegisteredStateMachines that belongs
// to the given event (locking it's mutex) and then calls the corresponding method.
// Register a statemachine for notification of event.
// static
void AIEvent::Register(AIEvents event, AIStateMachine* statemachine, bool one_shot)
{
statemachine->idle();
AIAccess<AIRegisteredStateMachines> registered_statemachines_w(registered_statemachines_list[event]);
registered_statemachines_w->Register(statemachine, one_shot);
}
// Unregister a statemachine for notification of event.
// static
void AIEvent::Unregister(AIEvents event, AIStateMachine* statemachine)
{
AIAccess<AIRegisteredStateMachines> registered_statemachines_w(registered_statemachines_list[event]);
registered_statemachines_w->Unregister(statemachine);
}
// Trigger event.
// static
void AIEvent::trigger(AIEvents event)
{
AIAccess<AIRegisteredStateMachines> registered_statemachines_w(registered_statemachines_list[event]);
registered_statemachines_w->trigger();
}

View File

@@ -0,0 +1,78 @@
/**
* @file aievent.h
* @brief Collect viewer events for statemachines.
*
* Copyright (c) 2011, 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.
*
* 19/05/2011
* Initial version, written by Aleric Inglewood @ SL
*/
#ifndef AIEVENT_H
#define AIEVENT_H
class AIStateMachine;
class AIEvent {
public:
enum AIEvents {
LLInventoryModel_mIsAgentInvUsable_true, // LLInventoryModel::mIsAgentInvUsable was set to true.
number_of_events
};
/**
* Register a statemachine for the given event.
*
* If the event happens then statemachine->cont() is called.
*
* @param event the event that we want to be notified of.
* @param statemachine the statemachine.
* @param one_shot if true then AIEvent::Unregister is called automatically when the event occured once.
*/
static void Register(AIEvents event, AIStateMachine* statemachine, bool one_shot = true);
/**
* Unregister a previously registered event.
*
* This may be called for already unregistered events.
* This should be called from the destructor of a statemachine for any event it registers,
* as well as when it doesn't need the event anymore (in the case on non- one shot events).
*
* @param event the event we want to no longer be notified off.
* @param statemachine the statemachine.
*/
static void Unregister(AIEvents event, AIStateMachine* statemachine);
/**
* Trigger the event.
*
* This is called by the viewer code when the event happens and
* causes statemachine->cont() to be called for every registered
* statemachine.
*
* @param event the event that just happened.
*/
static void trigger(AIEvents event);
};
#endif

View File

@@ -0,0 +1,193 @@
/**
* @file aifetchinventoryfolder.cpp
* @brief Implementation of AIFetchInventoryFolder
*
* Copyright (c) 2011, 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.
*
* 19/05/2011
* Initial version, written by Aleric Inglewood @ SL
*/
#include "linden_common.h"
#include "aifetchinventoryfolder.h"
#include "aievent.h"
#include "llagent.h"
enum fetchinventoryfolder_state_type {
AIFetchInventoryFolder_checkFolderExists = AIStateMachine::max_state,
AIFetchInventoryFolder_fetchDescendents,
AIFetchInventoryFolder_folderCompleted
};
class AIInventoryFetchDescendentsObserver : public LLInventoryFetchDescendentsObserver {
public:
AIInventoryFetchDescendentsObserver(AIStateMachine* statemachine, LLUUID const& folder);
~AIInventoryFetchDescendentsObserver() { gInventory.removeObserver(this); }
protected:
/*virtual*/ void done()
{
mStateMachine->set_state(AIFetchInventoryFolder_folderCompleted);
delete this;
}
private:
AIStateMachine* mStateMachine;
};
AIInventoryFetchDescendentsObserver::AIInventoryFetchDescendentsObserver(AIStateMachine* statemachine, LLUUID const& folder) : mStateMachine(statemachine)
{
mStateMachine->idle();
folder_ref_t folders(1, folder);
fetchDescendents(folders);
gInventory.addObserver(this);
if (isEverythingComplete())
done();
}
void AIFetchInventoryFolder::fetch(std::string const& foldername, bool create, bool fetch_contents)
{
fetch(gAgent.getInventoryRootID(), foldername, create, fetch_contents);
}
char const* AIFetchInventoryFolder::state_str_impl(state_type run_state) const
{
switch(run_state)
{
AI_CASE_RETURN(AIFetchInventoryFolder_checkFolderExists);
AI_CASE_RETURN(AIFetchInventoryFolder_fetchDescendents);
AI_CASE_RETURN(AIFetchInventoryFolder_folderCompleted);
}
return "UNKNOWN STATE";
}
void AIFetchInventoryFolder::initialize_impl(void)
{
mExists = false;
mCreated = false;
mNeedNotifyObservers = false;
set_state(AIFetchInventoryFolder_checkFolderExists);
if (!gInventory.isInventoryUsable())
AIEvent::Register(AIEvent::LLInventoryModel_mIsAgentInvUsable_true, this);
}
void AIFetchInventoryFolder::multiplex_impl(void)
{
switch (mRunState)
{
case AIFetchInventoryFolder_checkFolderExists:
{
// If LLInventoryModel_mIsAgentInvUsable_true then this should be and stay true forever.
llassert(gInventory.isInventoryUsable());
if (mParentFolder.isNull())
mParentFolder = gAgent.getInventoryRootID();
if (mFolderUUID.isNull() || !gInventory.getCategory(mFolderUUID)) // Is the UUID unknown, or doesn't exist?
{
// Set this to null here in case we abort.
mFolderUUID.setNull();
if (mFolderName.empty())
{
// We can only find a folder by name, or create it, if we know it's name.
llwarns << "Unknown folder ID " << mFolderUUID << llendl;
abort();
break;
}
// Check if the parent exists.
if (mParentFolder != gAgent.getInventoryRootID() && !gInventory.getCategory(mParentFolder))
{
llwarns << "Unknown parent folder ID " << mParentFolder << llendl;
abort();
break;
}
// Look up UUID by name.
LLInventoryModel::cat_array_t* categories;
gInventory.getDirectDescendentsOf(mParentFolder, categories);
for (S32 i = 0; i < categories->getLength(); ++i)
{
LLPointer<LLViewerInventoryCategory> const& category(categories->get(i));
if (category->getName() == mFolderName)
{
mFolderUUID = category->getUUID();
break;
}
}
if (mFolderUUID.isNull()) // Does the folder exist?
{
if (!mCreate)
{
// We're done.
finish();
break;
}
// Create the folder.
mFolderUUID = gInventory.createNewCategory(mParentFolder, LLAssetType::AT_NONE, mFolderName);
llassert_always(!mFolderUUID.isNull());
Dout(dc::statemachine, "Created folder \"" << mFolderName << "\".");
mNeedNotifyObservers = true;
}
mCreated = true;
}
// mFolderUUID is now valid.
mExists = true;
if (!mFetchContents || // No request to fetch contents.
LLInventoryModel::isEverythingFetched()) // No need to fetch contents.
{
// We're done.
finish();
break;
}
set_state(AIFetchInventoryFolder_fetchDescendents);
/*Fall-through*/
}
case AIFetchInventoryFolder_fetchDescendents:
{
// This sets the state to AIFetchInventoryFolder_folderCompleted once the folder is complete.
new AIInventoryFetchDescendentsObserver(this, mFolderUUID);
break;
}
case AIFetchInventoryFolder_folderCompleted:
{
// Does it still exist?
if (!gInventory.getCategory(mFolderUUID))
{
// Assume the folder was deleted in the meantime.
abort();
break;
}
llassert(gInventory.isCategoryComplete(mFolderUUID));
// The folder is complete!
finish();
break;
}
}
}
void AIFetchInventoryFolder::abort_impl(void)
{
}
void AIFetchInventoryFolder::finish_impl(void)
{
if (mNeedNotifyObservers)
gInventory.notifyObservers();
}

View File

@@ -0,0 +1,152 @@
/**
* @file aifetchinventoryfolder.h
* @brief Fetch an inventory folder
*
* Copyright (c) 2011, 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.
*
* 19/05/2011
* Initial version, written by Aleric Inglewood @ SL
*/
#ifndef AIFETCHINVENTORYFOLDER_H
#define AIFETCHINVENTORYFOLDER_H
#include "aistatemachine.h"
#include "lluuid.h"
#include <map>
// An inventory folder fetch state machine.
//
// Before calling run(), call fetch() to pass needed parameters.
//
// When the state machine finishes, call aborted() to check
// whether or not the statemachine succeeded in fetching
// the folder or not.
//
// Objects of this type can be reused multiple times, see
// also the documentation of AIStateMachine.
class AIFetchInventoryFolder : public AIStateMachine {
private:
std::string mFolderName; //!< Input variable.
bool mCreate; //!< Input variable: create mFolderName if it doesn't exist.
bool mFetchContents; //!< Input variable: fetch contents before finishing.
LLUUID mParentFolder; //!< Input variable: the UUID of the parent folder.
LLUUID mFolderUUID; //!< Input and/or output variable.
bool mExists; //!< Output variable: true if the folder exists.
bool mCreated; //!< Output variable: true if mFolderName didn't exist and was created by this object.
bool mNeedNotifyObservers;
public:
AIFetchInventoryFolder(void) : mCreate(false), mFetchContents(false), mExists(false), mCreated(false)
{ Dout(dc::statemachine, "Calling AIFetchInventoryFolder constructor [" << (void*)this << "]"); }
/**
* @brief Fetch an inventory folder by name, optionally creating it.
*
* Upon successful finish (aborted() returns false), exists() will return true
* if the folder exists; created() will return true if it was created;
* UUID() will return the UUID of the folder. It will then also be possible
* to scan over all folders (Category) of this folder. If fetch_contents
* is set, you will also be able to scan over the contents of the folder
* upon successful finish.
*
* @param parentUUID The UUID of the parent. Passing gAgent.getInventoryRootID(), or a null ID, will assume a root folder.
* @param foldername The name of the folder.
* @param create if set, create the folder if it doesn't exists yet.
* @param fetch_contents if set, fetch the contents before finishing.
*/
void fetch(LLUUID const& parentUUID, std::string const& foldername, bool create = false, bool fetch_contents = true)
{
mParentFolder = parentUUID;
mFetchContents = fetch_contents;
if (mFolderName != foldername)
{
mFolderName = foldername;
mFolderUUID.setNull();
}
}
/**
* @brief Fetch an inventory folder by name, optionally creating it.
*
* Upon successful finish (aborted() returns false), exists() will return
* true if the folder exists; created() will return true if it was created;
* UUID() will return the UUID of the folder. It will then also be possible
* to scan over all folders (Category) of this folder. If fetch_contents
* is set, you will also be able to scan over the contents of the folder
* upon successful finish.
*
* @param foldername The name of the folder.
* @param create if set, create the folder if it doesn't exists yet.
* @param fetch_contents if set, fetch the contents before finishing.
*/
void fetch(std::string const& foldername, bool create = false, bool fetch_contents = true);
/**
* @brief Fetch an inventory folder by UUID.
*
* Upon successful finish (aborted() returns false), exists() will return true
* if the folder exists; it will then be possible to scan over all folders (Category)
* of this folder. If fetch_contents is set, you will also be able to scan over
* the contents of the folder upon successful finish.
*
* @param folderUUID The UUID of the folder.
* @param fetch_contents if set, fetch the contents before finishing.
*/
void fetch(LLUUID const& folderUUID, bool fetch_contents = true)
{
mFetchContents = fetch_contents;
if (mFolderUUID != folderUUID)
{
mFolderName.clear();
mFolderUUID = folderUUID;
}
}
std::string const& name(void) const { return mFolderName; }
bool exists(void) const { return mExists; }
bool created(void) const { return mCreated; }
LLUUID const& UUID(void) const { llassert(mExists || mFolderUUID.isNull()); return mFolderUUID; }
protected:
// Call finish() (or abort()), not delete.
/*virtual*/ ~AIFetchInventoryFolder() { Dout(dc::statemachine, "Calling ~AIFetchInventoryFolder() [" << (void*)this << "]"); }
// Handle initializing the object.
/*virtual*/ void initialize_impl(void);
// Handle mRunState.
/*virtual*/ void multiplex_impl(void);
// 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);
// Implemenation of state_str for run states.
/*virtual*/ char const* state_str_impl(state_type run_state) const;
};
#endif

View File

@@ -132,6 +132,8 @@ void AIStateMachine::cont(void)
DoutEntering(dc::statemachine, "AIStateMachine::cont() [" << (void*)this << "]");
llassert(mIdle);
mIdle = false;
if (mQueued)
return;
AIWriteAccess<cscm_type> cscm_w(continued_statemachines_and_calling_mainloop);
cscm_w->continued_statemachines.push_back(this);
if (!cscm_w->calling_mainloop)
@@ -140,6 +142,7 @@ void AIStateMachine::cont(void)
cscm_w->calling_mainloop = true;
gIdleCallbacks.addFunction(&AIStateMachine::mainloop);
}
mQueued = true;
}
void AIStateMachine::set_state(state_type state)
@@ -214,6 +217,12 @@ void AIStateMachine::kill(void)
{
// Should only be called from finish().
llassert(mIdle && (mState == bs_initialize || mState == bs_finish));
if (mState == bs_initialize)
{
// Bump the statemachine onto the active statemachine list, or else it won't be deleted.
cont();
idle();
}
mState = bs_killed;
}
@@ -288,6 +297,7 @@ void AIStateMachine::mainloop(void*)
nonempty = true;
active_statemachines.push_back(QueueElement(*iter));
Dout(dc::statemachine, "Adding " << (void*)*iter << " to active_statemachines");
(*iter)->mQueued = false;
}
if (nonempty)
AIWriteAccess<cscm_type>(cscm_r)->continued_statemachines.clear();

View File

@@ -184,6 +184,7 @@ class AIStateMachine {
base_state_type mState; //!< State of the base class.
bool mIdle; //!< True if this state machine is not running.
bool mAborted; //!< True after calling abort() and before calling run().
bool mQueued; //!< True when the statemachine is queued to be added back to the active list.
S64 mSleep; //!< Non-zero while the state machine is sleeping.
// Callback facilities.
@@ -211,11 +212,11 @@ class AIStateMachine {
public:
//! Create a non-running state machine.
AIStateMachine(void) : mState(bs_initialize), mIdle(true), mAborted(true), mSleep(0), mParent(NULL), mCallback(NULL) { updateSettings(); }
AIStateMachine(void) : mState(bs_initialize), mIdle(true), mAborted(true), mQueued(false), mSleep(0), mParent(NULL), mCallback(NULL) { updateSettings(); }
protected:
//! The user should call 'kill()', not delete a AIStateMachine (derived) directly.
virtual ~AIStateMachine() { llassert(mState == bs_killed); }
virtual ~AIStateMachine() { llassert(mState == bs_killed && !mQueued); }
public:
//! Halt the state machine until cont() is called.