Esempio n. 1
0
void AIStateMachine::yield_frame(unsigned int frames)
{
  DoutEntering(dc::statemachine(mSMDebug), "AIStateMachine::yield_frame(" << frames << ") [" << (void*)this << "]");
  mSleep = -(S64)frames;
  // Sleeping is always done from the main thread.
  yield(&gMainThreadEngine);
}
Esempio n. 2
0
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);
}
Esempio n. 3
0
// 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;
}
Esempio n. 4
0
void AIStateMachine::run(callback_type::signal_type::slot_type const& slot, AIEngine* default_engine)
{
  DoutEntering(dc::statemachine(mSMDebug), "AIStateMachine::run(<slot>, default_engine = " << default_engine->name() << ") [" << (void*)this << "]");

#ifdef SHOW_ASSERT
  {
	multiplex_state_type_rat state_r(mState);
	// Can only be run when in one of these states.
	llassert(state_r->base_state == bs_reset || state_r->base_state == bs_finish || state_r->base_state == bs_callback);
	// Must be the first time we're being run, or we must be called from finish_impl or a callback function.
	llassert(!(state_r->base_state == bs_reset && (mParent || mCallback)));
  }
#endif

  // Store the requested default engine.
  mDefaultEngine = default_engine;

  // Initialize sleep timer.
  mSleep = 0;

  // Clean up any old callbacks.
  mParent = NULL;
  if (mCallback)
  {
	delete mCallback;
	mCallback = NULL;
  }

  // Create new call back.
  mCallback = new callback_type(slot);

  // Start from the beginning.
  reset();
}
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;
	}
  }

  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();
}
Esempio n. 6
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;
}
Esempio n. 7
0
void AIStateMachine::cont(void)
{
  DoutEntering(dc::statemachine(mSMDebug), "AIStateMachine::cont() [" << (void*)this << "]");
  {
	sub_state_type_wat sub_state_w(mSubState);
	// Void last call to idle(), if any.
	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.
	sub_state_w->need_run = true;
#ifdef SHOW_ASSERT
	// From this moment.
	mDebugContPending = true;
#endif
  }
  if (!mMultiplexMutex.isSelfLocked())
  {
	multiplex(schedule_run);
  }
}
Esempio n. 8
0
void AIStateMachine::idle(void)
{
  DoutEntering(dc::statemachine(mSMDebug), "AIStateMachine::idle() [" << (void*)this << "]");
#ifdef SHOW_ASSERT
  {
	multiplex_state_type_rat state_r(mState);
	// idle() may only be called from initialize_impl() or multiplex_impl().
	llassert(state_r->base_state == bs_multiplex || state_r->base_state == bs_initialize);
	// May only be called by the thread that is holding mMultiplexMutex.
	llassert(mThreadId.equals_current_thread());
  }
  // idle() 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 idle 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 idle() 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;
  }
  // Mark that we are idle.
  sub_state_w->idle = true;
  // Not sleeping (anymore).
  mSleep = 0;
}
Esempio n. 9
0
void AIStateMachine::set_state(state_type new_state)
{
  DoutEntering(dc::statemachine(mSMDebug), "AIStateMachine::set_state(" << state_str_impl(new_state) << ") [" << (void*)this << "]");
#ifdef SHOW_ASSERT
  {
	multiplex_state_type_rat state_r(mState);
	// set_state() may only be called from initialize_impl() or multiplex_impl().
	llassert(state_r->base_state == bs_initialize || state_r->base_state == bs_multiplex);
	// May only be called by the thread that is holding mMultiplexMutex. If this fails, you probably called set_state() by accident instead of advance_state().
	llassert(mThreadId.equals_current_thread());
  }
#endif
  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.
  sub_state_w->run_state = new_state;
  // Void last call to advance_state.
  sub_state_w->advance_state = 0;
  // Void last call to idle(), if any.
  sub_state_w->idle = false;
  // Honor a subsequent call to idle().
  sub_state_w->skip_idle = false;
#ifdef SHOW_ASSERT
  // We should run. This can only be cancelled by a call to idle().
  mDebugSetStatePending = true;
#endif
}
void AIStateMachine::run(callback_type::signal_type::slot_type const& slot)
{
  DoutEntering(dc::statemachine, "AIStateMachine::run(<slot>) [" << (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;
  }

  mCallback = new callback_type(slot);

  // Mark that run() has been called, in case we're being called from a callback function.
  mState = bs_initialize;

  // Set mIdle to false and add statemachine to continued_statemachines.
  mSetStateLock.lock();
  locked_cont();
}
	// This is called when the server expired and we're the only client on it.
	/*virtual*/ void deregistered(void)
	{
#ifdef DEBUG_SYNCOUTPUT
	  DoutEntering(dc::notice, "TestsuiteClient<" << synckeytype << ">::deregistered(), with this = " << this);
#endif
	  mRequestedRegistered = false;
	  mRequestedReady = 0;
	  mActualReady1 = false;
	  this->mReadyEvents = 0;
	}
Esempio n. 12
0
void AIStateMachine::advance_state(state_type new_state)
{
  DoutEntering(dc::statemachine(mSMDebug), "AIStateMachine::advance_state(" << state_str_impl(new_state) << ") [" << (void*)this << "]");
  {
	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.
	if (sub_state_w->advance_state >= new_state)
	{
	  Dout(dc::statemachine(mSMDebug), "Ignored, because " << state_str_impl(sub_state_w->advance_state) << " >= " << state_str_impl(new_state) << ".");
	  return;
	}
	// Ignore call to advance_state when the current state is greater than the requested state: the new state would be
	// ignored in begin_loop(), as is already remarked there: an advanced state that is not honored is not a reason to run.
	// This call might as well not have happened. Not returning here is a bug because that is effectively a cont(), while
	// 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)
	{
	  Dout(dc::statemachine(mSMDebug), "Ignored, because " << state_str_impl(sub_state_w->run_state) << " > " << state_str_impl(new_state) << " (current state).");
	  return;
	}
	// Increment state.
	sub_state_w->advance_state = new_state;
	// Void last call to idle(), if any.
	sub_state_w->idle = false;
	// Ignore a call to idle if it occurs before we leave multiplex_impl().
	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.
	sub_state_w->need_run = true;
#ifdef SHOW_ASSERT
	// From this moment on.
	mDebugAdvanceStatePending = true;
	// If the new state is equal to the current state, then this should be considered to be a cont()
	// because also equal states are ignored in begin_loop(). However, unlike a cont() we ignore a call
	// to idle() when the statemachine is already running in this state (because that is a race condition
	// and ignoring the idle() is the most logical thing to do then). Hence we treated this as a full
	// fletched advance_state but need to tell the debug code that it's really also a cont().
	if (sub_state_w->run_state == new_state)
	{
	  // From this moment.
	  mDebugContPending = true;
	}
#endif
  }
  if (!mMultiplexMutex.isSelfLocked())
  {
	multiplex(schedule_run);
  }
}
void AIStateMachine::idle(void)
{
  DoutEntering(dc::statemachine, "AIStateMachine::idle() [" << (void*)this << "]");
  llassert(is_main_thread());
  llassert(!mIdle);
  mIdle = true;
  mSleep = 0;
#ifdef SHOW_ASSERT
  mCalledThreadUnsafeIdle = true;
#endif
}
Esempio n. 14
0
void AIEngine::flush(void)
{
  engine_state_type_wat engine_state_w(mEngineState);
  DoutEntering(dc::statemachine, "AIEngine::flush [" << mName << "]: calling force_killed() on " << engine_state_w->list.size() << " state machines.");
  for (queued_type::iterator iter = engine_state_w->list.begin(); iter != engine_state_w->list.end(); ++iter)
  {
	// To avoid an assertion in ~AIStateMachine.
	iter->statemachine().force_killed();
  }
  engine_state_w->list.clear();
}
Esempio n. 15
0
void AIStateMachine::yield(void)
{
  DoutEntering(dc::statemachine(mSMDebug), "AIStateMachine::yield() [" << (void*)this << "]");
  multiplex_state_type_rat state_r(mState);
  // yield() may only be called from 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());
  // Set mYieldEngine to the best non-NUL value.
  mYieldEngine = state_r->current_engine ? state_r->current_engine : (mDefaultEngine ? mDefaultEngine : &gStateMachineThreadEngine);
}
Esempio n. 16
0
void JackOutput::create_allocated_buffer()
{
  DoutEntering(dc::notice, "JackOutput::create_allocated_buffer() with this = " << (void*)this << " [" << m_name << "].");

  // We should not try to allocate a buffer when we're already providing one.
  ASSERT(!has_provided_output_buffer(type()));
  // Makes no sense to allocate a buffer when we already have one.
  ASSERT(!m_allocated);

  m_chunk = static_cast<jack_default_audio_sample_t*>(JackChunkAllocator::instance().allocate());
  m_chunk_size = JackChunkAllocator::instance().chunk_size();
  m_allocated = true;
}
Esempio n. 17
0
void JackOutput::release_allocated_buffer(void)
{
  DoutEntering(dc::notice, "JackOutput::release_allocated_buffer() with this = " << (void*)this << " [" << m_name << "].");

  if (m_allocated)
  {
    ASSERT(m_chunk);
    JackChunkAllocator::instance().release(m_chunk);
    m_chunk = NULL;
    m_chunk_size = 0;
    m_allocated = false;
  }
}
void AICurlEasyRequestStateMachine::abort_impl(void)
{
  DoutEntering(dc::curl, "AICurlEasyRequestStateMachine::abort_impl() [" << (void*)this << "] [" << (void*)mCurlEasyRequest.get() << "]");
  // Revert call to addRequest() if that was already called (and the request wasn't removed again already).
  if (mAdded)
  {
	// Note that it's safe to call this even if the curl thread already removed it, or will remove it
	// after we called this, before processing the remove command; only the curl thread calls
	// MultiHandle::remove_easy_request, which is a no-op when called twice for the same easy request.
	mAdded = false;
	mCurlEasyRequest.removeRequest();
  }
}
void AIStateMachine::kill(void)
{
  DoutEntering(dc::statemachine, "AIStateMachine::kill() [" << (void*)this << "]");
  // Should only be called from finish() (or when not running (bs_initialize)).
  // However, also allow multiple calls to kill() on a row (bs_killed) (which effectively don't do anything).
  llassert(mIdle && (mState == bs_callback || mState == bs_finish || mState == bs_initialize || mState == bs_killed));
  base_state_type prev_state = mState;
  mState = bs_killed;
  if (prev_state == bs_initialize && mActive == as_idle)
  {
	// We're not running (ie being deleted by a parent statemachine), delete it immediately.
	delete this;
  }
}
Esempio n. 20
0
void AIStateMachine::run(AIStateMachine* parent, state_type new_parent_state, bool abort_parent, bool on_abort_signal_parent, AIEngine* default_engine)
{
  DoutEntering(dc::statemachine(mSMDebug), "AIStateMachine::run(" <<
	  (void*)parent << ", " <<
	  (parent ? parent->state_str_impl(new_parent_state) : "NA") <<
	  ", abort_parent = " << (abort_parent ? "true" : "false") <<
	  ", on_abort_signal_parent = " << (on_abort_signal_parent ? "true" : "false") <<
	  ", default_engine = " << (default_engine ? default_engine->name() : "NULL") << ") [" << (void*)this << "]");

#ifdef SHOW_ASSERT
  {
	multiplex_state_type_rat state_r(mState);
	// Can only be run when in one of these states.
	llassert(state_r->base_state == bs_reset || state_r->base_state == bs_finish || state_r->base_state == bs_callback);
	// Must be the first time we're being run, or we must be called from finish_impl or a callback function.
	llassert(!(state_r->base_state == bs_reset && (mParent || mCallback)));
  }
#endif

  // Store the requested default engine.
  mDefaultEngine = default_engine;

  // Initialize sleep timer.
  mSleep = 0;

  // 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;
	mOnAbortSignalParent = on_abort_signal_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->running());

  // Start from the beginning.
  reset();
}
Esempio n. 21
0
void AIStateMachine::yield(AIEngine* engine)
{
  llassert(engine);
  DoutEntering(dc::statemachine(mSMDebug), "AIStateMachine::yield(" << engine->name() << ") [" << (void*)this << "]");
#ifdef SHOW_ASSERT
  {
	multiplex_state_type_rat state_r(mState);
	// yield() may only be called from 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());
  }
#endif
  mYieldEngine = engine;
}
void AIStateMachine::idle(state_type current_run_state)
{
  DoutEntering(dc::statemachine, "AIStateMachine::idle(" << state_str(current_run_state) << ") [" << (void*)this << "]");
  llassert(is_main_thread());
  llassert(!mIdle);
  mSetStateLock.lock();
  // Only go idle if the run state is (still) what we expect it to be.
  // Otherwise assume that another thread called set_state() and continue running.
  if (current_run_state == mRunState)
  {
	mIdle = true;
	mSleep = 0;
  }
  mSetStateLock.unlock();
}
Esempio n. 23
0
void AIStateMachine::kill(void)
{
  DoutEntering(dc::statemachine(mSMDebug), "AIStateMachine::kill() [" << (void*)this << "]");
#ifdef SHOW_ASSERT
  {
	multiplex_state_type_rat state_r(mState);
	// kill() may only be called from the call back function.
	llassert(state_r->base_state == bs_callback);
	// May only be called by the thread that is holding mMultiplexMutex.
	llassert(mThreadId.equals_current_thread());
  }
#endif
  sub_state_type_wat sub_state_w(mSubState);
  // Void last call to run() (ie from finish_impl()), if any.
  sub_state_w->reset = false;
}
// static
void AIStateMachine::flush(void)
{
  DoutEntering(dc::curl, "AIStateMachine::flush(void)");
  {
	AIReadAccess<csme_type> csme_r(sContinuedStateMachinesAndMainloopEnabled);
	add_continued_statemachines(csme_r);
  }
  // Abort all state machines.
  for (active_statemachines_type::iterator iter = active_statemachines.begin(); iter != active_statemachines.end(); ++iter)
  {
	AIStateMachine& statemachine(iter->statemachine());
	if (statemachine.abortable())
	{
	  // We can't safely call abort() here for non-running (run() was called, but they weren't initialized yet) statemachines,
	  // because that might call kill() which in some cases is undesirable (ie, when it is owned by a partent that will
	  // also call abort() on it when it is aborted itself).
	  if (statemachine.running())
		statemachine.abort();
	  else
		statemachine.idle();		// Stop the statemachine from starting, in the next loop with batch == 0.
	}
  }
  for (int batch = 0;; ++batch)
  {
	// Run mainloop until all state machines are idle (batch == 0) or deleted (batch == 1).
	for(;;)
	{
	  {
		AIReadAccess<csme_type> csme_r(sContinuedStateMachinesAndMainloopEnabled);
		if (!csme_r->mainloop_enabled)
		  break;
	  }
	  mainloop();
	}
	if (batch == 1)
	  break;
	{
	  AIReadAccess<csme_type> csme_r(sContinuedStateMachinesAndMainloopEnabled);
	  add_continued_statemachines(csme_r);
	}
  }
  // At this point all statemachines should be idle.
  AIReadAccess<csme_type> csme_r(sContinuedStateMachinesAndMainloopEnabled);
  llinfos << "Current number of continued statemachines: " << csme_r->continued_statemachines.size() << llendl;
  llinfos << "Current number of active statemachines: " << active_statemachines.size() << llendl;
  llassert(csme_r->continued_statemachines.empty() && active_statemachines.empty());
}
void AICurlEasyRequestStateMachine::finish_impl(void)
{
  DoutEntering(dc::curl, "AICurlEasyRequestStateMachine::finish_impl() [" << (void*)this << "] [" << (void*)mCurlEasyRequest.get() << "]");
  // Revoke callbacks.
  {
	AICurlEasyRequest_wat curl_easy_request_w(*mCurlEasyRequest);
	curl_easy_request_w->send_buffer_events_to(NULL);
	curl_easy_request_w->send_handle_events_to(NULL);
	curl_easy_request_w->revokeCallbacks();
  }
  if (mTimer)
  {
	// Stop the timer, if it's still running.
	if (!mHandled)
	  mTimer->abort();
  }
}
Esempio n. 26
0
void AIStateMachine::finish(void)
{
  DoutEntering(dc::statemachine(mSMDebug), "AIStateMachine::finish() [" << (void*)this << "]");
#ifdef SHOW_ASSERT
  {
	multiplex_state_type_rat state_r(mState);
	// finish() may only be called from 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());
  }
#endif
  sub_state_type_wat sub_state_w(mSubState);
  // finish() should not be called when idle.
  llassert(!sub_state_w->idle);
  // Mark that we are finished.
  sub_state_w->finished = true;
}
// About thread safeness:
//
// The main thread initializes a statemachine and calls run, so a statemachine
// runs in the main thread. However, it is allowed that a state calls idle(current_state)
// and then allows one or more other threads to call cont() upon some
// event (only once, of course, as idle() has to be called before cont()
// can be called again-- and a non-main thread is not allowed to call idle()).
// Instead of cont() one may also call set_state().
// Of course, this may give rise to a race condition; if that happens then
// the thread that calls cont() (set_state()) first is serviced, and the other
// thread(s) are ignored, as if they never called cont().
void AIStateMachine::locked_cont(void)
{
  DoutEntering(dc::statemachine, "AIStateMachine::locked_cont() [" << (void*)this << "]");
  llassert(mIdle);
  // Atomic test mActive and change mIdle.
  mIdleActive.lock();
#ifdef SHOW_ASSERT
  mContThread.reset();
#endif
  mIdle = false;
  bool not_active = mActive == as_idle;
  mIdleActive.unlock();
  // mActive is only changed in AIStateMachine::mainloop, by the main-thread, and
  // here, possibly by any thread. However, after setting mIdle to false above, it
  // is impossible for any thread to come here, until after the main-thread called
  // idle(). So, if this is the main thread then that certainly isn't going to
  // happen until we left this function, while if this is another thread  and the
  // state machine is already running in the main thread then not_active is false
  // and we're already at the end of this function.
  // If not_active is true then main-thread is not running this statemachine.
  // It might call cont() (or set_state()) but never locked_cont(), and will never
  // start actually running until we are done here and release the lock on
  // sContinuedStateMachinesAndMainloopEnabled again. It is therefore safe
  // to release mSetStateLock here, with as advantage that if we're not the main-
  // thread and not_active is true, then the main-thread won't block when it has
  // a timer running that times out and calls set_state().
  mSetStateLock.unlock();
  if (not_active)
  {
	AIWriteAccess<csme_type> csme_w(sContinuedStateMachinesAndMainloopEnabled);
	// See above: it is not possible that mActive was changed since not_active
	// was set to true above.
	llassert_always(mActive == as_idle);
	Dout(dc::statemachine, "Adding " << (void*)this << " to continued_statemachines");
	csme_w->continued_statemachines.push_back(this);
	if (!csme_w->mainloop_enabled)
	{
	  Dout(dc::statemachine, "Activating AIStateMachine::mainloop.");
	  csme_w->mainloop_enabled = true;
	}
	mActive = as_queued;
	llassert_always(!mIdle);	// It should never happen that the main thread calls idle(), while another thread calls cont() concurrently.
  }
}
void AIStateMachine::abort(void)
{
  DoutEntering(dc::statemachine, "AIStateMachine::abort() [" << (void*)this << "]");
  // It's possible that abort() is called before calling AIStateMachine::multiplex.
  // In that case the statemachine wasn't initialized yet and we should just kill() it.
  if (LL_UNLIKELY(mState == bs_initialize))
  {
	// It's ok to use the thread-unsafe idle() here, because if the statemachine
	// wasn't started yet, then other threads won't call set_state() on it.
	if (!mIdle)
	  idle();
	// run() calls locked_cont() after which the top of the mainloop adds this
	// state machine to active_statemachines. Therefore, if the following fails
	// then either the same statemachine called run() immediately followed by abort(),
	// which is not allowed; or there were two active statemachines running,
	// the first created a new statemachine and called run() on it, and then
	// the other (before reaching the top of the mainloop) called abort() on
	// that freshly created statemachine. Obviously, this is highly unlikely,
	// but if that is the case then here we bump the statemachine into
	// continued_statemachines to prevent kill() to delete this statemachine:
	// the caller of abort() does not expect that.
	if (LL_UNLIKELY(mActive == as_idle))
	{
	  mSetStateLock.lock();
	  locked_cont();
	  idle();
	}
	kill();
  }
  else
  {
	llassert(mState == bs_run);
	mSetStateLock.lock();
	mState = bs_abort;		// Causes additional calls to set_state to be ignored.
	mSetStateLock.unlock();
	abort_impl();
	mAborted = true;
	finish();
  }
}
Esempio n. 29
0
AIStateMachine::state_type AIStateMachine::begin_loop(base_state_type base_state)
{
  DoutEntering(dc::statemachine(mSMDebug), "AIStateMachine::begin_loop(" << state_str(base_state) << ") [" << (void*)this << "]");

  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).
  sub_state_w->skip_idle = false;
  // Mark that we're about to honor all previous run requests.
  sub_state_w->need_run = false;
  // 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)
  {
	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;
  }
#ifdef SHOW_ASSERT
  else
  {
	// If advance_state wasn't honored then it isn't a reason to run.
	// We're running anyway, but that should be because set_state() was called.
	mDebugAdvanceStatePending = false;
  }
#endif
  sub_state_w->advance_state = 0;

#ifdef SHOW_ASSERT
  // Mark that we're running the loop.
  mThreadId.reset();
  // This point marks handling cont().
  mDebugShouldRun |= mDebugContPending;
  mDebugContPending = false;
  // This point also marks handling advance_state().
  mDebugShouldRun |= mDebugAdvanceStatePending;
  mDebugAdvanceStatePending = false;
#endif

  // Make a copy of the state that we're about to run.
  return sub_state_w->run_state;
}
Esempio n. 30
0
void AIStateMachine::callback(void)
{
  DoutEntering(dc::statemachine(mSMDebug), "AIStateMachine::callback() [" << (void*)this << "]");

  bool aborted = sub_state_type_rat(mSubState)->aborted;
  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 (aborted && mAbortParent)
	  {
		mParent->abort();
		mParent = NULL;
	  }
	  else if (!aborted || mOnAbortSignalParent)
	  {
		mParent->advance_state(mNewParentState);
	  }
	}
  }
  if (mCallback)
  {
	mCallback->callback(!aborted);
	if (multiplex_state_type_rat(mState)->base_state != bs_reset)
	{
	  delete mCallback;
	  mCallback = NULL;
	  mParent = NULL;
	}
  }
  else
  {
	// Not restarted by callback. Allow run() to be called later on.
	mParent = NULL;
  }
}