// Called from AIStateMachine::mainloop, but put here because we don't want to include llurlrequest.h there of course. void print_statemachine_diagnostics(U64 total_clocks, U64 max_delta, AIEngine::queued_type::const_reference slowest_element) { AIStateMachine const& slowest_state_machine = slowest_element.statemachine(); LLURLRequest const* request = dynamic_cast<LLURLRequest const*>(&slowest_state_machine); F64 const tfactor = 1000 / calc_clock_frequency(); std::ostringstream msg; if (total_clocks > max_delta) { msg << "AIStateMachine::mainloop did run for " << (total_clocks * tfactor) << " ms. The slowest "; } else { msg << "AIStateMachine::mainloop: A "; } msg << "state machine "; if (request) { msg << "(" << request->getResponderName() << ") "; } msg << "ran for " << (max_delta * tfactor) << " ms"; if (slowest_state_machine.getRuntime() > max_delta) { msg << " (" << (slowest_state_machine.getRuntime() * tfactor) << " ms in total now)"; } msg << "."; llwarns << msg.str() << llendl; }
// Called from AIStateMachine::mainloop void print_statemachine_diagnostics(U64 total_clocks, AIStateMachine::StateTimerBase::TimeData& slowest_timer, AIEngine::queued_type::const_reference slowest_element) { AIStateMachine const& slowest_state_machine = slowest_element.statemachine(); F64 const tfactor = 1000 / calc_clock_frequency(); std::ostringstream msg; U64 max_delta = slowest_timer.GetDuration(); if (total_clocks > max_delta) { msg << "AIStateMachine::mainloop did run for " << (total_clocks * tfactor) << " ms. The slowest "; } else { msg << "AIStateMachine::mainloop: A "; } msg << "state machine " << "(" << slowest_state_machine.getName() << ") " << "ran for " << (max_delta * tfactor) << " ms"; if (slowest_state_machine.getRuntime() > max_delta) { msg << " (" << (slowest_state_machine.getRuntime() * tfactor) << " ms in total now)"; } msg << ".\n"; AIStateMachine::StateTimerBase::DumpTimers(msg); llwarns << msg.str() << llendl; }
void AIStateMachine::yield_ms(unsigned int ms) { DoutEntering(dc::statemachine(mSMDebug), "AIStateMachine::yield_ms(" << ms << ") [" << (void*)this << "]"); mSleep = get_clock_count() + calc_clock_frequency() * ms / 1000; // Sleeping is always done from the main thread. yield(&gMainThreadEngine); }
void AIStateMachine::StateTimer::TimeData::DumpTimer(std::ostringstream& msg, std::string prefix) { F64 const tfactor = 1000 / calc_clock_frequency(); msg << prefix << mName << " " << (mEnd - mStart)*tfactor << "ms" << std::endl; prefix.push_back(' '); std::vector<TimeData>::iterator it; for (it = mChildren.begin(); it != mChildren.end(); ++it) { it->DumpTimer(msg, prefix); } }
// static void AIStateMachine::setMaxCount(F32 StateMachineMaxTime) { llassert(is_main_thread()); Dout(dc::statemachine, "(Re)calculating AIStateMachine::sMaxCount"); sMaxCount = calc_clock_frequency() * StateMachineMaxTime / 1000; }
// static void AIStateMachine::dowork(void) { llassert(!active_statemachines.empty()); // Run one or more state machines. U64 total_clocks = 0; 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. statemachine.multiplex(start); U64 delta = get_clock_count() - start; iter->add(delta); total_clocks += delta; if (total_clocks >= sMaxCount) { #ifndef LL_RELEASE_FOR_DOWNLOAD 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; } } } // 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) { // 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; } } if (active_statemachines.empty()) { // If this was the last state machine, remove mainloop from the IdleCallbacks. AIReadAccess<csme_type> csme_r(sContinuedStateMachinesAndMainloopEnabled, true); if (csme_r->continued_statemachines.empty() && csme_r->mainloop_enabled) { Dout(dc::statemachine, "Deactivating AIStateMachine::mainloop: no active state machines left."); AIWriteAccess<csme_type>(csme_r)->mainloop_enabled = false; } } }