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)
|
||||
{
|
||||
DoutEntering(dc::statemachine, "AIStateMachine::run(" << (void*)parent << ", " << (parent ? parent->state_str(new_parent_state) : "NA") <<
|
||||
", " << abort_parent << ", " << on_abort_signal_parent << ") [" << (void*)this << "]");
|
||||
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);
|
||||
@@ -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.
|
||||
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();
|
||||
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.
|
||||
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();
|
||||
locked_cont();
|
||||
}
|
||||
@@ -149,11 +148,9 @@ void AIStateMachine::run(callback_type::signal_type::slot_type const& slot)
|
||||
void AIStateMachine::idle(void)
|
||||
{
|
||||
DoutEntering(dc::statemachine, "AIStateMachine::idle() [" << (void*)this << "]");
|
||||
llassert(mInsideMultiplexImpl);
|
||||
mSetStateLock.lock();
|
||||
llassert(is_main_thread());
|
||||
llassert(!mIdle);
|
||||
mIdle = true;
|
||||
mSetStateLock.unlock();
|
||||
mSleep = 0;
|
||||
#ifdef SHOW_ASSERT
|
||||
mCalledThreadUnsafeIdle = true;
|
||||
@@ -163,12 +160,9 @@ void AIStateMachine::idle(void)
|
||||
void AIStateMachine::idle(state_type current_run_state)
|
||||
{
|
||||
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().
|
||||
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(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)
|
||||
@@ -202,7 +196,6 @@ void AIStateMachine::locked_cont(void)
|
||||
mIdle = false;
|
||||
bool not_active = mActive == as_idle;
|
||||
mIdleActive.unlock();
|
||||
mBoost = thread_safe_impl();
|
||||
// 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
|
||||
@@ -217,15 +210,8 @@ void AIStateMachine::locked_cont(void)
|
||||
// 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().
|
||||
// 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();
|
||||
if (mBoost)
|
||||
{
|
||||
// Run statemachine until it is finished or idle again.
|
||||
multiplex(0);
|
||||
}
|
||||
else if (not_active)
|
||||
if (not_active)
|
||||
{
|
||||
AIWriteAccess<csme_type> csme_w(sContinuedStateMachinesAndMainloopEnabled);
|
||||
// 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.
|
||||
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
|
||||
// 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
|
||||
@@ -290,11 +279,8 @@ void AIStateMachine::set_state(state_type state)
|
||||
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.
|
||||
llassert(!mCalledThreadUnsafeIdle || AIThreadID::in_main_thread());
|
||||
llassert(!mCalledThreadUnsafeIdle || is_main_thread());
|
||||
|
||||
// Change mRunState to the requested value.
|
||||
if (mRunState != state)
|
||||
@@ -307,15 +293,7 @@ void AIStateMachine::set_state(state_type state)
|
||||
if (mIdle)
|
||||
locked_cont(); // This unlocks mSetStateLock.
|
||||
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();
|
||||
}
|
||||
|
||||
// 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,
|
||||
@@ -476,7 +454,6 @@ void AIStateMachine::multiplex(U64 current_time)
|
||||
// amount of time has passed.
|
||||
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)
|
||||
@@ -498,90 +475,11 @@ void AIStateMachine::multiplex(U64 current_time)
|
||||
{
|
||||
mAborted = false;
|
||||
mState = bs_run;
|
||||
#ifdef SHOW_ASSERT
|
||||
mInsideMultiplexImpl = true;
|
||||
#endif
|
||||
initialize_impl();
|
||||
#ifdef SHOW_ASSERT
|
||||
mInsideMultiplexImpl = false;
|
||||
#endif
|
||||
if (mAborted || mState != bs_run)
|
||||
{
|
||||
mBoost = false;
|
||||
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
|
||||
@@ -692,10 +590,6 @@ void AIStateMachine::flush(void)
|
||||
for (active_statemachines_type::iterator iter = active_statemachines.begin(); iter != active_statemachines.end(); ++iter)
|
||||
{
|
||||
AIStateMachine& statemachine(iter->statemachine());
|
||||
#ifdef SHOW_ASSERT
|
||||
// Make assertions happy.
|
||||
statemachine.mInsideMultiplexImpl = true;
|
||||
#endif
|
||||
if (statemachine.abortable())
|
||||
{
|
||||
// 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.
|
||||
//
|
||||
class AIStateMachine {
|
||||
protected:
|
||||
//! The type of mState
|
||||
enum base_state_type {
|
||||
bs_initialize,
|
||||
@@ -214,15 +213,12 @@ class AIStateMachine {
|
||||
base_state_type mState; //!< State of the base class.
|
||||
bool mIdle; //!< True if this state machine is not running.
|
||||
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 (not used for boosted SM).
|
||||
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 (not used for boosted SM).
|
||||
LLMutex mMultiplexMutex; //!< Used for boosted state machines, locked during multiplexing.
|
||||
active_type mActive; //!< Whether statemachine is idle, queued to be added to the active list, or already on the active list.
|
||||
S64 mSleep; //!< Non-zero while the state machine is sleeping.
|
||||
LLMutex mIdleActive; //!< Used for atomic operations on the pair mIdle / mActive.
|
||||
#ifdef SHOW_ASSERT
|
||||
AIThreadID mContThread; //!< Thread that last called locked_cont().
|
||||
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
|
||||
|
||||
// Callback facilities.
|
||||
@@ -254,11 +250,10 @@ class AIStateMachine {
|
||||
|
||||
public:
|
||||
//! 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
|
||||
mContThread(AIThreadID::none), mCalledThreadUnsafeIdle(false),
|
||||
, mContThread(AIThreadID::none), mCalledThreadUnsafeIdle(false)
|
||||
#endif
|
||||
mParent(NULL), mCallback(NULL), mRunState(0)
|
||||
{ }
|
||||
|
||||
protected:
|
||||
@@ -379,10 +374,6 @@ class AIStateMachine {
|
||||
//! Return a stringified state, for debugging purposes.
|
||||
char const* state_str(state_type state);
|
||||
|
||||
protected:
|
||||
//! Return mState.
|
||||
base_state_type base_state(void) const { return mState; }
|
||||
|
||||
private:
|
||||
static void add_continued_statemachines(AIReadAccess<csme_type>& csme_r);
|
||||
static void dowork(void);
|
||||
@@ -421,10 +412,6 @@ class AIStateMachine {
|
||||
// Handle cleaning up from initialization (or post abort) state.
|
||||
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.
|
||||
virtual char const* state_str_impl(state_type run_state) const = 0;
|
||||
};
|
||||
|
||||
@@ -33,8 +33,7 @@
|
||||
|
||||
enum timer_state_type {
|
||||
AITimer_start = AIStateMachine::max_state,
|
||||
AITimer_expired,
|
||||
AITimer_abort
|
||||
AITimer_expired
|
||||
};
|
||||
|
||||
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_expired);
|
||||
AI_CASE_RETURN(AITimer_abort);
|
||||
}
|
||||
return "UNKNOWN STATE";
|
||||
}
|
||||
@@ -54,12 +52,6 @@ void AITimer::initialize_impl(void)
|
||||
set_state(AITimer_start);
|
||||
}
|
||||
|
||||
void AITimer::request_abort(void)
|
||||
{
|
||||
mFrameTimer.cancel();
|
||||
set_state(AITimer_abort);
|
||||
}
|
||||
|
||||
void AITimer::expired(void)
|
||||
{
|
||||
set_state(AITimer_expired);
|
||||
@@ -72,7 +64,7 @@ void AITimer::multiplex_impl(void)
|
||||
case AITimer_start:
|
||||
{
|
||||
mFrameTimer.create(mInterval, boost::bind(&AITimer::expired, this));
|
||||
idle(AITimer_start);
|
||||
idle();
|
||||
break;
|
||||
}
|
||||
case AITimer_expired:
|
||||
@@ -80,11 +72,6 @@ void AITimer::multiplex_impl(void)
|
||||
finish();
|
||||
break;
|
||||
}
|
||||
case AITimer_abort:
|
||||
{
|
||||
abort();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -82,11 +82,6 @@ class AITimer : public AIStateMachine {
|
||||
*/
|
||||
F64 getInterval(void) const { return mInterval; }
|
||||
|
||||
/**
|
||||
* @brief Abort the timer from another thread.
|
||||
*/
|
||||
void request_abort(void);
|
||||
|
||||
protected:
|
||||
// Call finish() (or abort()), not delete.
|
||||
/*virtual*/ ~AITimer() { DoutEntering(dc::statemachine, "~AITimer() [" << (void*)this << "]"); mFrameTimer.cancel(); }
|
||||
|
||||
@@ -33,13 +33,26 @@
|
||||
#include "aihttptimeoutpolicy.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
|
||||
{
|
||||
switch(run_state)
|
||||
{
|
||||
AI_CASE_RETURN(AICurlEasyRequestStateMachine_addRequest);
|
||||
AI_CASE_RETURN(AICurlEasyRequestStateMachine_waitAdded);
|
||||
AI_CASE_RETURN(AICurlEasyRequestStateMachine_added);
|
||||
AI_CASE_RETURN(AICurlEasyRequestStateMachine_timedOut);
|
||||
AI_CASE_RETURN(AICurlEasyRequestStateMachine_finished);
|
||||
AI_CASE_RETURN(AICurlEasyRequestStateMachine_removed);
|
||||
AI_CASE_RETURN(AICurlEasyRequestStateMachine_removed_after_finished);
|
||||
AI_CASE_RETURN(AICurlEasyRequestStateMachine_bad_file_descriptor);
|
||||
@@ -64,12 +77,14 @@ void AICurlEasyRequestStateMachine::initialize_impl(void)
|
||||
// CURL-THREAD
|
||||
void AICurlEasyRequestStateMachine::added_to_multi_handle(AICurlEasyRequest_wat&)
|
||||
{
|
||||
set_state(AICurlEasyRequestStateMachine_added);
|
||||
}
|
||||
|
||||
// CURL-THREAD
|
||||
void AICurlEasyRequestStateMachine::finished(AICurlEasyRequest_wat&)
|
||||
{
|
||||
mFinished = true;
|
||||
set_state(AICurlEasyRequestStateMachine_finished);
|
||||
}
|
||||
|
||||
// CURL-THREAD
|
||||
@@ -115,11 +130,15 @@ void AICurlEasyRequestStateMachine::multiplex_impl(void)
|
||||
// immediately after this call.
|
||||
mAdded = true;
|
||||
mCurlEasyRequest.addRequest(); // This causes the state to be changed, now or later, to
|
||||
// AICurlEasyRequestStateMachine_added, then
|
||||
// AICurlEasyRequestStateMachine_finished and then
|
||||
// AICurlEasyRequestStateMachine_removed_after_finished.
|
||||
|
||||
// The first two states might be skipped thus, and the state at this point is one of
|
||||
// 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)
|
||||
{
|
||||
@@ -132,16 +151,22 @@ void AICurlEasyRequestStateMachine::multiplex_impl(void)
|
||||
}
|
||||
break;
|
||||
}
|
||||
case AICurlEasyRequestStateMachine_waitAdded:
|
||||
case AICurlEasyRequestStateMachine_added:
|
||||
{
|
||||
// Nothing to do.
|
||||
idle(AICurlEasyRequestStateMachine_waitAdded);
|
||||
// The request was added to the multi handle. This is a no-op, which is good cause
|
||||
// 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;
|
||||
}
|
||||
case AICurlEasyRequestStateMachine_timedOut:
|
||||
{
|
||||
// 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 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.
|
||||
break;
|
||||
}
|
||||
case AICurlEasyRequestStateMachine_finished:
|
||||
case AICurlEasyRequestStateMachine_removed_after_finished:
|
||||
{
|
||||
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,
|
||||
// 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->request_abort();
|
||||
mTimer->abort();
|
||||
}
|
||||
|
||||
// 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();
|
||||
}
|
||||
|
||||
if (current_state == AICurlEasyRequestStateMachine_finished)
|
||||
{
|
||||
idle(current_state); // Wait till AICurlEasyRequestStateMachine::removed_from_multi_handle() is called.
|
||||
break;
|
||||
}
|
||||
|
||||
// See above.
|
||||
mTimedOut = false;
|
||||
/* 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.
|
||||
// Stop the timer, if it's still running.
|
||||
if (!mHandled)
|
||||
{
|
||||
mTimer->request_abort();
|
||||
}
|
||||
mTimer->abort();
|
||||
}
|
||||
// Auto clean up ourselves.
|
||||
kill();
|
||||
|
||||
@@ -35,15 +35,6 @@
|
||||
#include "aitimer.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.
|
||||
//
|
||||
// Before calling cersm.run() initialize the object (cersm) as follows:
|
||||
|
||||
@@ -832,9 +832,8 @@ class AICurlThread : public LLThread
|
||||
{
|
||||
public:
|
||||
static AICurlThread* sInstance;
|
||||
LLMutex mWakeUpMutex; // Set while a thread is waking up the curl thread.
|
||||
LLMutex mWakeUpFlagMutex; // Set when the curl thread is sleeping (in or about to enter select()).
|
||||
bool mWakeUpFlag; // Protected by mWakeUpFlagMutex.
|
||||
LLMutex mWakeUpMutex;
|
||||
bool mWakeUpFlag; // Protected by mWakeUpMutex.
|
||||
|
||||
public:
|
||||
// MAIN-THREAD
|
||||
@@ -1068,10 +1067,11 @@ void AICurlThread::cleanup_wakeup_fds(void)
|
||||
#endif
|
||||
}
|
||||
|
||||
// OTHER THREADS
|
||||
// MAIN-THREAD
|
||||
void AICurlThread::wakeup_thread(bool stop_thread)
|
||||
{
|
||||
DoutEntering(dc::curl, "AICurlThread::wakeup_thread");
|
||||
llassert(is_main_thread());
|
||||
|
||||
// If we are already exiting the viewer then return immediately.
|
||||
if (!mRunning)
|
||||
@@ -1079,20 +1079,12 @@ void AICurlThread::wakeup_thread(bool stop_thread)
|
||||
|
||||
// Last time we are run?
|
||||
if (stop_thread)
|
||||
mRunning = false; // Thread-safe because all other threads were already stopped.
|
||||
|
||||
// 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;
|
||||
}
|
||||
mRunning = false;
|
||||
|
||||
// Try if curl thread is still awake and if so, pass the new commands directly.
|
||||
if (mWakeUpFlagMutex.tryLock())
|
||||
if (mWakeUpMutex.tryLock())
|
||||
{
|
||||
mWakeUpFlag = true;
|
||||
mWakeUpFlagMutex.unlock();
|
||||
mWakeUpMutex.unlock();
|
||||
return;
|
||||
}
|
||||
@@ -1119,10 +1111,7 @@ void AICurlThread::wakeup_thread(bool stop_thread)
|
||||
{
|
||||
len = write(mWakeUpFd_in, "!", 1);
|
||||
if (len == -1 && errno == EAGAIN)
|
||||
{
|
||||
mWakeUpMutex.unlock();
|
||||
return; // Unread characters are still in the pipe, so no need to add more.
|
||||
}
|
||||
}
|
||||
while(len == -1 && errno == EINTR);
|
||||
if (len == -1)
|
||||
@@ -1131,7 +1120,6 @@ void AICurlThread::wakeup_thread(bool stop_thread)
|
||||
}
|
||||
llassert_always(len == 1);
|
||||
#endif
|
||||
mWakeUpMutex.unlock();
|
||||
}
|
||||
|
||||
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);
|
||||
if (command_queue_w->empty())
|
||||
{
|
||||
mWakeUpFlagMutex.lock();
|
||||
mWakeUpMutex.lock();
|
||||
mWakeUpFlag = false;
|
||||
mWakeUpFlagMutex.unlock();
|
||||
mWakeUpMutex.unlock();
|
||||
break;
|
||||
}
|
||||
// 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().
|
||||
for(;;)
|
||||
{
|
||||
mWakeUpFlagMutex.lock();
|
||||
mWakeUpMutex.lock();
|
||||
if (mWakeUpFlag)
|
||||
{
|
||||
mWakeUpFlagMutex.unlock();
|
||||
mWakeUpMutex.unlock();
|
||||
process_commands(multi_handle_w);
|
||||
continue;
|
||||
}
|
||||
@@ -1336,7 +1324,7 @@ void AICurlThread::run(void)
|
||||
// wakeup_thread() is also called after setting mRunning to false.
|
||||
if (!mRunning)
|
||||
{
|
||||
mWakeUpFlagMutex.unlock();
|
||||
mWakeUpMutex.unlock();
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -1412,7 +1400,7 @@ void AICurlThread::run(void)
|
||||
#endif
|
||||
#endif
|
||||
ready = select(nfds, read_fd_set, write_fd_set, NULL, &timeout);
|
||||
mWakeUpFlagMutex.unlock();
|
||||
mWakeUpMutex.unlock();
|
||||
#ifdef CWDEBUG
|
||||
#ifdef DEBUG_CURLIO
|
||||
Dout(dc::finish|cond_error_cf(ready == -1), ready);
|
||||
|
||||
@@ -84,11 +84,6 @@ public:
|
||||
|
||||
#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 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,
|
||||
// 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.
|
||||
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();
|
||||
}
|
||||
|
||||
@@ -133,9 +133,5 @@ class HTTPTimeout : public LLRefCount {
|
||||
} // namespace curlthread
|
||||
} // namespace AICurlPrivate
|
||||
|
||||
#if defined(CWDEBUG) || defined(DEBUG_CURLIO)
|
||||
extern bool gCurlIo;
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
@@ -202,9 +202,6 @@ public:
|
||||
// The name of the derived responder object. For debugging purposes.
|
||||
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:
|
||||
// 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.
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
// If the header is "Pragma" with no value, the caller intends to
|
||||
|
||||
@@ -122,9 +122,6 @@ class LLURLRequest : public AICurlEasyRequestStateMachine {
|
||||
protected:
|
||||
// Handle initializing the object.
|
||||
/*virtual*/ void initialize_impl(void);
|
||||
|
||||
// Return true if current state is thread safe.
|
||||
/*virtual*/ bool thread_safe_impl(void) const;
|
||||
};
|
||||
|
||||
#endif // LL_LLURLREQUEST_H
|
||||
|
||||
@@ -260,6 +260,10 @@ extern S32 gStartImageHeight;
|
||||
// local globals
|
||||
//
|
||||
|
||||
#if defined(CWDEBUG) || defined(DEBUG_CURLIO)
|
||||
static bool gCurlIo;
|
||||
#endif
|
||||
|
||||
static LLHost gAgentSimHost;
|
||||
static BOOL gSkipOptionalUpdate = FALSE;
|
||||
|
||||
|
||||
@@ -338,8 +338,6 @@ public:
|
||||
}
|
||||
#endif
|
||||
|
||||
/*virtual*/ bool thread_safe_complete(void) const { return true; }
|
||||
|
||||
/*virtual*/ void completedRaw(U32 status, const std::string& reason,
|
||||
const LLChannelDescriptors& channels,
|
||||
const buffer_ptr_t& buffer)
|
||||
|
||||
Reference in New Issue
Block a user