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
parent
7997a9c4e5
commit
2d7b7de203
|
|
@ -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*
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
Loading…
Reference in New Issue