// MAIN-THREAD void AIEngine::mainloop(void) { queued_type::iterator queued_element, end; { engine_state_type_wat engine_state_w(mEngineState); end = engine_state_w->list.end(); queued_element = engine_state_w->list.begin(); } U64 total_clocks = 0; #if STATE_MACHINE_PROFILING queued_type::value_type slowest_element(NULL); AIStateMachine::StateTimerRoot::TimeData slowest_timer; #endif while (queued_element != end) { AIStateMachine& state_machine(queued_element->statemachine()); AIStateMachine::StateTimerBase::TimeData time_data; if (!state_machine.sleep(get_clock_count())) { AIStateMachine::StateTimerRoot timer(state_machine.getName()); state_machine.multiplex(AIStateMachine::normal_run); time_data = timer.GetTimerData(); } if (U64 delta = time_data.GetDuration()) { state_machine.add(delta); total_clocks += delta; #if STATE_MACHINE_PROFILING if (delta > slowest_timer.GetDuration()) { slowest_element = *queued_element; slowest_timer = time_data; } #endif } bool active = state_machine.active(this); // This locks mState shortly, so it must be called before locking mEngineState because add() locks mEngineState while holding mState. engine_state_type_wat engine_state_w(mEngineState); if (!active) { Dout(dc::statemachine(state_machine.mSMDebug), "Erasing state machine [" << (void*)&state_machine << "] from " << mName); engine_state_w->list.erase(queued_element++); } else { ++queued_element; } if (total_clocks >= sMaxCount) { #if STATE_MACHINE_PROFILING print_statemachine_diagnostics(total_clocks, slowest_timer, slowest_element); #endif Dout(dc::statemachine, "Sorting " << engine_state_w->list.size() << " state machines."); engine_state_w->list.sort(QueueElementComp()); break; } } }
// 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; } } }