More integration work for texture fetch timeouts.

The fetch state machine received a new timeout during the WAIT_HTTP_REQ
state.  For the integration, rather than jump the state to done, we issue
a request cancel and let the notification plumbing do the rest without
any race conditions or special-case logic.
master
Monty Brandenberg 2012-07-03 13:06:46 -04:00
parent 7997a9c4e5
commit 2d7b7de203
8 changed files with 133 additions and 5 deletions

View File

@ -189,6 +189,30 @@ void HttpLibcurl::addOp(HttpOpRequest * op)
}
// Implements the transport part of any cancel operation.
// See if the handle is an active operation and if so,
// use the more complicated transport-based cancelation
// method to kill the request.
bool HttpLibcurl::cancel(HttpHandle handle)
{
HttpOpRequest * op(static_cast<HttpOpRequest *>(handle));
active_set_t::iterator it(mActiveOps.find(op));
if (mActiveOps.end() == it)
{
return false;
}
// Cancel request
cancelRequest(op);
// Drop references
mActiveOps.erase(it);
op->release();
return true;
}
// *NOTE: cancelRequest logic parallels completeRequest logic.
// Keep them synchronized as necessary. Caller is expected to
// remove to op from the active list and release the op *after*

View File

@ -85,6 +85,9 @@ public:
int getActiveCount() const;
int getActiveCountInClass(int policy_class) const;
// Shadows HttpService's method
bool cancel(HttpHandle handle);
protected:
/// Invoked when libcurl has indicated a request has been processed
/// to completion and we need to move the request to a new state.

View File

@ -61,7 +61,11 @@ HttpOpCancel::~HttpOpCancel()
void HttpOpCancel::stageFromRequest(HttpService * service)
{
// *FIXME: Need cancel functionality into services
if (! service->cancel(mHandle))
{
mStatus = HttpStatus(HttpStatus::LLCORE, HE_HANDLE_NOT_FOUND);
}
addAsReply();
}

View File

@ -231,9 +231,12 @@ bool HttpPolicy::changePriority(HttpHandle handle, HttpRequest::priority_t prior
for (int policy_class(0); policy_class < mActiveClasses; ++policy_class)
{
State & state(mState[policy_class]);
HttpReadyQueue::container_type & c(state.mReadyQueue.get_container());
// We don't scan retry queue because a priority change there
// is meaningless. The request will be issued based on retry
// intervals not priority value, which is now moot.
// Scan ready queue for requests that match policy
HttpReadyQueue::container_type & c(state.mReadyQueue.get_container());
for (HttpReadyQueue::container_type::iterator iter(c.begin()); c.end() != iter;)
{
HttpReadyQueue::container_type::iterator cur(iter++);
@ -253,6 +256,48 @@ bool HttpPolicy::changePriority(HttpHandle handle, HttpRequest::priority_t prior
}
bool HttpPolicy::cancel(HttpHandle handle)
{
for (int policy_class(0); policy_class < mActiveClasses; ++policy_class)
{
State & state(mState[policy_class]);
// Scan retry queue
HttpRetryQueue::container_type & c1(state.mRetryQueue.get_container());
for (HttpRetryQueue::container_type::iterator iter(c1.begin()); c1.end() != iter;)
{
HttpRetryQueue::container_type::iterator cur(iter++);
if (static_cast<HttpHandle>(*cur) == handle)
{
HttpOpRequest * op(*cur);
c1.erase(cur); // All iterators are now invalidated
op->cancel();
op->release();
return true;
}
}
// Scan ready queue
HttpReadyQueue::container_type & c2(state.mReadyQueue.get_container());
for (HttpReadyQueue::container_type::iterator iter(c2.begin()); c2.end() != iter;)
{
HttpReadyQueue::container_type::iterator cur(iter++);
if (static_cast<HttpHandle>(*cur) == handle)
{
HttpOpRequest * op(*cur);
c2.erase(cur); // All iterators are now invalidated
op->cancel();
op->release();
return true;
}
}
}
return false;
}
bool HttpPolicy::stageAfterCompletion(HttpOpRequest * op)
{
static const HttpStatus cant_connect(HttpStatus::EXT_CURL_EASY, CURLE_COULDNT_CONNECT);

View File

@ -92,6 +92,9 @@ public:
// Shadows HttpService's method
bool changePriority(HttpHandle handle, HttpRequest::priority_t priority);
// Shadows HttpService's method as well
bool cancel(HttpHandle handle);
/// When transport is finished with an op and takes it off the
/// active queue, it is delivered here for dispatch. Policy
/// may send it back to the ready/retry queues if it needs another

View File

@ -219,6 +219,31 @@ bool HttpService::changePriority(HttpHandle handle, HttpRequest::priority_t prio
}
/// Try to find the given request handle on any of the request
/// queues and cancel the operation.
///
/// @return True if the request was canceled.
///
/// Threading: callable by worker thread.
bool HttpService::cancel(HttpHandle handle)
{
bool canceled(false);
// Request can't be on request queue so skip that.
// Check the policy component's queues first
canceled = mPolicy->cancel(handle);
if (! canceled)
{
// If that didn't work, check transport's.
canceled = mTransport->cancel(handle);
}
return canceled;
}
/// Threading: callable by worker thread.
void HttpService::shutdown()
{

View File

@ -154,6 +154,14 @@ public:
/// Threading: callable by worker thread.
bool changePriority(HttpHandle handle, HttpRequest::priority_t priority);
/// Try to find the given request handle on any of the request
/// queues and cancel the operation.
///
/// @return True if the request was found and canceled.
///
/// Threading: callable by worker thread.
bool cancel(HttpHandle handle);
/// Threading: callable by worker thread.
HttpPolicy & getPolicy()
{

View File

@ -1560,8 +1560,24 @@ bool LLTextureFetchWorker::doWork(S32 param)
if(FETCHING_TIMEOUT < mRequestedTimer.getElapsedTimeF32())
{
//timeout, abort.
mState = DONE;
return true;
LL_WARNS("Texture") << "Fetch of texture " << mID << " timed out after "
<< mRequestedTimer.getElapsedTimeF32()
<< " seconds. Canceling request." << LL_ENDL;
if (LLCORE_HTTP_HANDLE_INVALID != mHttpHandle)
{
// Issue cancel on any outstanding request. Asynchronous
// so cancel may not actually take effect if operation is
// complete & queued. Either way, notification will
// complete and the request can be transitioned.
mFetcher->mHttpRequest->requestCancel(mHttpHandle, NULL);
}
else
{
// Shouldn't happen but if it does, cancel quickly.
mState = DONE;
return true;
}
}
setPriority(LLWorkerThread::PRIORITY_LOW | mWorkPriority);