Merge branch 'master' of git://github.com/AlericInglewood/SingularityViewer
This commit is contained in:
@@ -21,6 +21,7 @@ set(aistatemachine_SOURCE_FILES
|
|||||||
aistatemachine.cpp
|
aistatemachine.cpp
|
||||||
aistatemachinethread.cpp
|
aistatemachinethread.cpp
|
||||||
aitimer.cpp
|
aitimer.cpp
|
||||||
|
aicondition.cpp
|
||||||
)
|
)
|
||||||
|
|
||||||
set(aistatemachine_HEADER_FILES
|
set(aistatemachine_HEADER_FILES
|
||||||
@@ -29,6 +30,7 @@ set(aistatemachine_HEADER_FILES
|
|||||||
aistatemachine.h
|
aistatemachine.h
|
||||||
aistatemachinethread.h
|
aistatemachinethread.h
|
||||||
aitimer.h
|
aitimer.h
|
||||||
|
aicondition.h
|
||||||
)
|
)
|
||||||
|
|
||||||
set_source_files_properties(${aistatemachine_HEADER_FILES}
|
set_source_files_properties(${aistatemachine_HEADER_FILES}
|
||||||
|
|||||||
88
indra/aistatemachine/aicondition.cpp
Normal file
88
indra/aistatemachine/aicondition.cpp
Normal file
@@ -0,0 +1,88 @@
|
|||||||
|
/**
|
||||||
|
* @file aicondition.cpp
|
||||||
|
* @brief Implementation of AICondition
|
||||||
|
*
|
||||||
|
* Copyright (c) 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.
|
||||||
|
*
|
||||||
|
* 14/10/2013
|
||||||
|
* Initial version, written by Aleric Inglewood @ SL
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "aicondition.h"
|
||||||
|
#include "aistatemachine.h"
|
||||||
|
|
||||||
|
void AIConditionBase::wait(AIStateMachine* state_machine)
|
||||||
|
{
|
||||||
|
// The condition must be locked before calling AIStateMachine::wait().
|
||||||
|
llassert(mutex().isSelfLocked());
|
||||||
|
// Add the new state machine at the end.
|
||||||
|
mWaitingStateMachines.push_back(state_machine);
|
||||||
|
}
|
||||||
|
|
||||||
|
void AIConditionBase::remove(AIStateMachine* state_machine)
|
||||||
|
{
|
||||||
|
mutex().lock();
|
||||||
|
// Remove all occurances of state_machine from the queue.
|
||||||
|
queue_t::iterator const end = mWaitingStateMachines.end();
|
||||||
|
queue_t::iterator last = end;
|
||||||
|
for (queue_t::iterator iter = mWaitingStateMachines.begin(); iter != last; ++iter)
|
||||||
|
{
|
||||||
|
if (iter->get() == state_machine)
|
||||||
|
{
|
||||||
|
if (--last == iter)
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
queue_t::value_type::swap(*iter, *last);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// This invalidates all iterators involved, including end, but not any iterators to the remaining elements.
|
||||||
|
mWaitingStateMachines.erase(last, end);
|
||||||
|
mutex().unlock();
|
||||||
|
}
|
||||||
|
|
||||||
|
void AIConditionBase::signal(int n)
|
||||||
|
{
|
||||||
|
// The condition must be locked before calling AICondition::signal or AICondition::broadcast.
|
||||||
|
llassert(mutex().isSelfLocked());
|
||||||
|
// Signal n state machines.
|
||||||
|
while (n > 0 && !mWaitingStateMachines.empty())
|
||||||
|
{
|
||||||
|
LLPointer<AIStateMachine> state_machine = mWaitingStateMachines.front();
|
||||||
|
bool success = state_machine->signalled();
|
||||||
|
// Only state machines that are actually still blocked should be in the queue:
|
||||||
|
// they are removed from the queue by calling AICondition::remove whenever
|
||||||
|
// they are unblocked for whatever reason...
|
||||||
|
llassert(success);
|
||||||
|
if (success)
|
||||||
|
{
|
||||||
|
++n;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// We never get here...
|
||||||
|
remove(state_machine.get());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
110
indra/aistatemachine/aicondition.h
Normal file
110
indra/aistatemachine/aicondition.h
Normal file
@@ -0,0 +1,110 @@
|
|||||||
|
/**
|
||||||
|
* @file aicondition.h
|
||||||
|
* @brief Condition variable for statemachines.
|
||||||
|
*
|
||||||
|
* Copyright (c) 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.
|
||||||
|
*
|
||||||
|
* 14/10/2013
|
||||||
|
* Initial version, written by Aleric Inglewood @ SL
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef AICONDITION_H
|
||||||
|
#define AICONDITION_H
|
||||||
|
|
||||||
|
#include <deque>
|
||||||
|
#include <llpointer.h>
|
||||||
|
#include "aithreadsafe.h"
|
||||||
|
|
||||||
|
class AIStateMachine;
|
||||||
|
class LLMutex;
|
||||||
|
|
||||||
|
// class AICondition
|
||||||
|
//
|
||||||
|
// Call AIStateMachine::wait(AICondition&) in the multiplex_impl of a state machine to
|
||||||
|
// make the state machine go idle until some thread calls AICondition::signal().
|
||||||
|
//
|
||||||
|
// If the state machine is no longer running or wasn't waiting anymore because
|
||||||
|
// something else woke it up, then AICondition::signal() will wake up another
|
||||||
|
// state machine (if any).
|
||||||
|
//
|
||||||
|
// Usage:
|
||||||
|
//
|
||||||
|
// struct Foo { bool met(); }; // Returns true when the condition is met.
|
||||||
|
// AICondition<Foo> Condition_t;
|
||||||
|
// AIAccess<Foo> Condition_wat;
|
||||||
|
//
|
||||||
|
// // Some thread-safe condition variable.
|
||||||
|
// Condition_t condition;
|
||||||
|
//
|
||||||
|
// // Inside the state machine:
|
||||||
|
// {
|
||||||
|
// ...
|
||||||
|
// state WAIT_FOR_CONDITION:
|
||||||
|
// {
|
||||||
|
// // Lock condition and check it. Wait if condition is not met yet.
|
||||||
|
// {
|
||||||
|
// Condition_wat condition_w(condition);
|
||||||
|
// if (!condition_w->met())
|
||||||
|
// {
|
||||||
|
// wait(condition);
|
||||||
|
// break;
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// set_state(CONDITION_MET);
|
||||||
|
// break;
|
||||||
|
// }
|
||||||
|
// CONDITION_MET:
|
||||||
|
// {
|
||||||
|
//
|
||||||
|
|
||||||
|
class AIConditionBase
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
virtual ~AIConditionBase() { }
|
||||||
|
|
||||||
|
void signal(int n = 1); // Call this when the condition was met to release n state machines.
|
||||||
|
void broadcast(void) { signal(mWaitingStateMachines.size()); } // Release all blocked state machines.
|
||||||
|
|
||||||
|
private:
|
||||||
|
// These functions are called by AIStateMachine.
|
||||||
|
friend class AIStateMachine;
|
||||||
|
void wait(AIStateMachine* state_machine);
|
||||||
|
void remove(AIStateMachine* state_machine);
|
||||||
|
|
||||||
|
protected:
|
||||||
|
virtual LLMutex& mutex(void) = 0;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
typedef std::deque<LLPointer<AIStateMachine> > queue_t;
|
||||||
|
queue_t mWaitingStateMachines;
|
||||||
|
};
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
class AICondition : public AIThreadSafeSimpleDC<T>, public AIConditionBase
|
||||||
|
{
|
||||||
|
protected:
|
||||||
|
/*virtual*/ LLMutex& mutex(void) { return this->mMutex; }
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
@@ -33,6 +33,7 @@
|
|||||||
|
|
||||||
#include "linden_common.h"
|
#include "linden_common.h"
|
||||||
#include "aistatemachine.h"
|
#include "aistatemachine.h"
|
||||||
|
#include "aicondition.h"
|
||||||
#include "lltimer.h"
|
#include "lltimer.h"
|
||||||
|
|
||||||
//==================================================================
|
//==================================================================
|
||||||
@@ -283,7 +284,7 @@ char const* HelloWorld::state_str_impl(state_type run_state) const
|
|||||||
|
|
||||||
void AIEngine::add(AIStateMachine* state_machine)
|
void AIEngine::add(AIStateMachine* state_machine)
|
||||||
{
|
{
|
||||||
Dout(dc::statemachine, "Adding state machine [" << (void*)state_machine << "] to " << mName);
|
Dout(dc::statemachine(state_machine->mSMDebug), "Adding state machine [" << (void*)state_machine << "] to " << mName);
|
||||||
engine_state_type_wat engine_state_w(mEngineState);
|
engine_state_type_wat engine_state_w(mEngineState);
|
||||||
engine_state_w->list.push_back(QueueElement(state_machine));
|
engine_state_w->list.push_back(QueueElement(state_machine));
|
||||||
if (engine_state_w->waiting)
|
if (engine_state_w->waiting)
|
||||||
@@ -330,7 +331,7 @@ void AIEngine::mainloop(void)
|
|||||||
engine_state_type_wat engine_state_w(mEngineState);
|
engine_state_type_wat engine_state_w(mEngineState);
|
||||||
if (!active)
|
if (!active)
|
||||||
{
|
{
|
||||||
Dout(dc::statemachine, "Erasing state machine [" << (void*)&state_machine << "] from " << mName);
|
Dout(dc::statemachine(state_machine.mSMDebug), "Erasing state machine [" << (void*)&state_machine << "] from " << mName);
|
||||||
engine_state_w->list.erase(queued_element++);
|
engine_state_w->list.erase(queued_element++);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@@ -392,7 +393,7 @@ void AIStateMachine::multiplex(event_type event)
|
|||||||
// If this fails then you are using a pointer to a state machine instead of an LLPointer.
|
// If this fails then you are using a pointer to a state machine instead of an LLPointer.
|
||||||
llassert(event == initial_run || getNumRefs() > 0);
|
llassert(event == initial_run || getNumRefs() > 0);
|
||||||
|
|
||||||
DoutEntering(dc::statemachine, "AIStateMachine::multiplex(" << event_str(event) << ") [" << (void*)this << "]");
|
DoutEntering(dc::statemachine(mSMDebug), "AIStateMachine::multiplex(" << event_str(event) << ") [" << (void*)this << "]");
|
||||||
|
|
||||||
base_state_type state;
|
base_state_type state;
|
||||||
state_type run_state;
|
state_type run_state;
|
||||||
@@ -407,7 +408,7 @@ void AIStateMachine::multiplex(event_type event)
|
|||||||
llassert(!mMultiplexMutex.isSelfLocked()); // We may never enter recursively!
|
llassert(!mMultiplexMutex.isSelfLocked()); // We may never enter recursively!
|
||||||
if (!mMultiplexMutex.tryLock())
|
if (!mMultiplexMutex.tryLock())
|
||||||
{
|
{
|
||||||
Dout(dc::statemachine, "Leaving because it is already being run [" << (void*)this << "]");
|
Dout(dc::statemachine(mSMDebug), "Leaving because it is already being run [" << (void*)this << "]");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -421,7 +422,7 @@ void AIStateMachine::multiplex(event_type event)
|
|||||||
// we should indeed run, again.
|
// we should indeed run, again.
|
||||||
if (event == schedule_run && !sub_state_type_rat(mSubState)->need_run)
|
if (event == schedule_run && !sub_state_type_rat(mSubState)->need_run)
|
||||||
{
|
{
|
||||||
Dout(dc::statemachine, "Leaving because it was already being run [" << (void*)this << "]");
|
Dout(dc::statemachine(mSMDebug), "Leaving because it was already being run [" << (void*)this << "]");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -440,9 +441,9 @@ void AIStateMachine::multiplex(event_type event)
|
|||||||
{
|
{
|
||||||
#ifdef CWDEBUG
|
#ifdef CWDEBUG
|
||||||
if (state == bs_multiplex)
|
if (state == bs_multiplex)
|
||||||
Dout(dc::statemachine, "Running state bs_multiplex / " << state_str_impl(run_state) << " [" << (void*)this << "]");
|
Dout(dc::statemachine(mSMDebug), "Running state bs_multiplex / " << state_str_impl(run_state) << " [" << (void*)this << "]");
|
||||||
else
|
else
|
||||||
Dout(dc::statemachine, "Running state " << state_str(state) << " [" << (void*)this << "]");
|
Dout(dc::statemachine(mSMDebug), "Running state " << state_str(state) << " [" << (void*)this << "]");
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef SHOW_ASSERT
|
#ifdef SHOW_ASSERT
|
||||||
@@ -503,7 +504,7 @@ void AIStateMachine::multiplex(event_type event)
|
|||||||
// run of bs_reset is not a problem because it happens to be a NoOp.
|
// run of bs_reset is not a problem because it happens to be a NoOp.
|
||||||
state = (state == bs_initialize) ? bs_reset : bs_abort;
|
state = (state == bs_initialize) ? bs_reset : bs_abort;
|
||||||
#ifdef CWDEBUG
|
#ifdef CWDEBUG
|
||||||
Dout(dc::statemachine, "Late abort detected! Running state " << state_str(state) << " instead [" << (void*)this << "]");
|
Dout(dc::statemachine(mSMDebug), "Late abort detected! Running state " << state_str(state) << " instead [" << (void*)this << "]");
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
#ifdef SHOW_ASSERT
|
#ifdef SHOW_ASSERT
|
||||||
@@ -665,7 +666,7 @@ void AIStateMachine::multiplex(event_type event)
|
|||||||
|
|
||||||
#ifdef CWDEBUG
|
#ifdef CWDEBUG
|
||||||
if (state != state_w->base_state)
|
if (state != state_w->base_state)
|
||||||
Dout(dc::statemachine, "Base state changed from " << state_str(state) << " to " << state_str(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 << "]");
|
"; need_new_run = " << (need_new_run ? "true" : "false") << " [" << (void*)this << "]");
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
@@ -699,11 +700,15 @@ void AIStateMachine::multiplex(event_type event)
|
|||||||
// Mark that we're added to this engine, and at the same time, that we're not added to the previous one.
|
// 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;
|
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
|
else
|
||||||
{
|
{
|
||||||
// Remove this state machine from any engine.
|
// Remove this state machine from any engine,
|
||||||
// Cause the engine to remove us.
|
// causing the engine to remove us.
|
||||||
state_w->current_engine = NULL;
|
state_w->current_engine = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -749,7 +754,7 @@ void AIStateMachine::multiplex(event_type event)
|
|||||||
|
|
||||||
AIStateMachine::state_type AIStateMachine::begin_loop(base_state_type base_state)
|
AIStateMachine::state_type AIStateMachine::begin_loop(base_state_type base_state)
|
||||||
{
|
{
|
||||||
DoutEntering(dc::statemachine, "AIStateMachine::begin_loop(" << state_str(base_state) << ") [" << (void*)this << "]");
|
DoutEntering(dc::statemachine(mSMDebug), "AIStateMachine::begin_loop(" << state_str(base_state) << ") [" << (void*)this << "]");
|
||||||
|
|
||||||
sub_state_type_wat sub_state_w(mSubState);
|
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).
|
// Honor a subsequent call to idle() (only necessary in bs_multiplex, but it doesn't hurt to reset this flag in other states too).
|
||||||
@@ -759,7 +764,7 @@ AIStateMachine::state_type AIStateMachine::begin_loop(base_state_type base_state
|
|||||||
// Honor previous calls to advance_state() (once run_state is initialized).
|
// 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)
|
if (base_state == bs_multiplex && sub_state_w->advance_state > sub_state_w->run_state)
|
||||||
{
|
{
|
||||||
Dout(dc::statemachine, "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) << "]");
|
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;
|
sub_state_w->run_state = sub_state_w->advance_state;
|
||||||
}
|
}
|
||||||
#ifdef SHOW_ASSERT
|
#ifdef SHOW_ASSERT
|
||||||
@@ -789,7 +794,7 @@ AIStateMachine::state_type AIStateMachine::begin_loop(base_state_type base_state
|
|||||||
|
|
||||||
void AIStateMachine::run(AIStateMachine* parent, state_type new_parent_state, bool abort_parent, bool on_abort_signal_parent, AIEngine* default_engine)
|
void AIStateMachine::run(AIStateMachine* parent, state_type new_parent_state, bool abort_parent, bool on_abort_signal_parent, AIEngine* default_engine)
|
||||||
{
|
{
|
||||||
DoutEntering(dc::statemachine, "AIStateMachine::run(" <<
|
DoutEntering(dc::statemachine(mSMDebug), "AIStateMachine::run(" <<
|
||||||
(void*)parent << ", " <<
|
(void*)parent << ", " <<
|
||||||
(parent ? parent->state_str_impl(new_parent_state) : "NA") <<
|
(parent ? parent->state_str_impl(new_parent_state) : "NA") <<
|
||||||
", abort_parent = " << (abort_parent ? "true" : "false") <<
|
", abort_parent = " << (abort_parent ? "true" : "false") <<
|
||||||
@@ -839,7 +844,7 @@ void AIStateMachine::run(AIStateMachine* parent, state_type new_parent_state, bo
|
|||||||
|
|
||||||
void AIStateMachine::run(callback_type::signal_type::slot_type const& slot, AIEngine* default_engine)
|
void AIStateMachine::run(callback_type::signal_type::slot_type const& slot, AIEngine* default_engine)
|
||||||
{
|
{
|
||||||
DoutEntering(dc::statemachine, "AIStateMachine::run(<slot>, default_engine = " << default_engine->name() << ") [" << (void*)this << "]");
|
DoutEntering(dc::statemachine(mSMDebug), "AIStateMachine::run(<slot>, default_engine = " << default_engine->name() << ") [" << (void*)this << "]");
|
||||||
|
|
||||||
#ifdef SHOW_ASSERT
|
#ifdef SHOW_ASSERT
|
||||||
{
|
{
|
||||||
@@ -874,7 +879,7 @@ void AIStateMachine::run(callback_type::signal_type::slot_type const& slot, AIEn
|
|||||||
|
|
||||||
void AIStateMachine::callback(void)
|
void AIStateMachine::callback(void)
|
||||||
{
|
{
|
||||||
DoutEntering(dc::statemachine, "AIStateMachine::callback() [" << (void*)this << "]");
|
DoutEntering(dc::statemachine(mSMDebug), "AIStateMachine::callback() [" << (void*)this << "]");
|
||||||
|
|
||||||
bool aborted = sub_state_type_rat(mSubState)->aborted;
|
bool aborted = sub_state_type_rat(mSubState)->aborted;
|
||||||
if (mParent)
|
if (mParent)
|
||||||
@@ -920,7 +925,7 @@ void AIStateMachine::force_killed(void)
|
|||||||
|
|
||||||
void AIStateMachine::kill(void)
|
void AIStateMachine::kill(void)
|
||||||
{
|
{
|
||||||
DoutEntering(dc::statemachine, "AIStateMachine::kill() [" << (void*)this << "]");
|
DoutEntering(dc::statemachine(mSMDebug), "AIStateMachine::kill() [" << (void*)this << "]");
|
||||||
#ifdef SHOW_ASSERT
|
#ifdef SHOW_ASSERT
|
||||||
{
|
{
|
||||||
multiplex_state_type_rat state_r(mState);
|
multiplex_state_type_rat state_r(mState);
|
||||||
@@ -937,7 +942,7 @@ void AIStateMachine::kill(void)
|
|||||||
|
|
||||||
void AIStateMachine::reset()
|
void AIStateMachine::reset()
|
||||||
{
|
{
|
||||||
DoutEntering(dc::statemachine, "AIStateMachine::reset() [" << (void*)this << "]");
|
DoutEntering(dc::statemachine(mSMDebug), "AIStateMachine::reset() [" << (void*)this << "]");
|
||||||
#ifdef SHOW_ASSERT
|
#ifdef SHOW_ASSERT
|
||||||
mDebugAborted = false;
|
mDebugAborted = false;
|
||||||
mDebugContPending = false;
|
mDebugContPending = false;
|
||||||
@@ -960,6 +965,8 @@ void AIStateMachine::reset()
|
|||||||
sub_state_w->reset = true;
|
sub_state_w->reset = true;
|
||||||
// Start running.
|
// Start running.
|
||||||
sub_state_w->idle = false;
|
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.
|
// Keep running till we reach at least bs_multiplex.
|
||||||
sub_state_w->need_run = true;
|
sub_state_w->need_run = true;
|
||||||
}
|
}
|
||||||
@@ -972,7 +979,7 @@ void AIStateMachine::reset()
|
|||||||
|
|
||||||
void AIStateMachine::set_state(state_type new_state)
|
void AIStateMachine::set_state(state_type new_state)
|
||||||
{
|
{
|
||||||
DoutEntering(dc::statemachine, "AIStateMachine::set_state(" << state_str_impl(new_state) << ") [" << (void*)this << "]");
|
DoutEntering(dc::statemachine(mSMDebug), "AIStateMachine::set_state(" << state_str_impl(new_state) << ") [" << (void*)this << "]");
|
||||||
#ifdef SHOW_ASSERT
|
#ifdef SHOW_ASSERT
|
||||||
{
|
{
|
||||||
multiplex_state_type_rat state_r(mState);
|
multiplex_state_type_rat state_r(mState);
|
||||||
@@ -983,6 +990,8 @@ void AIStateMachine::set_state(state_type new_state)
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
sub_state_type_wat sub_state_w(mSubState);
|
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.
|
// Force current state to the requested state.
|
||||||
sub_state_w->run_state = new_state;
|
sub_state_w->run_state = new_state;
|
||||||
// Void last call to advance_state.
|
// Void last call to advance_state.
|
||||||
@@ -999,13 +1008,13 @@ void AIStateMachine::set_state(state_type new_state)
|
|||||||
|
|
||||||
void AIStateMachine::advance_state(state_type new_state)
|
void AIStateMachine::advance_state(state_type new_state)
|
||||||
{
|
{
|
||||||
DoutEntering(dc::statemachine, "AIStateMachine::advance_state(" << state_str_impl(new_state) << ") [" << (void*)this << "]");
|
DoutEntering(dc::statemachine(mSMDebug), "AIStateMachine::advance_state(" << state_str_impl(new_state) << ") [" << (void*)this << "]");
|
||||||
{
|
{
|
||||||
sub_state_type_wat sub_state_w(mSubState);
|
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.
|
// 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)
|
if (sub_state_w->advance_state >= new_state)
|
||||||
{
|
{
|
||||||
Dout(dc::statemachine, "Ignored, because " << state_str_impl(sub_state_w->advance_state) << " >= " << state_str_impl(new_state) << ".");
|
Dout(dc::statemachine(mSMDebug), "Ignored, because " << state_str_impl(sub_state_w->advance_state) << " >= " << state_str_impl(new_state) << ".");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// Ignore call to advance_state when the current state is greater than the requested state: the new state would be
|
// Ignore call to advance_state when the current state is greater than the requested state: the new state would be
|
||||||
@@ -1014,7 +1023,7 @@ void AIStateMachine::advance_state(state_type new_state)
|
|||||||
// the state change is and should be being ignored: the statemachine would start running it's current state (again).
|
// 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)
|
if (sub_state_w->run_state > new_state)
|
||||||
{
|
{
|
||||||
Dout(dc::statemachine, "Ignored, because " << state_str_impl(sub_state_w->run_state) << " > " << state_str_impl(new_state) << " (current state).");
|
Dout(dc::statemachine(mSMDebug), "Ignored, because " << state_str_impl(sub_state_w->run_state) << " > " << state_str_impl(new_state) << " (current state).");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// Increment state.
|
// Increment state.
|
||||||
@@ -1023,6 +1032,13 @@ void AIStateMachine::advance_state(state_type new_state)
|
|||||||
sub_state_w->idle = false;
|
sub_state_w->idle = false;
|
||||||
// Ignore a call to idle if it occurs before we leave multiplex_impl().
|
// Ignore a call to idle if it occurs before we leave multiplex_impl().
|
||||||
sub_state_w->skip_idle = true;
|
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.
|
// Mark that a re-entry of multiplex() is necessary.
|
||||||
sub_state_w->need_run = true;
|
sub_state_w->need_run = true;
|
||||||
#ifdef SHOW_ASSERT
|
#ifdef SHOW_ASSERT
|
||||||
@@ -1048,7 +1064,7 @@ void AIStateMachine::advance_state(state_type new_state)
|
|||||||
|
|
||||||
void AIStateMachine::idle(void)
|
void AIStateMachine::idle(void)
|
||||||
{
|
{
|
||||||
DoutEntering(dc::statemachine, "AIStateMachine::idle() [" << (void*)this << "]");
|
DoutEntering(dc::statemachine(mSMDebug), "AIStateMachine::idle() [" << (void*)this << "]");
|
||||||
#ifdef SHOW_ASSERT
|
#ifdef SHOW_ASSERT
|
||||||
{
|
{
|
||||||
multiplex_state_type_rat state_r(mState);
|
multiplex_state_type_rat state_r(mState);
|
||||||
@@ -1066,7 +1082,7 @@ void AIStateMachine::idle(void)
|
|||||||
// Ignore call to idle() when advance_state() was called since last call to set_state().
|
// Ignore call to idle() when advance_state() was called since last call to set_state().
|
||||||
if (sub_state_w->skip_idle)
|
if (sub_state_w->skip_idle)
|
||||||
{
|
{
|
||||||
Dout(dc::statemachine, "Ignored, because skip_idle is true (advance_state() was called last).");
|
Dout(dc::statemachine(mSMDebug), "Ignored, because skip_idle is true (advance_state() was called last).");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// Mark that we are idle.
|
// Mark that we are idle.
|
||||||
@@ -1075,13 +1091,54 @@ void AIStateMachine::idle(void)
|
|||||||
mSleep = 0;
|
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)
|
void AIStateMachine::cont(void)
|
||||||
{
|
{
|
||||||
DoutEntering(dc::statemachine, "AIStateMachine::cont() [" << (void*)this << "]");
|
DoutEntering(dc::statemachine(mSMDebug), "AIStateMachine::cont() [" << (void*)this << "]");
|
||||||
{
|
{
|
||||||
sub_state_type_wat sub_state_w(mSubState);
|
sub_state_type_wat sub_state_w(mSubState);
|
||||||
// Void last call to idle(), if any.
|
// Void last call to idle(), if any.
|
||||||
sub_state_w->idle = false;
|
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.
|
// Mark that a re-entry of multiplex() is necessary.
|
||||||
sub_state_w->need_run = true;
|
sub_state_w->need_run = true;
|
||||||
#ifdef SHOW_ASSERT
|
#ifdef SHOW_ASSERT
|
||||||
@@ -1095,15 +1152,56 @@ void AIStateMachine::cont(void)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 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)
|
void AIStateMachine::abort(void)
|
||||||
{
|
{
|
||||||
DoutEntering(dc::statemachine, "AIStateMachine::abort() [" << (void*)this << "]");
|
DoutEntering(dc::statemachine(mSMDebug), "AIStateMachine::abort() [" << (void*)this << "]");
|
||||||
bool is_waiting = false;
|
bool is_waiting = false;
|
||||||
{
|
{
|
||||||
multiplex_state_type_rat state_r(mState);
|
multiplex_state_type_rat state_r(mState);
|
||||||
sub_state_type_wat sub_state_w(mSubState);
|
sub_state_type_wat sub_state_w(mSubState);
|
||||||
// Mark that we are aborted, iff we didn't already finish.
|
// Mark that we are aborted, iff we didn't already finish.
|
||||||
sub_state_w->aborted = !sub_state_w->finished;
|
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.
|
// Mark that a re-entry of multiplex() is necessary.
|
||||||
sub_state_w->need_run = true;
|
sub_state_w->need_run = true;
|
||||||
// Schedule a new run when this state machine is waiting.
|
// Schedule a new run when this state machine is waiting.
|
||||||
@@ -1128,7 +1226,7 @@ void AIStateMachine::abort(void)
|
|||||||
|
|
||||||
void AIStateMachine::finish(void)
|
void AIStateMachine::finish(void)
|
||||||
{
|
{
|
||||||
DoutEntering(dc::statemachine, "AIStateMachine::finish() [" << (void*)this << "]");
|
DoutEntering(dc::statemachine(mSMDebug), "AIStateMachine::finish() [" << (void*)this << "]");
|
||||||
#ifdef SHOW_ASSERT
|
#ifdef SHOW_ASSERT
|
||||||
{
|
{
|
||||||
multiplex_state_type_rat state_r(mState);
|
multiplex_state_type_rat state_r(mState);
|
||||||
@@ -1147,7 +1245,7 @@ void AIStateMachine::finish(void)
|
|||||||
|
|
||||||
void AIStateMachine::yield(void)
|
void AIStateMachine::yield(void)
|
||||||
{
|
{
|
||||||
DoutEntering(dc::statemachine, "AIStateMachine::yield() [" << (void*)this << "]");
|
DoutEntering(dc::statemachine(mSMDebug), "AIStateMachine::yield() [" << (void*)this << "]");
|
||||||
multiplex_state_type_rat state_r(mState);
|
multiplex_state_type_rat state_r(mState);
|
||||||
// yield() may only be called from multiplex_impl().
|
// yield() may only be called from multiplex_impl().
|
||||||
llassert(state_r->base_state == bs_multiplex);
|
llassert(state_r->base_state == bs_multiplex);
|
||||||
@@ -1160,7 +1258,7 @@ void AIStateMachine::yield(void)
|
|||||||
void AIStateMachine::yield(AIEngine* engine)
|
void AIStateMachine::yield(AIEngine* engine)
|
||||||
{
|
{
|
||||||
llassert(engine);
|
llassert(engine);
|
||||||
DoutEntering(dc::statemachine, "AIStateMachine::yield(" << engine->name() << ") [" << (void*)this << "]");
|
DoutEntering(dc::statemachine(mSMDebug), "AIStateMachine::yield(" << engine->name() << ") [" << (void*)this << "]");
|
||||||
#ifdef SHOW_ASSERT
|
#ifdef SHOW_ASSERT
|
||||||
{
|
{
|
||||||
multiplex_state_type_rat state_r(mState);
|
multiplex_state_type_rat state_r(mState);
|
||||||
@@ -1173,9 +1271,19 @@ void AIStateMachine::yield(AIEngine* engine)
|
|||||||
mYieldEngine = engine;
|
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)
|
void AIStateMachine::yield_frame(unsigned int frames)
|
||||||
{
|
{
|
||||||
DoutEntering(dc::statemachine, "AIStateMachine::yield_frame(" << frames << ") [" << (void*)this << "]");
|
DoutEntering(dc::statemachine(mSMDebug), "AIStateMachine::yield_frame(" << frames << ") [" << (void*)this << "]");
|
||||||
mSleep = -(S64)frames;
|
mSleep = -(S64)frames;
|
||||||
// Sleeping is always done from the main thread.
|
// Sleeping is always done from the main thread.
|
||||||
yield(&gMainThreadEngine);
|
yield(&gMainThreadEngine);
|
||||||
@@ -1183,7 +1291,7 @@ void AIStateMachine::yield_frame(unsigned int frames)
|
|||||||
|
|
||||||
void AIStateMachine::yield_ms(unsigned int ms)
|
void AIStateMachine::yield_ms(unsigned int ms)
|
||||||
{
|
{
|
||||||
DoutEntering(dc::statemachine, "AIStateMachine::yield_ms(" << ms << ") [" << (void*)this << "]");
|
DoutEntering(dc::statemachine(mSMDebug), "AIStateMachine::yield_ms(" << ms << ") [" << (void*)this << "]");
|
||||||
mSleep = get_clock_count() + calc_clock_frequency() * ms / 1000;
|
mSleep = get_clock_count() + calc_clock_frequency() * ms / 1000;
|
||||||
// Sleeping is always done from the main thread.
|
// Sleeping is always done from the main thread.
|
||||||
yield(&gMainThreadEngine);
|
yield(&gMainThreadEngine);
|
||||||
@@ -1233,7 +1341,7 @@ void AIEngine::threadloop(void)
|
|||||||
engine_state_type_wat engine_state_w(mEngineState);
|
engine_state_type_wat engine_state_w(mEngineState);
|
||||||
if (!active)
|
if (!active)
|
||||||
{
|
{
|
||||||
Dout(dc::statemachine, "Erasing state machine [" << (void*)&state_machine << "] from " << mName);
|
Dout(dc::statemachine(state_machine.mSMDebug), "Erasing state machine [" << (void*)&state_machine << "] from " << mName);
|
||||||
engine_state_w->list.erase(queued_element++);
|
engine_state_w->list.erase(queued_element++);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
|||||||
@@ -39,6 +39,7 @@
|
|||||||
#include <list>
|
#include <list>
|
||||||
#include <boost/signals2.hpp>
|
#include <boost/signals2.hpp>
|
||||||
|
|
||||||
|
class AIConditionBase;
|
||||||
class AIStateMachine;
|
class AIStateMachine;
|
||||||
|
|
||||||
class AIEngine
|
class AIEngine
|
||||||
@@ -132,6 +133,7 @@ class AIStateMachine : public LLThreadSafeRefCount
|
|||||||
struct sub_state_type {
|
struct sub_state_type {
|
||||||
state_type run_state;
|
state_type run_state;
|
||||||
state_type advance_state;
|
state_type advance_state;
|
||||||
|
AIConditionBase* blocked;
|
||||||
bool reset;
|
bool reset;
|
||||||
bool need_run;
|
bool need_run;
|
||||||
bool idle;
|
bool idle;
|
||||||
@@ -195,20 +197,36 @@ class AIStateMachine : public LLThreadSafeRefCount
|
|||||||
bool mDebugAdvanceStatePending; // True while advance_state() was called by not handled yet.
|
bool mDebugAdvanceStatePending; // True while advance_state() was called by not handled yet.
|
||||||
bool mDebugRefCalled; // True when ref() is called (or will be called within the critial area of mMultiplexMutex).
|
bool mDebugRefCalled; // True when ref() is called (or will be called within the critial area of mMultiplexMutex).
|
||||||
#endif
|
#endif
|
||||||
|
#ifdef CWDEBUG
|
||||||
|
protected:
|
||||||
|
bool mSMDebug; // Print debug output only when true.
|
||||||
|
#endif
|
||||||
|
private:
|
||||||
U64 mRuntime; // Total time spent running in the main thread (in clocks).
|
U64 mRuntime; // Total time spent running in the main thread (in clocks).
|
||||||
|
|
||||||
public:
|
public:
|
||||||
AIStateMachine(void) : mCallback(NULL), mDefaultEngine(NULL), mYieldEngine(NULL),
|
AIStateMachine(CWD_ONLY(bool debug)) : mCallback(NULL), mDefaultEngine(NULL), mYieldEngine(NULL),
|
||||||
#ifdef SHOW_ASSERT
|
#ifdef SHOW_ASSERT
|
||||||
mThreadId(AIThreadID::none), mDebugLastState(bs_killed), mDebugShouldRun(false), mDebugAborted(false), mDebugContPending(false),
|
mThreadId(AIThreadID::none), mDebugLastState(bs_killed), mDebugShouldRun(false), mDebugAborted(false), mDebugContPending(false),
|
||||||
mDebugSetStatePending(false), mDebugAdvanceStatePending(false), mDebugRefCalled(false),
|
mDebugSetStatePending(false), mDebugAdvanceStatePending(false), mDebugRefCalled(false),
|
||||||
|
#endif
|
||||||
|
#ifdef CWDEBUG
|
||||||
|
mSMDebug(debug),
|
||||||
#endif
|
#endif
|
||||||
mRuntime(0)
|
mRuntime(0)
|
||||||
{ }
|
{ }
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
// 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.
|
// The user should call finish() (or abort(), or kill() from the call back when finish_impl() calls run()),
|
||||||
virtual ~AIStateMachine() { llassert(multiplex_state_type_rat(mState)->base_state == bs_killed); }
|
// not delete a class derived from AIStateMachine directly. Deleting it directly before calling run() is
|
||||||
|
// ok however.
|
||||||
|
virtual ~AIStateMachine()
|
||||||
|
{
|
||||||
|
#ifdef SHOW_ASSERT
|
||||||
|
base_state_type state = multiplex_state_type_rat(mState)->base_state;
|
||||||
|
llassert(state == bs_killed || state == bs_reset);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
public:
|
public:
|
||||||
// These functions may be called directly after creation, or from within finish_impl(), or from the call back function.
|
// These functions may be called directly after creation, or from within finish_impl(), or from the call back function.
|
||||||
@@ -224,11 +242,13 @@ class AIStateMachine : public LLThreadSafeRefCount
|
|||||||
void set_state(state_type new_state); // Run this state the NEXT loop.
|
void set_state(state_type new_state); // Run this state the NEXT loop.
|
||||||
// These functions can only be called from within multiplex_impl().
|
// These functions can only be called from within multiplex_impl().
|
||||||
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 idle(void); // Go idle unless cont() or advance_state() were called since the start of the current loop, or until they are called.
|
||||||
|
void wait(AIConditionBase& condition); // The same as idle(), but wake up when AICondition<T>::signal() is called.
|
||||||
void finish(void); // Mark that the state machine finished and schedule the call back.
|
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.
|
void yield(void); // Yield to give CPU to other state machines, but do not go idle.
|
||||||
void yield(AIEngine* engine); // Yield to give CPU to other state machines, but do not go idle. Continue running from engine 'engine'.
|
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.
|
void yield_frame(unsigned int frames); // Run from the main-thread engine after at least 'frames' frames have passed.
|
||||||
void yield_ms(unsigned int ms); // Run from the main-thread engine after roughly 'ms' miliseconds have passed.
|
void yield_ms(unsigned int ms); // Run from the main-thread engine after roughly 'ms' miliseconds have passed.
|
||||||
|
bool yield_if_not(AIEngine* engine); // Do not really yield, unless the current engine is not 'engine'. Returns true if it switched engine.
|
||||||
|
|
||||||
public:
|
public:
|
||||||
// This function can be called from multiplex_imp(), but also by a child state machine and
|
// This function can be called from multiplex_imp(), but also by a child state machine and
|
||||||
@@ -236,11 +256,12 @@ class AIStateMachine : public LLThreadSafeRefCount
|
|||||||
// to access this state machine.
|
// to access this state machine.
|
||||||
void abort(void); // Abort the state machine (unsuccessful finish).
|
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.
|
// These are the only three functions that can be called by any thread at any moment.
|
||||||
// Those threads should use an LLPointer<AIStateMachine> to access this state machine.
|
// 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 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
|
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.
|
// iff new_state is larger than the last state that was processed.
|
||||||
|
bool signalled(void); // Call cont() iff this state machine is still blocked after a call to wait(). Returns false if it already unblocked.
|
||||||
|
|
||||||
public:
|
public:
|
||||||
// Accessors.
|
// Accessors.
|
||||||
|
|||||||
@@ -181,7 +181,11 @@ class AIStateMachineThreadBase : public AIStateMachine {
|
|||||||
static state_type const max_state = wait_stopped + 1;
|
static state_type const max_state = wait_stopped + 1;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
AIStateMachineThreadBase(void) { }
|
AIStateMachineThreadBase(CWD_ONLY(bool debug))
|
||||||
|
#ifdef CWDEBUG
|
||||||
|
: AIStateMachine(debug)
|
||||||
|
#endif
|
||||||
|
{ }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
// Handle initializing the object.
|
// Handle initializing the object.
|
||||||
@@ -217,7 +221,10 @@ class AIStateMachineThread : public AIStateMachineThreadBase {
|
|||||||
|
|
||||||
public:
|
public:
|
||||||
// Constructor.
|
// Constructor.
|
||||||
AIStateMachineThread(void)
|
AIStateMachineThread(CWD_ONLY(bool debug))
|
||||||
|
#ifdef CWDEBUG
|
||||||
|
: AIStateMachineThreadBase(debug)
|
||||||
|
#endif
|
||||||
{
|
{
|
||||||
*AIThreadImpl::StateMachineThread_wat(mThreadImpl.mStateMachineThread) = this;
|
*AIThreadImpl::StateMachineThread_wat(mThreadImpl.mStateMachineThread) = this;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -76,7 +76,11 @@ class AITimer : public AIStateMachine {
|
|||||||
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.
|
||||||
|
|
||||||
public:
|
public:
|
||||||
AITimer(void) : mInterval(0) { DoutEntering(dc::statemachine, "AITimer(void) [" << (void*)this << "]"); }
|
AITimer(CWD_ONLY(bool debug = false)) :
|
||||||
|
#ifdef CWDEBUG
|
||||||
|
AIStateMachine(debug),
|
||||||
|
#endif
|
||||||
|
mInterval(0) { DoutEntering(dc::statemachine(mSMDebug), "AITimer(void) [" << (void*)this << "]"); }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Set the interval after which the timer should expire.
|
* @brief Set the interval after which the timer should expire.
|
||||||
@@ -96,7 +100,7 @@ class AITimer : public AIStateMachine {
|
|||||||
|
|
||||||
protected:
|
protected:
|
||||||
// Call finish() (or abort()), not delete.
|
// Call finish() (or abort()), not delete.
|
||||||
/*virtual*/ ~AITimer() { DoutEntering(dc::statemachine, "~AITimer() [" << (void*)this << "]"); mFrameTimer.cancel(); }
|
/*virtual*/ ~AITimer() { DoutEntering(dc::statemachine(mSMDebug), "~AITimer() [" << (void*)this << "]"); mFrameTimer.cancel(); }
|
||||||
|
|
||||||
// Handle initializing the object.
|
// Handle initializing the object.
|
||||||
/*virtual*/ void initialize_impl(void);
|
/*virtual*/ void initialize_impl(void);
|
||||||
|
|||||||
@@ -144,6 +144,7 @@ extern LL_COMMON_API fake_channel const snapshot;
|
|||||||
#define CWDEBUG_MARKER 0
|
#define CWDEBUG_MARKER 0
|
||||||
|
|
||||||
#define BACKTRACE do { } while(0)
|
#define BACKTRACE do { } while(0)
|
||||||
|
#define CWD_ONLY(...)
|
||||||
|
|
||||||
#endif // !DOXYGEN
|
#endif // !DOXYGEN
|
||||||
|
|
||||||
@@ -180,6 +181,7 @@ extern LL_COMMON_API fake_channel const snapshot;
|
|||||||
#include <set>
|
#include <set>
|
||||||
|
|
||||||
#define CWD_API __attribute__ ((visibility("default")))
|
#define CWD_API __attribute__ ((visibility("default")))
|
||||||
|
#define CWD_ONLY(...) __VA_ARGS__
|
||||||
|
|
||||||
//! Debug specific code.
|
//! Debug specific code.
|
||||||
namespace debug {
|
namespace debug {
|
||||||
|
|||||||
@@ -250,10 +250,13 @@ void AICurlEasyRequestStateMachine::finish_impl(void)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
AICurlEasyRequestStateMachine::AICurlEasyRequestStateMachine(void) :
|
AICurlEasyRequestStateMachine::AICurlEasyRequestStateMachine(CWD_ONLY(bool debug)) :
|
||||||
|
#ifdef CWDEBUG
|
||||||
|
AIStateMachine(debug),
|
||||||
|
#endif
|
||||||
mTotalDelayTimeout(AIHTTPTimeoutPolicy::getDebugSettingsCurlTimeout().getTotalDelay())
|
mTotalDelayTimeout(AIHTTPTimeoutPolicy::getDebugSettingsCurlTimeout().getTotalDelay())
|
||||||
{
|
{
|
||||||
Dout(dc::statemachine, "Calling AICurlEasyRequestStateMachine(void) [" << (void*)this << "] [" << (void*)mCurlEasyRequest.get() << "]");
|
Dout(dc::statemachine(mSMDebug), "Calling AICurlEasyRequestStateMachine(void) [" << (void*)this << "] [" << (void*)mCurlEasyRequest.get() << "]");
|
||||||
AICurlInterface::Stats::AICurlEasyRequestStateMachine_count++;
|
AICurlInterface::Stats::AICurlEasyRequestStateMachine_count++;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -264,7 +267,7 @@ void AICurlEasyRequestStateMachine::setTotalDelayTimeout(F32 totalDelayTimeout)
|
|||||||
|
|
||||||
AICurlEasyRequestStateMachine::~AICurlEasyRequestStateMachine()
|
AICurlEasyRequestStateMachine::~AICurlEasyRequestStateMachine()
|
||||||
{
|
{
|
||||||
Dout(dc::statemachine, "Calling ~AICurlEasyRequestStateMachine() [" << (void*)this << "] [" << (void*)mCurlEasyRequest.get() << "]");
|
Dout(dc::statemachine(mSMDebug), "Calling ~AICurlEasyRequestStateMachine() [" << (void*)this << "] [" << (void*)mCurlEasyRequest.get() << "]");
|
||||||
--AICurlInterface::Stats::AICurlEasyRequestStateMachine_count;
|
--AICurlInterface::Stats::AICurlEasyRequestStateMachine_count;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -52,7 +52,7 @@
|
|||||||
// Construction of a AICurlEasyRequestStateMachine might throw AICurlNoEasyHandle.
|
// Construction of a AICurlEasyRequestStateMachine might throw AICurlNoEasyHandle.
|
||||||
class AICurlEasyRequestStateMachine : public AIStateMachine, public AICurlEasyHandleEvents {
|
class AICurlEasyRequestStateMachine : public AIStateMachine, public AICurlEasyHandleEvents {
|
||||||
public:
|
public:
|
||||||
AICurlEasyRequestStateMachine(void);
|
AICurlEasyRequestStateMachine(CWD_ONLY(bool debug = false));
|
||||||
|
|
||||||
// Transparent access.
|
// Transparent access.
|
||||||
AICurlEasyRequest mCurlEasyRequest;
|
AICurlEasyRequest mCurlEasyRequest;
|
||||||
|
|||||||
@@ -1414,7 +1414,10 @@ void LLMeshUploadThread::preStart()
|
|||||||
|
|
||||||
AIMeshUpload::AIMeshUpload(LLMeshUploadThread::instance_list& data, LLVector3& scale, bool upload_textures, bool upload_skin, bool upload_joints, std::string const& upload_url, bool do_upload,
|
AIMeshUpload::AIMeshUpload(LLMeshUploadThread::instance_list& data, LLVector3& scale, bool upload_textures, bool upload_skin, bool upload_joints, std::string const& upload_url, bool do_upload,
|
||||||
LLHandle<LLWholeModelFeeObserver> const& fee_observer, LLHandle<LLWholeModelUploadObserver> const& upload_observer) :
|
LLHandle<LLWholeModelFeeObserver> const& fee_observer, LLHandle<LLWholeModelUploadObserver> const& upload_observer) :
|
||||||
mMeshUpload(new AIStateMachineThread<LLMeshUploadThread>), mWholeModelUploadURL(upload_url)
|
#ifdef CWDEBUG
|
||||||
|
AIStateMachine(false),
|
||||||
|
#endif
|
||||||
|
mMeshUpload(new AIStateMachineThread<LLMeshUploadThread>(CWD_ONLY(false))), mWholeModelUploadURL(upload_url)
|
||||||
{
|
{
|
||||||
mMeshUpload->thread_impl().init(data, scale, upload_textures, upload_skin, upload_joints, do_upload, fee_observer, upload_observer);
|
mMeshUpload->thread_impl().init(data, scale, upload_textures, upload_skin, upload_joints, do_upload, fee_observer, upload_observer);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -155,7 +155,7 @@ void AIFetchInventoryFolder::multiplex_impl(state_type run_state)
|
|||||||
// Create the folder.
|
// Create the folder.
|
||||||
mFolderUUID = gInventory.createNewCategory(mParentFolder, LLFolderType::FT_NONE, mFolderName);
|
mFolderUUID = gInventory.createNewCategory(mParentFolder, LLFolderType::FT_NONE, mFolderName);
|
||||||
llassert_always(!mFolderUUID.isNull());
|
llassert_always(!mFolderUUID.isNull());
|
||||||
Dout(dc::statemachine, "Created folder \"" << mFolderName << "\".");
|
Dout(dc::statemachine(mSMDebug), "Created folder \"" << mFolderName << "\".");
|
||||||
mNeedNotifyObservers = true;
|
mNeedNotifyObservers = true;
|
||||||
}
|
}
|
||||||
mCreated = true;
|
mCreated = true;
|
||||||
|
|||||||
@@ -58,8 +58,12 @@ class AIFetchInventoryFolder : public AIStateMachine {
|
|||||||
bool mNeedNotifyObservers;
|
bool mNeedNotifyObservers;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
AIFetchInventoryFolder(void) : mCreate(false), mFetchContents(false), mExists(false), mCreated(false)
|
AIFetchInventoryFolder(CWD_ONLY(bool debug = false)) :
|
||||||
{ Dout(dc::statemachine, "Calling AIFetchInventoryFolder constructor [" << (void*)this << "]"); }
|
#ifdef CWDEBUG
|
||||||
|
AIStateMachine(debug),
|
||||||
|
#endif
|
||||||
|
mCreate(false), mFetchContents(false), mExists(false), mCreated(false)
|
||||||
|
{ Dout(dc::statemachine(mSMDebug), "Calling AIFetchInventoryFolder constructor [" << (void*)this << "]"); }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Fetch an inventory folder by name, optionally creating it.
|
* @brief Fetch an inventory folder by name, optionally creating it.
|
||||||
@@ -132,7 +136,7 @@ class AIFetchInventoryFolder : public AIStateMachine {
|
|||||||
|
|
||||||
protected:
|
protected:
|
||||||
// Call finish() (or abort()), not delete.
|
// Call finish() (or abort()), not delete.
|
||||||
/*virtual*/ ~AIFetchInventoryFolder() { Dout(dc::statemachine, "Calling ~AIFetchInventoryFolder() [" << (void*)this << "]"); }
|
/*virtual*/ ~AIFetchInventoryFolder() { Dout(dc::statemachine(mSMDebug), "Calling ~AIFetchInventoryFolder() [" << (void*)this << "]"); }
|
||||||
|
|
||||||
// Handle initializing the object.
|
// Handle initializing the object.
|
||||||
/*virtual*/ void initialize_impl(void);
|
/*virtual*/ void initialize_impl(void);
|
||||||
|
|||||||
@@ -60,7 +60,11 @@ char const* AIFilePicker::state_str_impl(state_type run_state) const
|
|||||||
return "UNKNOWN STATE";
|
return "UNKNOWN STATE";
|
||||||
}
|
}
|
||||||
|
|
||||||
AIFilePicker::AIFilePicker(void) : mPluginManager(NULL), mCanceled(false)
|
AIFilePicker::AIFilePicker(CWD_ONLY(bool debug)) :
|
||||||
|
#ifdef CWDEBUG
|
||||||
|
AIStateMachine(debug),
|
||||||
|
#endif
|
||||||
|
mPluginManager(NULL), mCanceled(false)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -168,7 +168,7 @@ public:
|
|||||||
|
|
||||||
public:
|
public:
|
||||||
// The derived class must have a default constructor.
|
// The derived class must have a default constructor.
|
||||||
AIFilePicker(void);
|
AIFilePicker(CWD_ONLY(bool debug = false));
|
||||||
|
|
||||||
// Create a dynamically created AIFilePicker object.
|
// Create a dynamically created AIFilePicker object.
|
||||||
static AIFilePicker* create(void) { AIFilePicker* filepicker = new AIFilePicker; return filepicker; }
|
static AIFilePicker* create(void) { AIFilePicker* filepicker = new AIFilePicker; return filepicker; }
|
||||||
|
|||||||
Reference in New Issue
Block a user