Revert "Work in progress"
This reverts commit ef35aa7954
because it contained too much wrong things that I won't be
using. I'll re-commit stuff from it after that that I do
want to keep.
This commit is contained in:
@@ -82,8 +82,7 @@ void AIStateMachine::setMaxCount(F32 StateMachineMaxTime)
|
|||||||
|
|
||||||
void AIStateMachine::run(AIStateMachine* parent, state_type new_parent_state, bool abort_parent, bool on_abort_signal_parent)
|
void AIStateMachine::run(AIStateMachine* parent, state_type new_parent_state, bool abort_parent, bool on_abort_signal_parent)
|
||||||
{
|
{
|
||||||
DoutEntering(dc::statemachine, "AIStateMachine::run(" << (void*)parent << ", " << (parent ? parent->state_str(new_parent_state) : "NA") <<
|
DoutEntering(dc::statemachine, "AIStateMachine::run(" << (void*)parent << ", " << (parent ? parent->state_str(new_parent_state) : "NA") << ", " << abort_parent << ") [" << (void*)this << "]");
|
||||||
", " << abort_parent << ", " << on_abort_signal_parent << ") [" << (void*)this << "]");
|
|
||||||
// Must be the first time we're being run, or we must be called from a callback function.
|
// Must be the first time we're being run, or we must be called from a callback function.
|
||||||
llassert(!mParent || mState == bs_callback);
|
llassert(!mParent || mState == bs_callback);
|
||||||
llassert(!mCallback || mState == bs_callback);
|
llassert(!mCallback || mState == bs_callback);
|
||||||
@@ -114,7 +113,7 @@ void AIStateMachine::run(AIStateMachine* parent, state_type new_parent_state, bo
|
|||||||
// Mark that run() has been called, in case we're being called from a callback function.
|
// Mark that run() has been called, in case we're being called from a callback function.
|
||||||
mState = bs_initialize;
|
mState = bs_initialize;
|
||||||
|
|
||||||
// Set mIdle to false and add statemachine to continued_statemachines-- or, when boosted, run the statemachine till it finished or goes idle.
|
// Set mIdle to false and add statemachine to continued_statemachines.
|
||||||
mSetStateLock.lock();
|
mSetStateLock.lock();
|
||||||
locked_cont();
|
locked_cont();
|
||||||
}
|
}
|
||||||
@@ -141,7 +140,7 @@ void AIStateMachine::run(callback_type::signal_type::slot_type const& slot)
|
|||||||
// Mark that run() has been called, in case we're being called from a callback function.
|
// Mark that run() has been called, in case we're being called from a callback function.
|
||||||
mState = bs_initialize;
|
mState = bs_initialize;
|
||||||
|
|
||||||
// Set mIdle to false and add statemachine to continued_statemachines-- or, when boosted, run the statemachine till it finished or goes idle.
|
// Set mIdle to false and add statemachine to continued_statemachines.
|
||||||
mSetStateLock.lock();
|
mSetStateLock.lock();
|
||||||
locked_cont();
|
locked_cont();
|
||||||
}
|
}
|
||||||
@@ -149,11 +148,9 @@ void AIStateMachine::run(callback_type::signal_type::slot_type const& slot)
|
|||||||
void AIStateMachine::idle(void)
|
void AIStateMachine::idle(void)
|
||||||
{
|
{
|
||||||
DoutEntering(dc::statemachine, "AIStateMachine::idle() [" << (void*)this << "]");
|
DoutEntering(dc::statemachine, "AIStateMachine::idle() [" << (void*)this << "]");
|
||||||
llassert(mInsideMultiplexImpl);
|
llassert(is_main_thread());
|
||||||
mSetStateLock.lock();
|
|
||||||
llassert(!mIdle);
|
llassert(!mIdle);
|
||||||
mIdle = true;
|
mIdle = true;
|
||||||
mSetStateLock.unlock();
|
|
||||||
mSleep = 0;
|
mSleep = 0;
|
||||||
#ifdef SHOW_ASSERT
|
#ifdef SHOW_ASSERT
|
||||||
mCalledThreadUnsafeIdle = true;
|
mCalledThreadUnsafeIdle = true;
|
||||||
@@ -163,12 +160,9 @@ void AIStateMachine::idle(void)
|
|||||||
void AIStateMachine::idle(state_type current_run_state)
|
void AIStateMachine::idle(state_type current_run_state)
|
||||||
{
|
{
|
||||||
DoutEntering(dc::statemachine, "AIStateMachine::idle(" << state_str(current_run_state) << ") [" << (void*)this << "]");
|
DoutEntering(dc::statemachine, "AIStateMachine::idle(" << state_str(current_run_state) << ") [" << (void*)this << "]");
|
||||||
llassert(mInsideMultiplexImpl); // Only boosted state machines can come here as non-main thread because you may only get here from multiplex_impl().
|
llassert(is_main_thread());
|
||||||
mSetStateLock.lock();
|
|
||||||
// If two different threads call multiplex_impl() (boosted state machines) then that should happen serialized:
|
|
||||||
// only one thread may call multiplex() at any given time. Hence, it is impossible that idle() is called while
|
|
||||||
// we are already idle.
|
|
||||||
llassert(!mIdle);
|
llassert(!mIdle);
|
||||||
|
mSetStateLock.lock();
|
||||||
// Only go idle if the run state is (still) what we expect it to be.
|
// 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.
|
// Otherwise assume that another thread called set_state() and continue running.
|
||||||
if (current_run_state == mRunState)
|
if (current_run_state == mRunState)
|
||||||
@@ -202,7 +196,6 @@ void AIStateMachine::locked_cont(void)
|
|||||||
mIdle = false;
|
mIdle = false;
|
||||||
bool not_active = mActive == as_idle;
|
bool not_active = mActive == as_idle;
|
||||||
mIdleActive.unlock();
|
mIdleActive.unlock();
|
||||||
mBoost = thread_safe_impl();
|
|
||||||
// mActive is only changed in AIStateMachine::mainloop, by the main-thread, and
|
// 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
|
// 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
|
// is impossible for any thread to come here, until after the main-thread called
|
||||||
@@ -217,15 +210,8 @@ void AIStateMachine::locked_cont(void)
|
|||||||
// to release mSetStateLock here, with as advantage that if we're not the main-
|
// 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
|
// 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().
|
// a timer running that times out and calls set_state().
|
||||||
// Finally, if mBoost is true then not_active is not going to be tested and
|
|
||||||
// mActive is not going to be changed, so then we're done with this lock too.
|
|
||||||
mSetStateLock.unlock();
|
mSetStateLock.unlock();
|
||||||
if (mBoost)
|
if (not_active)
|
||||||
{
|
|
||||||
// Run statemachine until it is finished or idle again.
|
|
||||||
multiplex(0);
|
|
||||||
}
|
|
||||||
else if (not_active)
|
|
||||||
{
|
{
|
||||||
AIWriteAccess<csme_type> csme_w(sContinuedStateMachinesAndMainloopEnabled);
|
AIWriteAccess<csme_type> csme_w(sContinuedStateMachinesAndMainloopEnabled);
|
||||||
// See above: it is not possible that mActive was changed since not_active
|
// See above: it is not possible that mActive was changed since not_active
|
||||||
@@ -250,6 +236,9 @@ void AIStateMachine::set_state(state_type state)
|
|||||||
// Stop race condition of multiple threads calling cont() or set_state() here.
|
// Stop race condition of multiple threads calling cont() or set_state() here.
|
||||||
mSetStateLock.lock();
|
mSetStateLock.lock();
|
||||||
|
|
||||||
|
// Do not call set_state() unless running.
|
||||||
|
llassert(mState == bs_run || !is_main_thread());
|
||||||
|
|
||||||
// If this function is called from another thread than the main thread, then we have to ignore
|
// If this function is called from another thread than the main thread, then we have to ignore
|
||||||
// it if we're not idle and the state is less than the current state. The main thread must
|
// it if we're not idle and the state is less than the current state. The main thread must
|
||||||
// be able to change the state to anything (also smaller values). Note that that only can work
|
// be able to change the state to anything (also smaller values). Note that that only can work
|
||||||
@@ -290,11 +279,8 @@ void AIStateMachine::set_state(state_type state)
|
|||||||
return; // Ignore.
|
return; // Ignore.
|
||||||
}
|
}
|
||||||
|
|
||||||
// Do not call set_state() from multiplex_impl unless running.
|
|
||||||
llassert(mState == bs_run || !mInsideMultiplexImpl);
|
|
||||||
|
|
||||||
// Do not call idle() when set_state is called from another thread; use idle(state_type) instead.
|
// Do not call idle() when set_state is called from another thread; use idle(state_type) instead.
|
||||||
llassert(!mCalledThreadUnsafeIdle || AIThreadID::in_main_thread());
|
llassert(!mCalledThreadUnsafeIdle || is_main_thread());
|
||||||
|
|
||||||
// Change mRunState to the requested value.
|
// Change mRunState to the requested value.
|
||||||
if (mRunState != state)
|
if (mRunState != state)
|
||||||
@@ -307,15 +293,7 @@ void AIStateMachine::set_state(state_type state)
|
|||||||
if (mIdle)
|
if (mIdle)
|
||||||
locked_cont(); // This unlocks mSetStateLock.
|
locked_cont(); // This unlocks mSetStateLock.
|
||||||
else
|
else
|
||||||
{
|
|
||||||
// The state was changed while not being idle.
|
|
||||||
// Note that if thread_safe_impl() returns true now then we don't need to call multiplex() here
|
|
||||||
// when we ended up here from multiplex(). However, if we end up here as the result of an
|
|
||||||
// external state change while the state machine is running in the main thread, then we can't
|
|
||||||
// call multiplex here; it would be too dangerous. In that case we let the main thread pick
|
|
||||||
// up this state.
|
|
||||||
mSetStateLock.unlock();
|
mSetStateLock.unlock();
|
||||||
}
|
|
||||||
|
|
||||||
// If we get here then mIdle is false, so only mRunState can still be changed but we won't
|
// If we get here then mIdle is false, so only mRunState can still be changed but we won't
|
||||||
// call locked_cont() anymore. When the main thread finally picks up on the state change,
|
// call locked_cont() anymore. When the main thread finally picks up on the state change,
|
||||||
@@ -476,7 +454,6 @@ void AIStateMachine::multiplex(U64 current_time)
|
|||||||
// amount of time has passed.
|
// amount of time has passed.
|
||||||
if (mSleep != 0)
|
if (mSleep != 0)
|
||||||
{
|
{
|
||||||
llassert(!mBoost); // Sleeping MUST be done in the main thread: thread_safe_impl() should have returned false.
|
|
||||||
if (mSleep < 0)
|
if (mSleep < 0)
|
||||||
{
|
{
|
||||||
if (++mSleep)
|
if (++mSleep)
|
||||||
@@ -498,90 +475,11 @@ void AIStateMachine::multiplex(U64 current_time)
|
|||||||
{
|
{
|
||||||
mAborted = false;
|
mAborted = false;
|
||||||
mState = bs_run;
|
mState = bs_run;
|
||||||
#ifdef SHOW_ASSERT
|
|
||||||
mInsideMultiplexImpl = true;
|
|
||||||
#endif
|
|
||||||
initialize_impl();
|
initialize_impl();
|
||||||
#ifdef SHOW_ASSERT
|
|
||||||
mInsideMultiplexImpl = false;
|
|
||||||
#endif
|
|
||||||
if (mAborted || mState != bs_run)
|
if (mAborted || mState != bs_run)
|
||||||
{
|
|
||||||
mBoost = false;
|
|
||||||
return;
|
return;
|
||||||
}
|
|
||||||
mSetStateLock.lock();
|
|
||||||
mBoost = thread_safe_impl();
|
|
||||||
if (!mBoost && !AIThreadID::in_main_thread())
|
|
||||||
{
|
|
||||||
// Pass running on the main thread.
|
|
||||||
mIdle = true;
|
|
||||||
locked_cont();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
mSetStateLock.unlock();
|
|
||||||
}
|
|
||||||
if (!mBoost)
|
|
||||||
{
|
|
||||||
llassert(AIThreadID::in_main_thread());
|
|
||||||
#ifdef SHOW_ASSERT
|
|
||||||
mInsideMultiplexImpl = true;
|
|
||||||
#endif
|
|
||||||
multiplex_impl();
|
|
||||||
#ifdef SHOW_ASSERT
|
|
||||||
mInsideMultiplexImpl = false;
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (mState != bs_run)
|
|
||||||
return;
|
|
||||||
|
|
||||||
mSetStateLock.lock();
|
|
||||||
llassert(!mIdle); // We should never get here when idle.
|
|
||||||
|
|
||||||
if (!mMultiplexMutex.tryLock()) // Point A
|
|
||||||
{
|
|
||||||
// If another thread is already already calling multiplex_impl() then we can't do that too.
|
|
||||||
// However, instead of blocking we simply return: by getting here it is guaranteed that
|
|
||||||
// the other thread will do at least one more test of 'mState == bs_run && !mIdle' before
|
|
||||||
// stopping to run the statemachine.
|
|
||||||
// The reason for that is that if another thread had mMultiplexMutex at point A, while
|
|
||||||
// we had mSetStateLock, then the other thread has to be between point B and point C
|
|
||||||
// because that is the only area where mSetStateLock is unlocked while mMultiplexMutex
|
|
||||||
// is locked, and the test follows this area.
|
|
||||||
mSetStateLock.unlock();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
do
|
|
||||||
{
|
|
||||||
mSetStateLock.unlock(); // Point B
|
|
||||||
#ifdef SHOW_ASSERT
|
|
||||||
mInsideMultiplexImpl = true;
|
|
||||||
#endif
|
|
||||||
multiplex_impl();
|
|
||||||
#ifdef SHOW_ASSERT
|
|
||||||
mInsideMultiplexImpl = false;
|
|
||||||
#endif
|
|
||||||
mSetStateLock.lock(); // Point C
|
|
||||||
|
|
||||||
// The state might have changed, check if we may still run outside the main thread.
|
|
||||||
mBoost = thread_safe_impl();
|
|
||||||
if (!mBoost && !AIThreadID::in_main_thread())
|
|
||||||
{
|
|
||||||
mMultiplexMutex.unlock();
|
|
||||||
// Pass running on the main thread.
|
|
||||||
mIdle = true;
|
|
||||||
locked_cont();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
while (mState == bs_run && !mIdle);
|
|
||||||
|
|
||||||
mMultiplexMutex.unlock();
|
|
||||||
mSetStateLock.unlock();
|
|
||||||
}
|
}
|
||||||
|
multiplex_impl();
|
||||||
}
|
}
|
||||||
|
|
||||||
//static
|
//static
|
||||||
@@ -692,10 +590,6 @@ void AIStateMachine::flush(void)
|
|||||||
for (active_statemachines_type::iterator iter = active_statemachines.begin(); iter != active_statemachines.end(); ++iter)
|
for (active_statemachines_type::iterator iter = active_statemachines.begin(); iter != active_statemachines.end(); ++iter)
|
||||||
{
|
{
|
||||||
AIStateMachine& statemachine(iter->statemachine());
|
AIStateMachine& statemachine(iter->statemachine());
|
||||||
#ifdef SHOW_ASSERT
|
|
||||||
// Make assertions happy.
|
|
||||||
statemachine.mInsideMultiplexImpl = true;
|
|
||||||
#endif
|
|
||||||
if (statemachine.abortable())
|
if (statemachine.abortable())
|
||||||
{
|
{
|
||||||
// We can't safely call abort() here for non-running (run() was called, but they weren't initialized yet) statemachines,
|
// We can't safely call abort() here for non-running (run() was called, but they weren't initialized yet) statemachines,
|
||||||
|
|||||||
@@ -176,7 +176,6 @@
|
|||||||
// Should return a stringified value of run_state.
|
// Should return a stringified value of run_state.
|
||||||
//
|
//
|
||||||
class AIStateMachine {
|
class AIStateMachine {
|
||||||
protected:
|
|
||||||
//! The type of mState
|
//! The type of mState
|
||||||
enum base_state_type {
|
enum base_state_type {
|
||||||
bs_initialize,
|
bs_initialize,
|
||||||
@@ -214,15 +213,12 @@ class AIStateMachine {
|
|||||||
base_state_type mState; //!< State of the base class.
|
base_state_type mState; //!< State of the base class.
|
||||||
bool mIdle; //!< True if this state machine is not running.
|
bool mIdle; //!< True if this state machine is not running.
|
||||||
bool mAborted; //!< True after calling abort() and before calling run().
|
bool mAborted; //!< True after calling abort() and before calling run().
|
||||||
bool mBoost; //!< True when not being run by the main thread, but by which ever thread is changing the state.
|
active_type mActive; //!< Whether statemachine is idle, queued to be added to the active list, or already on the active list.
|
||||||
active_type mActive; //!< Whether statemachine is idle, queued to be added to the active list, or already on the active list (not used for boosted SM).
|
S64 mSleep; //!< Non-zero while the state machine is sleeping.
|
||||||
S64 mSleep; //!< Non-zero while the state machine is sleeping (not used for boosted SM).
|
LLMutex mIdleActive; //!< Used for atomic operations on the pair mIdle / mActive.
|
||||||
LLMutex mIdleActive; //!< Used for atomic operations on the pair mIdle / mActive (not used for boosted SM).
|
|
||||||
LLMutex mMultiplexMutex; //!< Used for boosted state machines, locked during multiplexing.
|
|
||||||
#ifdef SHOW_ASSERT
|
#ifdef SHOW_ASSERT
|
||||||
AIThreadID mContThread; //!< Thread that last called locked_cont().
|
AIThreadID mContThread; //!< Thread that last called locked_cont().
|
||||||
bool mCalledThreadUnsafeIdle; //!< Set to true when idle() is called.
|
bool mCalledThreadUnsafeIdle; //!< Set to true when idle() is called.
|
||||||
bool mInsideMultiplexImpl; //!< Set to true while running a boosted state machine (also set while inside initialize_impl).
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// Callback facilities.
|
// Callback facilities.
|
||||||
@@ -254,11 +250,10 @@ class AIStateMachine {
|
|||||||
|
|
||||||
public:
|
public:
|
||||||
//! Create a non-running state machine.
|
//! Create a non-running state machine.
|
||||||
AIStateMachine(void) : mState(bs_initialize), mIdle(true), mAborted(true), mActive(as_idle), mSleep(0),
|
AIStateMachine(void) : mState(bs_initialize), mIdle(true), mAborted(true), mActive(as_idle), mSleep(0), mParent(NULL), mCallback(NULL)
|
||||||
#ifdef SHOW_ASSERT
|
#ifdef SHOW_ASSERT
|
||||||
mContThread(AIThreadID::none), mCalledThreadUnsafeIdle(false),
|
, mContThread(AIThreadID::none), mCalledThreadUnsafeIdle(false)
|
||||||
#endif
|
#endif
|
||||||
mParent(NULL), mCallback(NULL), mRunState(0)
|
|
||||||
{ }
|
{ }
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
@@ -379,10 +374,6 @@ class AIStateMachine {
|
|||||||
//! Return a stringified state, for debugging purposes.
|
//! Return a stringified state, for debugging purposes.
|
||||||
char const* state_str(state_type state);
|
char const* state_str(state_type state);
|
||||||
|
|
||||||
protected:
|
|
||||||
//! Return mState.
|
|
||||||
base_state_type base_state(void) const { return mState; }
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
static void add_continued_statemachines(AIReadAccess<csme_type>& csme_r);
|
static void add_continued_statemachines(AIReadAccess<csme_type>& csme_r);
|
||||||
static void dowork(void);
|
static void dowork(void);
|
||||||
@@ -421,10 +412,6 @@ class AIStateMachine {
|
|||||||
// Handle cleaning up from initialization (or post abort) state.
|
// Handle cleaning up from initialization (or post abort) state.
|
||||||
virtual void finish_impl(void) = 0;
|
virtual void finish_impl(void) = 0;
|
||||||
|
|
||||||
// Determine if it is thread-safe to run the current state in another thread than the main-thread.
|
|
||||||
// Only called while mSetStateLock is locked, may ONLY access mState and mRunState.
|
|
||||||
virtual bool thread_safe_impl(void) const { return false; }
|
|
||||||
|
|
||||||
// Implemenation of state_str for run states.
|
// Implemenation of state_str for run states.
|
||||||
virtual char const* state_str_impl(state_type run_state) const = 0;
|
virtual char const* state_str_impl(state_type run_state) const = 0;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -33,8 +33,7 @@
|
|||||||
|
|
||||||
enum timer_state_type {
|
enum timer_state_type {
|
||||||
AITimer_start = AIStateMachine::max_state,
|
AITimer_start = AIStateMachine::max_state,
|
||||||
AITimer_expired,
|
AITimer_expired
|
||||||
AITimer_abort
|
|
||||||
};
|
};
|
||||||
|
|
||||||
char const* AITimer::state_str_impl(state_type run_state) const
|
char const* AITimer::state_str_impl(state_type run_state) const
|
||||||
@@ -43,7 +42,6 @@ char const* AITimer::state_str_impl(state_type run_state) const
|
|||||||
{
|
{
|
||||||
AI_CASE_RETURN(AITimer_start);
|
AI_CASE_RETURN(AITimer_start);
|
||||||
AI_CASE_RETURN(AITimer_expired);
|
AI_CASE_RETURN(AITimer_expired);
|
||||||
AI_CASE_RETURN(AITimer_abort);
|
|
||||||
}
|
}
|
||||||
return "UNKNOWN STATE";
|
return "UNKNOWN STATE";
|
||||||
}
|
}
|
||||||
@@ -54,12 +52,6 @@ void AITimer::initialize_impl(void)
|
|||||||
set_state(AITimer_start);
|
set_state(AITimer_start);
|
||||||
}
|
}
|
||||||
|
|
||||||
void AITimer::request_abort(void)
|
|
||||||
{
|
|
||||||
mFrameTimer.cancel();
|
|
||||||
set_state(AITimer_abort);
|
|
||||||
}
|
|
||||||
|
|
||||||
void AITimer::expired(void)
|
void AITimer::expired(void)
|
||||||
{
|
{
|
||||||
set_state(AITimer_expired);
|
set_state(AITimer_expired);
|
||||||
@@ -72,7 +64,7 @@ void AITimer::multiplex_impl(void)
|
|||||||
case AITimer_start:
|
case AITimer_start:
|
||||||
{
|
{
|
||||||
mFrameTimer.create(mInterval, boost::bind(&AITimer::expired, this));
|
mFrameTimer.create(mInterval, boost::bind(&AITimer::expired, this));
|
||||||
idle(AITimer_start);
|
idle();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case AITimer_expired:
|
case AITimer_expired:
|
||||||
@@ -80,11 +72,6 @@ void AITimer::multiplex_impl(void)
|
|||||||
finish();
|
finish();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case AITimer_abort:
|
|
||||||
{
|
|
||||||
abort();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -82,11 +82,6 @@ class AITimer : public AIStateMachine {
|
|||||||
*/
|
*/
|
||||||
F64 getInterval(void) const { return mInterval; }
|
F64 getInterval(void) const { return mInterval; }
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Abort the timer from another thread.
|
|
||||||
*/
|
|
||||||
void request_abort(void);
|
|
||||||
|
|
||||||
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, "~AITimer() [" << (void*)this << "]"); mFrameTimer.cancel(); }
|
||||||
|
|||||||
@@ -33,13 +33,26 @@
|
|||||||
#include "aihttptimeoutpolicy.h"
|
#include "aihttptimeoutpolicy.h"
|
||||||
#include "llcontrol.h"
|
#include "llcontrol.h"
|
||||||
|
|
||||||
|
enum curleasyrequeststatemachine_state_type {
|
||||||
|
AICurlEasyRequestStateMachine_addRequest = AIStateMachine::max_state,
|
||||||
|
AICurlEasyRequestStateMachine_waitAdded,
|
||||||
|
AICurlEasyRequestStateMachine_added,
|
||||||
|
AICurlEasyRequestStateMachine_timedOut, // This must be smaller than the rest, so they always overrule.
|
||||||
|
AICurlEasyRequestStateMachine_finished,
|
||||||
|
AICurlEasyRequestStateMachine_removed, // The removed states must be largest two, so they are never ignored.
|
||||||
|
AICurlEasyRequestStateMachine_removed_after_finished,
|
||||||
|
AICurlEasyRequestStateMachine_bad_file_descriptor
|
||||||
|
};
|
||||||
|
|
||||||
char const* AICurlEasyRequestStateMachine::state_str_impl(state_type run_state) const
|
char const* AICurlEasyRequestStateMachine::state_str_impl(state_type run_state) const
|
||||||
{
|
{
|
||||||
switch(run_state)
|
switch(run_state)
|
||||||
{
|
{
|
||||||
AI_CASE_RETURN(AICurlEasyRequestStateMachine_addRequest);
|
AI_CASE_RETURN(AICurlEasyRequestStateMachine_addRequest);
|
||||||
AI_CASE_RETURN(AICurlEasyRequestStateMachine_waitAdded);
|
AI_CASE_RETURN(AICurlEasyRequestStateMachine_waitAdded);
|
||||||
|
AI_CASE_RETURN(AICurlEasyRequestStateMachine_added);
|
||||||
AI_CASE_RETURN(AICurlEasyRequestStateMachine_timedOut);
|
AI_CASE_RETURN(AICurlEasyRequestStateMachine_timedOut);
|
||||||
|
AI_CASE_RETURN(AICurlEasyRequestStateMachine_finished);
|
||||||
AI_CASE_RETURN(AICurlEasyRequestStateMachine_removed);
|
AI_CASE_RETURN(AICurlEasyRequestStateMachine_removed);
|
||||||
AI_CASE_RETURN(AICurlEasyRequestStateMachine_removed_after_finished);
|
AI_CASE_RETURN(AICurlEasyRequestStateMachine_removed_after_finished);
|
||||||
AI_CASE_RETURN(AICurlEasyRequestStateMachine_bad_file_descriptor);
|
AI_CASE_RETURN(AICurlEasyRequestStateMachine_bad_file_descriptor);
|
||||||
@@ -64,12 +77,14 @@ void AICurlEasyRequestStateMachine::initialize_impl(void)
|
|||||||
// CURL-THREAD
|
// CURL-THREAD
|
||||||
void AICurlEasyRequestStateMachine::added_to_multi_handle(AICurlEasyRequest_wat&)
|
void AICurlEasyRequestStateMachine::added_to_multi_handle(AICurlEasyRequest_wat&)
|
||||||
{
|
{
|
||||||
|
set_state(AICurlEasyRequestStateMachine_added);
|
||||||
}
|
}
|
||||||
|
|
||||||
// CURL-THREAD
|
// CURL-THREAD
|
||||||
void AICurlEasyRequestStateMachine::finished(AICurlEasyRequest_wat&)
|
void AICurlEasyRequestStateMachine::finished(AICurlEasyRequest_wat&)
|
||||||
{
|
{
|
||||||
mFinished = true;
|
mFinished = true;
|
||||||
|
set_state(AICurlEasyRequestStateMachine_finished);
|
||||||
}
|
}
|
||||||
|
|
||||||
// CURL-THREAD
|
// CURL-THREAD
|
||||||
@@ -115,11 +130,15 @@ void AICurlEasyRequestStateMachine::multiplex_impl(void)
|
|||||||
// immediately after this call.
|
// immediately after this call.
|
||||||
mAdded = true;
|
mAdded = true;
|
||||||
mCurlEasyRequest.addRequest(); // This causes the state to be changed, now or later, to
|
mCurlEasyRequest.addRequest(); // This causes the state to be changed, now or later, to
|
||||||
|
// AICurlEasyRequestStateMachine_added, then
|
||||||
|
// AICurlEasyRequestStateMachine_finished and then
|
||||||
// AICurlEasyRequestStateMachine_removed_after_finished.
|
// AICurlEasyRequestStateMachine_removed_after_finished.
|
||||||
|
|
||||||
// The first two states might be skipped thus, and the state at this point is one of
|
// The first two states might be skipped thus, and the state at this point is one of
|
||||||
// 1) AICurlEasyRequestStateMachine_waitAdded (idle)
|
// 1) AICurlEasyRequestStateMachine_waitAdded (idle)
|
||||||
// 2) AICurlEasyRequestStateMachine_removed_after_finished (running)
|
// 2) AICurlEasyRequestStateMachine_added (running)
|
||||||
|
// 3) AICurlEasyRequestStateMachine_finished (running)
|
||||||
|
// 4) AICurlEasyRequestStateMachine_removed_after_finished (running)
|
||||||
|
|
||||||
if (mTotalDelayTimeout > 0.f)
|
if (mTotalDelayTimeout > 0.f)
|
||||||
{
|
{
|
||||||
@@ -132,16 +151,22 @@ void AICurlEasyRequestStateMachine::multiplex_impl(void)
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case AICurlEasyRequestStateMachine_waitAdded:
|
case AICurlEasyRequestStateMachine_added:
|
||||||
{
|
{
|
||||||
// Nothing to do.
|
// The request was added to the multi handle. This is a no-op, which is good cause
|
||||||
idle(AICurlEasyRequestStateMachine_waitAdded);
|
// this state might be skipped anyway ;).
|
||||||
|
idle(current_state); // Wait for the next event.
|
||||||
|
|
||||||
|
// The state at this point is one of
|
||||||
|
// 1) AICurlEasyRequestStateMachine_added (idle)
|
||||||
|
// 2) AICurlEasyRequestStateMachine_finished (running)
|
||||||
|
// 3) AICurlEasyRequestStateMachine_removed_after_finished (running)
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case AICurlEasyRequestStateMachine_timedOut:
|
case AICurlEasyRequestStateMachine_timedOut:
|
||||||
{
|
{
|
||||||
// It is possible that exactly at this point the state changes into
|
// It is possible that exactly at this point the state changes into
|
||||||
// AICurlEasyRequestStateMachine_removed_after_finished, with as result that mTimedOut
|
// AICurlEasyRequestStateMachine_finished, with as result that mTimedOut
|
||||||
// is set while we will continue with that state. Hence that mTimedOut
|
// is set while we will continue with that state. Hence that mTimedOut
|
||||||
// is explicitly reset in that state.
|
// is explicitly reset in that state.
|
||||||
|
|
||||||
@@ -154,6 +179,7 @@ void AICurlEasyRequestStateMachine::multiplex_impl(void)
|
|||||||
idle(current_state); // Wait till AICurlEasyRequestStateMachine::removed_from_multi_handle() is called.
|
idle(current_state); // Wait till AICurlEasyRequestStateMachine::removed_from_multi_handle() is called.
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
case AICurlEasyRequestStateMachine_finished:
|
||||||
case AICurlEasyRequestStateMachine_removed_after_finished:
|
case AICurlEasyRequestStateMachine_removed_after_finished:
|
||||||
{
|
{
|
||||||
if (!mHandled)
|
if (!mHandled)
|
||||||
@@ -165,8 +191,7 @@ void AICurlEasyRequestStateMachine::multiplex_impl(void)
|
|||||||
{
|
{
|
||||||
// Stop the timer. Note that it's the main thread that generates timer events,
|
// Stop the timer. Note that it's the main thread that generates timer events,
|
||||||
// so we're certain that there will be no time out anymore if we reach this point.
|
// so we're certain that there will be no time out anymore if we reach this point.
|
||||||
// Note that we can't call abort() directly here, as we're a boosted statemachine.
|
mTimer->abort();
|
||||||
mTimer->request_abort();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// The request finished and either data or an error code is available.
|
// The request finished and either data or an error code is available.
|
||||||
@@ -174,6 +199,12 @@ void AICurlEasyRequestStateMachine::multiplex_impl(void)
|
|||||||
easy_request_w->processOutput();
|
easy_request_w->processOutput();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (current_state == AICurlEasyRequestStateMachine_finished)
|
||||||
|
{
|
||||||
|
idle(current_state); // Wait till AICurlEasyRequestStateMachine::removed_from_multi_handle() is called.
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
// See above.
|
// See above.
|
||||||
mTimedOut = false;
|
mTimedOut = false;
|
||||||
/* Fall-Through */
|
/* Fall-Through */
|
||||||
@@ -233,9 +264,7 @@ void AICurlEasyRequestStateMachine::finish_impl(void)
|
|||||||
// Note that even if the timer expired, it wasn't deleted because we used AIPersistentTimer; so mTimer is still valid.
|
// Note that even if the timer expired, it wasn't deleted because we used AIPersistentTimer; so mTimer is still valid.
|
||||||
// Stop the timer, if it's still running.
|
// Stop the timer, if it's still running.
|
||||||
if (!mHandled)
|
if (!mHandled)
|
||||||
{
|
mTimer->abort();
|
||||||
mTimer->request_abort();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
// Auto clean up ourselves.
|
// Auto clean up ourselves.
|
||||||
kill();
|
kill();
|
||||||
|
|||||||
@@ -35,15 +35,6 @@
|
|||||||
#include "aitimer.h"
|
#include "aitimer.h"
|
||||||
#include "aicurl.h"
|
#include "aicurl.h"
|
||||||
|
|
||||||
enum curleasyrequeststatemachine_state_type {
|
|
||||||
AICurlEasyRequestStateMachine_addRequest = AIStateMachine::max_state,
|
|
||||||
AICurlEasyRequestStateMachine_waitAdded,
|
|
||||||
AICurlEasyRequestStateMachine_timedOut, // This must be smaller than the rest, so they always overrule.
|
|
||||||
AICurlEasyRequestStateMachine_removed, // The removed states must be largest two, so they are never ignored.
|
|
||||||
AICurlEasyRequestStateMachine_removed_after_finished,
|
|
||||||
AICurlEasyRequestStateMachine_bad_file_descriptor
|
|
||||||
};
|
|
||||||
|
|
||||||
// A curl easy request state machine.
|
// A curl easy request state machine.
|
||||||
//
|
//
|
||||||
// Before calling cersm.run() initialize the object (cersm) as follows:
|
// Before calling cersm.run() initialize the object (cersm) as follows:
|
||||||
|
|||||||
@@ -832,9 +832,8 @@ class AICurlThread : public LLThread
|
|||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
static AICurlThread* sInstance;
|
static AICurlThread* sInstance;
|
||||||
LLMutex mWakeUpMutex; // Set while a thread is waking up the curl thread.
|
LLMutex mWakeUpMutex;
|
||||||
LLMutex mWakeUpFlagMutex; // Set when the curl thread is sleeping (in or about to enter select()).
|
bool mWakeUpFlag; // Protected by mWakeUpMutex.
|
||||||
bool mWakeUpFlag; // Protected by mWakeUpFlagMutex.
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
// MAIN-THREAD
|
// MAIN-THREAD
|
||||||
@@ -1068,10 +1067,11 @@ void AICurlThread::cleanup_wakeup_fds(void)
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
// OTHER THREADS
|
// MAIN-THREAD
|
||||||
void AICurlThread::wakeup_thread(bool stop_thread)
|
void AICurlThread::wakeup_thread(bool stop_thread)
|
||||||
{
|
{
|
||||||
DoutEntering(dc::curl, "AICurlThread::wakeup_thread");
|
DoutEntering(dc::curl, "AICurlThread::wakeup_thread");
|
||||||
|
llassert(is_main_thread());
|
||||||
|
|
||||||
// If we are already exiting the viewer then return immediately.
|
// If we are already exiting the viewer then return immediately.
|
||||||
if (!mRunning)
|
if (!mRunning)
|
||||||
@@ -1079,20 +1079,12 @@ void AICurlThread::wakeup_thread(bool stop_thread)
|
|||||||
|
|
||||||
// Last time we are run?
|
// Last time we are run?
|
||||||
if (stop_thread)
|
if (stop_thread)
|
||||||
mRunning = false; // Thread-safe because all other threads were already stopped.
|
mRunning = false;
|
||||||
|
|
||||||
// Stop two threads running the following code at the same time.
|
|
||||||
if (!mWakeUpMutex.tryLock())
|
|
||||||
{
|
|
||||||
// If another thread is already busy waking up the curl thread, then we don't have to.
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Try if curl thread is still awake and if so, pass the new commands directly.
|
// Try if curl thread is still awake and if so, pass the new commands directly.
|
||||||
if (mWakeUpFlagMutex.tryLock())
|
if (mWakeUpMutex.tryLock())
|
||||||
{
|
{
|
||||||
mWakeUpFlag = true;
|
mWakeUpFlag = true;
|
||||||
mWakeUpFlagMutex.unlock();
|
|
||||||
mWakeUpMutex.unlock();
|
mWakeUpMutex.unlock();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -1119,10 +1111,7 @@ void AICurlThread::wakeup_thread(bool stop_thread)
|
|||||||
{
|
{
|
||||||
len = write(mWakeUpFd_in, "!", 1);
|
len = write(mWakeUpFd_in, "!", 1);
|
||||||
if (len == -1 && errno == EAGAIN)
|
if (len == -1 && errno == EAGAIN)
|
||||||
{
|
|
||||||
mWakeUpMutex.unlock();
|
|
||||||
return; // Unread characters are still in the pipe, so no need to add more.
|
return; // Unread characters are still in the pipe, so no need to add more.
|
||||||
}
|
|
||||||
}
|
}
|
||||||
while(len == -1 && errno == EINTR);
|
while(len == -1 && errno == EINTR);
|
||||||
if (len == -1)
|
if (len == -1)
|
||||||
@@ -1131,7 +1120,6 @@ void AICurlThread::wakeup_thread(bool stop_thread)
|
|||||||
}
|
}
|
||||||
llassert_always(len == 1);
|
llassert_always(len == 1);
|
||||||
#endif
|
#endif
|
||||||
mWakeUpMutex.unlock();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
apr_status_t AICurlThread::join_thread(void)
|
apr_status_t AICurlThread::join_thread(void)
|
||||||
@@ -1259,9 +1247,9 @@ void AICurlThread::process_commands(AICurlMultiHandle_wat const& multi_handle_w)
|
|||||||
command_queue_wat command_queue_w(command_queue);
|
command_queue_wat command_queue_w(command_queue);
|
||||||
if (command_queue_w->empty())
|
if (command_queue_w->empty())
|
||||||
{
|
{
|
||||||
mWakeUpFlagMutex.lock();
|
mWakeUpMutex.lock();
|
||||||
mWakeUpFlag = false;
|
mWakeUpFlag = false;
|
||||||
mWakeUpFlagMutex.unlock();
|
mWakeUpMutex.unlock();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
// Move the next command from the queue into command_being_processed.
|
// Move the next command from the queue into command_being_processed.
|
||||||
@@ -1324,10 +1312,10 @@ void AICurlThread::run(void)
|
|||||||
// Process every command in command_queue before filling the fd_set passed to select().
|
// Process every command in command_queue before filling the fd_set passed to select().
|
||||||
for(;;)
|
for(;;)
|
||||||
{
|
{
|
||||||
mWakeUpFlagMutex.lock();
|
mWakeUpMutex.lock();
|
||||||
if (mWakeUpFlag)
|
if (mWakeUpFlag)
|
||||||
{
|
{
|
||||||
mWakeUpFlagMutex.unlock();
|
mWakeUpMutex.unlock();
|
||||||
process_commands(multi_handle_w);
|
process_commands(multi_handle_w);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@@ -1336,7 +1324,7 @@ void AICurlThread::run(void)
|
|||||||
// wakeup_thread() is also called after setting mRunning to false.
|
// wakeup_thread() is also called after setting mRunning to false.
|
||||||
if (!mRunning)
|
if (!mRunning)
|
||||||
{
|
{
|
||||||
mWakeUpFlagMutex.unlock();
|
mWakeUpMutex.unlock();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1412,7 +1400,7 @@ void AICurlThread::run(void)
|
|||||||
#endif
|
#endif
|
||||||
#endif
|
#endif
|
||||||
ready = select(nfds, read_fd_set, write_fd_set, NULL, &timeout);
|
ready = select(nfds, read_fd_set, write_fd_set, NULL, &timeout);
|
||||||
mWakeUpFlagMutex.unlock();
|
mWakeUpMutex.unlock();
|
||||||
#ifdef CWDEBUG
|
#ifdef CWDEBUG
|
||||||
#ifdef DEBUG_CURLIO
|
#ifdef DEBUG_CURLIO
|
||||||
Dout(dc::finish|cond_error_cf(ready == -1), ready);
|
Dout(dc::finish|cond_error_cf(ready == -1), ready);
|
||||||
|
|||||||
@@ -84,11 +84,6 @@ public:
|
|||||||
|
|
||||||
#include "aihttptimeout.h"
|
#include "aihttptimeout.h"
|
||||||
|
|
||||||
// If this is set, treat dc::curlio as off in the assertion below.
|
|
||||||
#if defined(CWDEBUG) || defined(DEBUG_CURLIO)
|
|
||||||
bool gCurlIo;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
namespace AICurlPrivate {
|
namespace AICurlPrivate {
|
||||||
namespace curlthread {
|
namespace curlthread {
|
||||||
|
|
||||||
@@ -174,7 +169,7 @@ bool HTTPTimeout::data_received(size_t n/*,*/
|
|||||||
// using CURLOPT_DEBUGFUNCTION. Note that mDebugIsHeadOrGetMethod is only valid when the debug channel 'curlio' is on,
|
// using CURLOPT_DEBUGFUNCTION. Note that mDebugIsHeadOrGetMethod is only valid when the debug channel 'curlio' is on,
|
||||||
// because it is set in the debug callback function.
|
// because it is set in the debug callback function.
|
||||||
// This is also normal if we received a HTTP header with an error status, since that can interrupt our upload.
|
// This is also normal if we received a HTTP header with an error status, since that can interrupt our upload.
|
||||||
Debug(llassert(upload_error_status || AICurlEasyRequest_wat(*mLockObj)->mDebugIsHeadOrGetMethod || !dc::curlio.is_on() || gCurlIo));
|
Debug(llassert(upload_error_status || AICurlEasyRequest_wat(*mLockObj)->mDebugIsHeadOrGetMethod || !dc::curlio.is_on()));
|
||||||
// 'Upload finished' detection failed, generate it now.
|
// 'Upload finished' detection failed, generate it now.
|
||||||
upload_finished();
|
upload_finished();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -133,9 +133,5 @@ class HTTPTimeout : public LLRefCount {
|
|||||||
} // namespace curlthread
|
} // namespace curlthread
|
||||||
} // namespace AICurlPrivate
|
} // namespace AICurlPrivate
|
||||||
|
|
||||||
#if defined(CWDEBUG) || defined(DEBUG_CURLIO)
|
|
||||||
extern bool gCurlIo;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|||||||
@@ -202,9 +202,6 @@ public:
|
|||||||
// The name of the derived responder object. For debugging purposes.
|
// The name of the derived responder object. For debugging purposes.
|
||||||
virtual char const* getName(void) const = 0;
|
virtual char const* getName(void) const = 0;
|
||||||
|
|
||||||
// Returning true causes AICurlEasyRequestStateMachine::multiplex_impl to be called from non-main threads.
|
|
||||||
virtual bool thread_safe_complete(void) const { return false; }
|
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
// Derived classes can override this to get the HTML headers that were received, when the message is completed.
|
// Derived classes can override this to get the HTML headers that were received, when the message is completed.
|
||||||
// Only actually called for classes that implement a needsHeaders() that returns true.
|
// Only actually called for classes that implement a needsHeaders() that returns true.
|
||||||
|
|||||||
@@ -83,24 +83,6 @@ LLURLRequest::LLURLRequest(LLURLRequest::ERequestAction action, std::string cons
|
|||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
bool LLURLRequest::thread_safe_impl(void) const
|
|
||||||
{
|
|
||||||
if (base_state() == bs_run &&
|
|
||||||
(mRunState == AICurlEasyRequestStateMachine_removed_after_finished ||
|
|
||||||
mRunState == AICurlEasyRequestStateMachine_addRequest) &&
|
|
||||||
mResponder->thread_safe_complete())
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// AICurlEasyRequestStateMachine::initialize_impl is thread safe because the statemachine
|
|
||||||
// will only just have been created and no other thread can know of this instance.
|
|
||||||
if (base_state() == bs_initialize)
|
|
||||||
return true;
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
void LLURLRequest::initialize_impl(void)
|
void LLURLRequest::initialize_impl(void)
|
||||||
{
|
{
|
||||||
// If the header is "Pragma" with no value, the caller intends to
|
// If the header is "Pragma" with no value, the caller intends to
|
||||||
|
|||||||
@@ -122,9 +122,6 @@ class LLURLRequest : public AICurlEasyRequestStateMachine {
|
|||||||
protected:
|
protected:
|
||||||
// Handle initializing the object.
|
// Handle initializing the object.
|
||||||
/*virtual*/ void initialize_impl(void);
|
/*virtual*/ void initialize_impl(void);
|
||||||
|
|
||||||
// Return true if current state is thread safe.
|
|
||||||
/*virtual*/ bool thread_safe_impl(void) const;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // LL_LLURLREQUEST_H
|
#endif // LL_LLURLREQUEST_H
|
||||||
|
|||||||
@@ -260,6 +260,10 @@ extern S32 gStartImageHeight;
|
|||||||
// local globals
|
// local globals
|
||||||
//
|
//
|
||||||
|
|
||||||
|
#if defined(CWDEBUG) || defined(DEBUG_CURLIO)
|
||||||
|
static bool gCurlIo;
|
||||||
|
#endif
|
||||||
|
|
||||||
static LLHost gAgentSimHost;
|
static LLHost gAgentSimHost;
|
||||||
static BOOL gSkipOptionalUpdate = FALSE;
|
static BOOL gSkipOptionalUpdate = FALSE;
|
||||||
|
|
||||||
|
|||||||
@@ -338,8 +338,6 @@ public:
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/*virtual*/ bool thread_safe_complete(void) const { return true; }
|
|
||||||
|
|
||||||
/*virtual*/ void completedRaw(U32 status, const std::string& reason,
|
/*virtual*/ void completedRaw(U32 status, const std::string& reason,
|
||||||
const LLChannelDescriptors& channels,
|
const LLChannelDescriptors& channels,
|
||||||
const buffer_ptr_t& buffer)
|
const buffer_ptr_t& buffer)
|
||||||
|
|||||||
Reference in New Issue
Block a user