From 8a44c8fce1512004a00d8fd8ee3d80ba27156983 Mon Sep 17 00:00:00 2001 From: Siana Gearz Date: Mon, 10 Sep 2012 02:06:07 +0200 Subject: [PATCH] Fast timer fix-up The logic is incorrect in principle, but at least it's better behaved now. Also, get fast timer out of important parts of aistatemachine. --- indra/llcommon/llfasttimer_class.cpp | 24 +- indra/llcommon/llfasttimer_class.h | 17 +- indra/newview/statemachine/aistatemachine.cpp | 622 +++++++++--------- indra/newview/statemachine/aistatemachine.h | 4 +- 4 files changed, 334 insertions(+), 333 deletions(-) diff --git a/indra/llcommon/llfasttimer_class.cpp b/indra/llcommon/llfasttimer_class.cpp index 5fb9fd580..07ea31a5a 100644 --- a/indra/llcommon/llfasttimer_class.cpp +++ b/indra/llcommon/llfasttimer_class.cpp @@ -182,6 +182,7 @@ std::string LLFastTimer::sLogName = ""; BOOL LLFastTimer::sMetricLog = FALSE; LLMutex* LLFastTimer::sLogLock = NULL; std::queue LLFastTimer::sLogQueue; +const int LLFastTimer::NamedTimer::HISTORY_NUM = 300; #if LL_WINDOWS #define USE_RDTSC 1 @@ -435,16 +436,12 @@ LLFastTimer::NamedTimer::NamedTimer(const std::string& name) mFrameStateIndex = frame_state_list.size(); getFrameStateList().push_back(FrameState(this)); - mCountHistory = new U32[HISTORY_NUM]; - memset(mCountHistory, 0, sizeof(U32) * HISTORY_NUM); - mCallHistory = new U32[HISTORY_NUM]; - memset(mCallHistory, 0, sizeof(U32) * HISTORY_NUM); + mCountHistory.resize(HISTORY_NUM); + mCallHistory.resize(HISTORY_NUM); } LLFastTimer::NamedTimer::~NamedTimer() { - delete[] mCountHistory; - delete[] mCallHistory; } std::string LLFastTimer::NamedTimer::getToolTip(S32 history_idx) @@ -637,10 +634,12 @@ void LLFastTimer::NamedTimer::accumulateTimings() // update timer history int hidx = cur_frame % HISTORY_NUM; + int weight = llmin(100, cur_frame); + timerp->mCountHistory[hidx] = timerp->mTotalTimeCounter; - timerp->mCountAverage = ((U64)timerp->mCountAverage * cur_frame + timerp->mTotalTimeCounter) / (cur_frame+1); + timerp->mCountAverage = ((F64)timerp->mCountAverage * weight + (F64)timerp->mTotalTimeCounter) / (weight+1); timerp->mCallHistory[hidx] = timerp->getFrameState().mCalls; - timerp->mCallAverage = ((U64)timerp->mCallAverage * cur_frame + timerp->getFrameState().mCalls) / (cur_frame+1); + timerp->mCallAverage = ((F64)timerp->mCallAverage * weight + (F64)timerp->getFrameState().mCalls) / (weight+1); } } } @@ -776,8 +775,10 @@ void LLFastTimer::NamedTimer::reset() timer.mCountAverage = 0; timer.mCallAverage = 0; - memset(timer.mCountHistory, 0, sizeof(U32) * HISTORY_NUM); - memset(timer.mCallHistory, 0, sizeof(U32) * HISTORY_NUM); + timer.mCountHistory.clear(); + timer.mCountHistory.resize(HISTORY_NUM); + timer.mCallHistory.clear(); + timer.mCallHistory.resize(HISTORY_NUM); } } @@ -856,7 +857,8 @@ void LLFastTimer::nextFrame() if (!sPauseHistory) { NamedTimer::processTimes(); - sLastFrameIndex = sCurFrameIndex++; + sLastFrameIndex = sCurFrameIndex; + ++sCurFrameIndex; } // get ready for next frame diff --git a/indra/llcommon/llfasttimer_class.h b/indra/llcommon/llfasttimer_class.h index c6a6f59c1..aa1f8ffac 100644 --- a/indra/llcommon/llfasttimer_class.h +++ b/indra/llcommon/llfasttimer_class.h @@ -66,7 +66,7 @@ public: public: ~NamedTimer(); - enum { HISTORY_NUM = 300 }; + static const int HISTORY_NUM; const std::string& getName() const { return mName; } NamedTimer* getParent() const { return mParent; } @@ -82,8 +82,8 @@ public: void setCollapsed(bool collapsed) { mCollapsed = collapsed; } bool getCollapsed() const { return mCollapsed; } - U32 getCountAverage() const; //{ return mCountAverage } - U32 getCallAverage() const; //{ return mCallAverage } + U32 getCountAverage() const; + U32 getCallAverage() const; U32 getHistoricalCount(S32 history_index = 0) const; U32 getHistoricalCalls(S32 history_index = 0) const; @@ -122,11 +122,11 @@ public: U32 mTotalTimeCounter; - U32 mCountAverage; - U32 mCallAverage; + F64 mCountAverage; + F64 mCallAverage; - U32* mCountHistory; - U32* mCallHistory; + std::vector mCountHistory; + std::vector mCallHistory; // tree structure NamedTimer* mParent; // NamedTimer of caller(parent) @@ -258,11 +258,10 @@ public: static CurTimerData sCurTimerData; static std::string sClockType; -public: +private: static U32 getCPUClockCount32(); static U64 getCPUClockCount64(); -private: static S32 sCurFrameIndex; static S32 sLastFrameIndex; static U64 sLastFrameTime; diff --git a/indra/newview/statemachine/aistatemachine.cpp b/indra/newview/statemachine/aistatemachine.cpp index badcdf5f5..3cd5c056c 100644 --- a/indra/newview/statemachine/aistatemachine.cpp +++ b/indra/newview/statemachine/aistatemachine.cpp @@ -44,35 +44,35 @@ extern LLControlGroup gSavedSettings; // Local variables. namespace { - struct QueueElementComp; + struct QueueElementComp; - class QueueElement { - private: - AIStateMachine* mStateMachine; - U64 mRuntime; + class QueueElement { + private: + AIStateMachine* mStateMachine; + U64 mRuntime; - public: - QueueElement(AIStateMachine* statemachine) : mStateMachine(statemachine), mRuntime(0) { } - friend bool operator==(QueueElement const& e1, QueueElement const& e2) { return e1.mStateMachine == e2.mStateMachine; } - friend struct QueueElementComp; + public: + QueueElement(AIStateMachine* statemachine) : mStateMachine(statemachine), mRuntime(0) { } + friend bool operator==(QueueElement const& e1, QueueElement const& e2) { return e1.mStateMachine == e2.mStateMachine; } + friend struct QueueElementComp; - AIStateMachine& statemachine(void) const { return *mStateMachine; } - void add(U64 count) { mRuntime += count; } - }; + AIStateMachine& statemachine(void) const { return *mStateMachine; } + void add(U64 count) { mRuntime += count; } + }; - struct QueueElementComp { - bool operator()(QueueElement const& e1, QueueElement const& e2) const { return e1.mRuntime < e2.mRuntime; } - }; + struct QueueElementComp { + bool operator()(QueueElement const& e1, QueueElement const& e2) const { return e1.mRuntime < e2.mRuntime; } + }; - typedef std::vector active_statemachines_type; - active_statemachines_type active_statemachines; - typedef std::vector continued_statemachines_type; - struct cscm_type - { - continued_statemachines_type continued_statemachines; - bool calling_mainloop; - }; - AIThreadSafeDC continued_statemachines_and_calling_mainloop; + typedef std::vector active_statemachines_type; + active_statemachines_type active_statemachines; + typedef std::vector continued_statemachines_type; + struct cscm_type + { + continued_statemachines_type continued_statemachines; + bool calling_mainloop; + }; + AIThreadSafeDC continued_statemachines_and_calling_mainloop; } // static @@ -80,8 +80,8 @@ AIThreadSafeSimpleDC AIStateMachine::sMaxCount; void AIStateMachine::updateSettings(void) { - Dout(dc::statemachine, "Initializing AIStateMachine::sMaxCount"); - *AIAccess(sMaxCount) = calc_clock_frequency() * gSavedSettings.getU32("StateMachineMaxTime") / 1000; + Dout(dc::statemachine, "Initializing AIStateMachine::sMaxCount"); + *AIAccess(sMaxCount) = calc_clock_frequency() * gSavedSettings.getU32("StateMachineMaxTime") / 1000; } //---------------------------------------------------------------------------- @@ -91,70 +91,70 @@ void AIStateMachine::updateSettings(void) void AIStateMachine::run(AIStateMachine* parent, state_type new_parent_state, bool abort_parent) { - DoutEntering(dc::statemachine, "AIStateMachine::run(" << (void*)parent << ", " << (parent ? parent->state_str(new_parent_state) : "NA") << ", " << abort_parent << ") [" << (void*)this << "]"); - // Must be the first time we're being run, or we must be called from a callback function. - llassert(!mParent || mState == bs_callback); - llassert(!mCallback || mState == bs_callback); - // Can only be run when in this state. - llassert(mState == bs_initialize || mState == bs_callback); + DoutEntering(dc::statemachine, "AIStateMachine::run(" << (void*)parent << ", " << (parent ? parent->state_str(new_parent_state) : "NA") << ", " << abort_parent << ") [" << (void*)this << "]"); + // Must be the first time we're being run, or we must be called from a callback function. + llassert(!mParent || mState == bs_callback); + llassert(!mCallback || mState == bs_callback); + // Can only be run when in this state. + llassert(mState == bs_initialize || mState == bs_callback); - // Allow NULL to be passed as parent to signal that we want to reuse the old one. - if (parent) - { - mParent = parent; - // In that case remove any old callback! - if (mCallback) - { - delete mCallback; - mCallback = NULL; - } + // Allow NULL to be passed as parent to signal that we want to reuse the old one. + if (parent) + { + mParent = parent; + // In that case remove any old callback! + if (mCallback) + { + delete mCallback; + mCallback = NULL; + } - mNewParentState = new_parent_state; - mAbortParent = abort_parent; - } + mNewParentState = new_parent_state; + mAbortParent = abort_parent; + } - // If abort_parent is requested then a parent must be provided. - llassert(!abort_parent || mParent); - // If a parent is provided, it must be running. - llassert(!mParent || mParent->mState == bs_run); + // If abort_parent is requested then a parent must be provided. + llassert(!abort_parent || mParent); + // If a parent is provided, it must be running. + llassert(!mParent || mParent->mState == bs_run); - // Mark that run() has been called, in case we're being called from a callback function. - mState = bs_initialize; + // Mark that run() has been called, in case we're being called from a callback function. + mState = bs_initialize; - cont(); + cont(); } void AIStateMachine::run(callback_type::signal_type::slot_type const& slot) { - DoutEntering(dc::statemachine, "AIStateMachine::run() [" << (void*)this << "]"); - // Must be the first time we're being run, or we must be called from a callback function. - llassert(!mParent || mState == bs_callback); - llassert(!mCallback || mState == bs_callback); - // Can only be run when in this state. - llassert(mState == bs_initialize || mState == bs_callback); + DoutEntering(dc::statemachine, "AIStateMachine::run() [" << (void*)this << "]"); + // Must be the first time we're being run, or we must be called from a callback function. + llassert(!mParent || mState == bs_callback); + llassert(!mCallback || mState == bs_callback); + // Can only be run when in this state. + llassert(mState == bs_initialize || mState == bs_callback); - // Clean up any old callbacks. - mParent = NULL; - if (mCallback) - { - delete mCallback; - mCallback = NULL; - } + // Clean up any old callbacks. + mParent = NULL; + if (mCallback) + { + delete mCallback; + mCallback = NULL; + } - mCallback = new callback_type(slot); + mCallback = new callback_type(slot); - // Mark that run() has been called, in case we're being called from a callback function. - mState = bs_initialize; + // Mark that run() has been called, in case we're being called from a callback function. + mState = bs_initialize; - cont(); + cont(); } void AIStateMachine::idle(void) { - DoutEntering(dc::statemachine, "AIStateMachine::idle() [" << (void*)this << "]"); - llassert(!mIdle); - mIdle = true; - mSleep = 0; + DoutEntering(dc::statemachine, "AIStateMachine::idle() [" << (void*)this << "]"); + llassert(!mIdle); + mIdle = true; + mSleep = 0; } // About thread safeness: @@ -168,149 +168,149 @@ void AIStateMachine::idle(void) void AIStateMachine::cont(void) { - DoutEntering(dc::statemachine, "AIStateMachine::cont() [" << (void*)this << "]"); - llassert(mIdle); - // Atomic test mActive and change mIdle. - mIdleActive.lock(); - mIdle = false; - bool not_active = mActive == as_idle; - mIdleActive.unlock(); - if (not_active) - { - AIWriteAccess cscm_w(continued_statemachines_and_calling_mainloop); - // We only get here when the statemachine was idle (set by the main thread), - // see first assertion. Hence, the main thread is not changing this, as the - // statemachine is not running. Thus, mActive can have changed when a THIRD - // thread called cont(), which is not allowed: if two threads can call cont() - // at any moment then the first assertion can't hold. - llassert_always(mActive == as_idle); - cscm_w->continued_statemachines.push_back(this); - if (!cscm_w->calling_mainloop) - { - Dout(dc::statemachine, "Adding AIStateMachine::mainloop to gIdleCallbacks"); - cscm_w->calling_mainloop = true; - gIdleCallbacks.addFunction(&AIStateMachine::mainloop); - } - mActive = as_queued; - llassert_always(!mIdle); // It should never happen that one thread calls cont() while another calls idle() concurrently. - } + DoutEntering(dc::statemachine, "AIStateMachine::cont() [" << (void*)this << "]"); + llassert(mIdle); + // Atomic test mActive and change mIdle. + mIdleActive.lock(); + mIdle = false; + bool not_active = mActive == as_idle; + mIdleActive.unlock(); + if (not_active) + { + AIWriteAccess cscm_w(continued_statemachines_and_calling_mainloop); + // We only get here when the statemachine was idle (set by the main thread), + // see first assertion. Hence, the main thread is not changing this, as the + // statemachine is not running. Thus, mActive can have changed when a THIRD + // thread called cont(), which is not allowed: if two threads can call cont() + // at any moment then the first assertion can't hold. + llassert_always(mActive == as_idle); + cscm_w->continued_statemachines.push_back(this); + if (!cscm_w->calling_mainloop) + { + Dout(dc::statemachine, "Adding AIStateMachine::mainloop to gIdleCallbacks"); + cscm_w->calling_mainloop = true; + gIdleCallbacks.addFunction(&AIStateMachine::mainloop); + } + mActive = as_queued; + llassert_always(!mIdle); // It should never happen that one thread calls cont() while another calls idle() concurrently. + } } void AIStateMachine::set_state(state_type state) { - DoutEntering(dc::statemachine, "AIStateMachine::set_state(" << state_str(state) << ") [" << (void*)this << "]"); - llassert(mState == bs_run); - if (mRunState != state) - { - mRunState = state; - Dout(dc::statemachine, "mRunState set to " << state_str(mRunState)); - } - if (mIdle) - cont(); + DoutEntering(dc::statemachine, "AIStateMachine::set_state(" << state_str(state) << ") [" << (void*)this << "]"); + llassert(mState == bs_run); + if (mRunState != state) + { + mRunState = state; + Dout(dc::statemachine, "mRunState set to " << state_str(mRunState)); + } + if (mIdle) + cont(); } void AIStateMachine::abort(void) { - DoutEntering(dc::statemachine, "AIStateMachine::abort() [" << (void*)this << "]"); - llassert(mState == bs_run); - mState = bs_abort; - abort_impl(); - mAborted = true; - finish(); + DoutEntering(dc::statemachine, "AIStateMachine::abort() [" << (void*)this << "]"); + llassert(mState == bs_run); + mState = bs_abort; + abort_impl(); + mAborted = true; + finish(); } void AIStateMachine::finish(void) { - DoutEntering(dc::statemachine, "AIStateMachine::finish() [" << (void*)this << "]"); - llassert(mState == bs_run || mState == bs_abort); - // It is possible that mIdle is true when abort or finish was called from - // outside multiplex_impl. However, that only may be done by the main thread. - llassert(!mIdle || is_main_thread()); - if (!mIdle) - idle(); - mState = bs_finish; - finish_impl(); - // Did finish_impl call kill()? Then that is only the default. Remember it. - bool default_delete = (mState == bs_killed); - mState = bs_finish; - if (mParent) - { - // It is possible that the parent is not running when the parent is in fact aborting and called - // abort on this object from it's abort_impl function. It that case we don't want to recursively - // call abort again (or change it's state). - if (mParent->running()) - { - if (mAborted && mAbortParent) - { - mParent->abort(); - mParent = NULL; - } - else - { - mParent->set_state(mNewParentState); - } - } - } - // After this (bool)*this evaluates to true and we can call the callback, which then is allowed to call run(). - mState = bs_callback; - if (mCallback) - { - // This can/may call kill() that sets mState to bs_kill and in which case the whole AIStateMachine - // will be deleted from the mainloop, or it may call run() that sets mState is set to bs_initialize - // and might change or reuse mCallback or mParent. - mCallback->callback(!mAborted); - if (mState != bs_initialize) - { - delete mCallback; - mCallback = NULL; - mParent = NULL; - } - } - else - { - // Not restarted by callback. Allow run() to be called later on. - mParent = NULL; - } - // Fix the final state. - if (mState == bs_callback) - mState = default_delete ? bs_killed : bs_initialize; - if (mState == bs_killed && mActive == as_idle) - { - // Bump the statemachine onto the active statemachine list, or else it won't be deleted. - cont(); - idle(); - } + DoutEntering(dc::statemachine, "AIStateMachine::finish() [" << (void*)this << "]"); + llassert(mState == bs_run || mState == bs_abort); + // It is possible that mIdle is true when abort or finish was called from + // outside multiplex_impl. However, that only may be done by the main thread. + llassert(!mIdle || is_main_thread()); + if (!mIdle) + idle(); + mState = bs_finish; + finish_impl(); + // Did finish_impl call kill()? Then that is only the default. Remember it. + bool default_delete = (mState == bs_killed); + mState = bs_finish; + if (mParent) + { + // It is possible that the parent is not running when the parent is in fact aborting and called + // abort on this object from it's abort_impl function. It that case we don't want to recursively + // call abort again (or change it's state). + if (mParent->running()) + { + if (mAborted && mAbortParent) + { + mParent->abort(); + mParent = NULL; + } + else + { + mParent->set_state(mNewParentState); + } + } + } + // After this (bool)*this evaluates to true and we can call the callback, which then is allowed to call run(). + mState = bs_callback; + if (mCallback) + { + // This can/may call kill() that sets mState to bs_kill and in which case the whole AIStateMachine + // will be deleted from the mainloop, or it may call run() that sets mState is set to bs_initialize + // and might change or reuse mCallback or mParent. + mCallback->callback(!mAborted); + if (mState != bs_initialize) + { + delete mCallback; + mCallback = NULL; + mParent = NULL; + } + } + else + { + // Not restarted by callback. Allow run() to be called later on. + mParent = NULL; + } + // Fix the final state. + if (mState == bs_callback) + mState = default_delete ? bs_killed : bs_initialize; + if (mState == bs_killed && mActive == as_idle) + { + // Bump the statemachine onto the active statemachine list, or else it won't be deleted. + cont(); + idle(); + } } void AIStateMachine::kill(void) { - // Should only be called from finish() (or when not running (bs_initialize)). - llassert(mIdle && (mState == bs_callback || mState == bs_finish || mState == bs_initialize)); - base_state_type prev_state = mState; - mState = bs_killed; - if (prev_state == bs_initialize) - { - // We're not running (ie being deleted by a parent statemachine), delete it immediately. - delete this; - } + // Should only be called from finish() (or when not running (bs_initialize)). + llassert(mIdle && (mState == bs_callback || mState == bs_finish || mState == bs_initialize)); + base_state_type prev_state = mState; + mState = bs_killed; + if (prev_state == bs_initialize) + { + // We're not running (ie being deleted by a parent statemachine), delete it immediately. + delete this; + } } // Return stringified 'state'. char const* AIStateMachine::state_str(state_type state) { - if (state >= min_state && state < max_state) - { - switch (state) - { - AI_CASE_RETURN(bs_initialize); - AI_CASE_RETURN(bs_run); - AI_CASE_RETURN(bs_abort); - AI_CASE_RETURN(bs_finish); - AI_CASE_RETURN(bs_callback); - AI_CASE_RETURN(bs_killed); - } - } - return state_str_impl(state); + if (state >= min_state && state < max_state) + { + switch (state) + { + AI_CASE_RETURN(bs_initialize); + AI_CASE_RETURN(bs_run); + AI_CASE_RETURN(bs_abort); + AI_CASE_RETURN(bs_finish); + AI_CASE_RETURN(bs_callback); + AI_CASE_RETURN(bs_killed); + } + } + return state_str_impl(state); } //---------------------------------------------------------------------------- @@ -320,135 +320,135 @@ char const* AIStateMachine::state_str(state_type state) void AIStateMachine::multiplex(U64 current_time) { - // Return immediately when this state machine is sleeping. - // A negative value of mSleep means we're counting frames, - // a positive value means we're waiting till a certain - // amount of time has passed. - if (mSleep != 0) - { - if (mSleep < 0) - { - if (++mSleep) - return; - } - else - { - if (current_time < (U64)mSleep) - return; - mSleep = 0; - } - } + // Return immediately when this state machine is sleeping. + // A negative value of mSleep means we're counting frames, + // a positive value means we're waiting till a certain + // amount of time has passed. + if (mSleep != 0) + { + if (mSleep < 0) + { + if (++mSleep) + return; + } + else + { + if (current_time < (U64)mSleep) + return; + mSleep = 0; + } + } - DoutEntering(dc::statemachine, "AIStateMachine::multiplex() [" << (void*)this << "] [with state: " << state_str(mState == bs_run ? mRunState : mState) << "]"); - llassert(mState == bs_initialize || mState == bs_run); + DoutEntering(dc::statemachine, "AIStateMachine::multiplex() [" << (void*)this << "] [with state: " << state_str(mState == bs_run ? mRunState : mState) << "]"); + llassert(mState == bs_initialize || mState == bs_run); - // Real state machine starts here. - if (mState == bs_initialize) - { - mAborted = false; - mState = bs_run; - initialize_impl(); - if (mAborted || mState != bs_run) - return; - } - multiplex_impl(); + // Real state machine starts here. + if (mState == bs_initialize) + { + mAborted = false; + mState = bs_run; + initialize_impl(); + if (mAborted || mState != bs_run) + return; + } + multiplex_impl(); } static LLFastTimer::DeclareTimer FTM_STATEMACHINE("State Machine"); // static void AIStateMachine::mainloop(void*) { - LLFastTimer t(FTM_STATEMACHINE); - // Add continued state machines. - { - AIReadAccess cscm_r(continued_statemachines_and_calling_mainloop); - bool nonempty = false; - for (continued_statemachines_type::const_iterator iter = cscm_r->continued_statemachines.begin(); iter != cscm_r->continued_statemachines.end(); ++iter) - { - nonempty = true; - active_statemachines.push_back(QueueElement(*iter)); - Dout(dc::statemachine, "Adding " << (void*)*iter << " to active_statemachines"); - (*iter)->mActive = as_active; - } - if (nonempty) - AIWriteAccess(cscm_r)->continued_statemachines.clear(); - } - llassert(!active_statemachines.empty()); - // Run one or more state machines. - U64 total_clocks = 0; - U64 max_count = *AIAccess(sMaxCount); - for (active_statemachines_type::iterator iter = active_statemachines.begin(); iter != active_statemachines.end(); ++iter) - { - AIStateMachine& statemachine(iter->statemachine()); - if (!statemachine.mIdle) - { - U64 start = LLFastTimer::getCPUClockCount64(); - // This might call idle() and then pass the statemachine to another thread who then may call cont(). - // Hence, after this isn't not sure what mIdle is, and it can change from true to false at any moment, - // if it is true after this function returns. - iter->statemachine().multiplex(start); - U64 delta = LLFastTimer::getCPUClockCount64() - start; - iter->add(delta); - total_clocks += delta; - if (total_clocks >= max_count) - { + LLFastTimer t(FTM_STATEMACHINE); + // Add continued state machines. + { + AIReadAccess cscm_r(continued_statemachines_and_calling_mainloop); + bool nonempty = false; + for (continued_statemachines_type::const_iterator iter = cscm_r->continued_statemachines.begin(); iter != cscm_r->continued_statemachines.end(); ++iter) + { + nonempty = true; + active_statemachines.push_back(QueueElement(*iter)); + Dout(dc::statemachine, "Adding " << (void*)*iter << " to active_statemachines"); + (*iter)->mActive = as_active; + } + if (nonempty) + AIWriteAccess(cscm_r)->continued_statemachines.clear(); + } + llassert(!active_statemachines.empty()); + // Run one or more state machines. + U64 total_clocks = 0; + U64 max_count = *AIAccess(sMaxCount); + for (active_statemachines_type::iterator iter = active_statemachines.begin(); iter != active_statemachines.end(); ++iter) + { + AIStateMachine& statemachine(iter->statemachine()); + if (!statemachine.mIdle) + { + U64 start = get_clock_count(); + // This might call idle() and then pass the statemachine to another thread who then may call cont(). + // Hence, after this isn't not sure what mIdle is, and it can change from true to false at any moment, + // if it is true after this function returns. + iter->statemachine().multiplex(start); + U64 delta = get_clock_count() - start; + iter->add(delta); + total_clocks += delta; + if (total_clocks >= max_count) + { #ifndef LL_RELEASE_FOR_DOWNLOAD - llwarns << "AIStateMachine::mainloop did run for " << (total_clocks * 1000 / calc_clock_frequency()) << " ms." << llendl; + llwarns << "AIStateMachine::mainloop did run for " << (total_clocks * 1000 / calc_clock_frequency()) << " ms." << llendl; #endif - std::sort(active_statemachines.begin(), active_statemachines.end(), QueueElementComp()); - break; - } + std::sort(active_statemachines.begin(), active_statemachines.end(), QueueElementComp()); + break; + } + } } - } - // Remove idle state machines from the loop. - active_statemachines_type::iterator iter = active_statemachines.begin(); - while (iter != active_statemachines.end()) - { - AIStateMachine& statemachine(iter->statemachine()); - // Atomic test mIdle and change mActive. - bool locked = statemachine.mIdleActive.tryLock(); - // If the lock failed, then another thread is in the middle of calling cont(), - // thus mIdle will end up false. So, there is no reason to block here; just - // treat mIdle as false already. - if (locked && statemachine.mIdle) + // Remove idle state machines from the loop. + active_statemachines_type::iterator iter = active_statemachines.begin(); + while (iter != active_statemachines.end()) { - // Without the lock, it would be possible that another thread called cont() right here, - // changing mIdle to false again but NOT adding the statemachine to continued_statemachines, - // thinking it is in active_statemachines (and it is), while immediately below it is - // erased from active_statemachines. - statemachine.mActive = as_idle; - // Now, calling cont() is ok -- as that will cause the statemachine to be added to - // continued_statemachines, so it's fine in that case-- even necessary-- to remove it from - // active_statemachines regardless, and we can release the lock here. - statemachine.mIdleActive.unlock(); - Dout(dc::statemachine, "Erasing " << (void*)&statemachine << " from active_statemachines"); - iter = active_statemachines.erase(iter); - if (statemachine.mState == bs_killed) - { - Dout(dc::statemachine, "Deleting " << (void*)&statemachine); - delete &statemachine; - } + AIStateMachine& statemachine(iter->statemachine()); + // Atomic test mIdle and change mActive. + bool locked = statemachine.mIdleActive.tryLock(); + // If the lock failed, then another thread is in the middle of calling cont(), + // thus mIdle will end up false. So, there is no reason to block here; just + // treat mIdle as false already. + if (locked && statemachine.mIdle) + { + // Without the lock, it would be possible that another thread called cont() right here, + // changing mIdle to false again but NOT adding the statemachine to continued_statemachines, + // thinking it is in active_statemachines (and it is), while immediately below it is + // erased from active_statemachines. + statemachine.mActive = as_idle; + // Now, calling cont() is ok -- as that will cause the statemachine to be added to + // continued_statemachines, so it's fine in that case-- even necessary-- to remove it from + // active_statemachines regardless, and we can release the lock here. + statemachine.mIdleActive.unlock(); + Dout(dc::statemachine, "Erasing " << (void*)&statemachine << " from active_statemachines"); + iter = active_statemachines.erase(iter); + if (statemachine.mState == bs_killed) + { + Dout(dc::statemachine, "Deleting " << (void*)&statemachine); + delete &statemachine; + } + } + else + { + if (locked) + { + statemachine.mIdleActive.unlock(); + } + llassert(statemachine.mActive == as_active); // It should not be possible that another thread called cont() and changed this when we are we are not idle. + llassert(statemachine.mState == bs_run || statemachine.mState == bs_initialize); + ++iter; + } } - else + if (active_statemachines.empty()) { - if (locked) - { - statemachine.mIdleActive.unlock(); - } - llassert(statemachine.mActive == as_active); // It should not be possible that another thread called cont() and changed this when we are we are not idle. - llassert(statemachine.mState == bs_run || statemachine.mState == bs_initialize); - ++iter; + // If this was the last state machine, remove mainloop from the IdleCallbacks. + AIReadAccess cscm_r(continued_statemachines_and_calling_mainloop); + if (cscm_r->continued_statemachines.empty() && cscm_r->calling_mainloop) + { + Dout(dc::statemachine, "Removing AIStateMachine::mainloop from gIdleCallbacks"); + AIWriteAccess(cscm_r)->calling_mainloop = false; + gIdleCallbacks.deleteFunction(&AIStateMachine::mainloop); + } } - } - if (active_statemachines.empty()) - { - // If this was the last state machine, remove mainloop from the IdleCallbacks. - AIReadAccess cscm_r(continued_statemachines_and_calling_mainloop); - if (cscm_r->continued_statemachines.empty() && cscm_r->calling_mainloop) - { - Dout(dc::statemachine, "Removing AIStateMachine::mainloop from gIdleCallbacks"); - AIWriteAccess(cscm_r)->calling_mainloop = false; - gIdleCallbacks.deleteFunction(&AIStateMachine::mainloop); - } - } } diff --git a/indra/newview/statemachine/aistatemachine.h b/indra/newview/statemachine/aistatemachine.h index f20fcf048..48effea9a 100644 --- a/indra/newview/statemachine/aistatemachine.h +++ b/indra/newview/statemachine/aistatemachine.h @@ -32,7 +32,7 @@ #define AISTATEMACHINE_H #include "aithreadsafe.h" -#include "llfasttimer.h" +#include "lltimer.h" #include //! @@ -247,7 +247,7 @@ class AIStateMachine { void yield_frame(unsigned int frames) { mSleep = -(S64)frames; } //! Temporarily halt the state machine. - void yield_ms(unsigned int ms) { mSleep = LLFastTimer::getCPUClockCount64() + LLFastTimer::countsPerSecond() * ms / 1000; } + void yield_ms(unsigned int ms) { mSleep = get_clock_count() + calc_clock_frequency() * ms / 1000; } //! Continue running after calling idle. void cont(void);