Skip to content
Snippets Groups Projects
Commit 2d7b7de2 authored by Monty Brandenberg's avatar Monty Brandenberg
Browse files

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.
parent 7997a9c4
No related branches found
No related tags found
No related merge requests found
...@@ -189,6 +189,30 @@ void HttpLibcurl::addOp(HttpOpRequest * op) ...@@ -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. // *NOTE: cancelRequest logic parallels completeRequest logic.
// Keep them synchronized as necessary. Caller is expected to // Keep them synchronized as necessary. Caller is expected to
// remove to op from the active list and release the op *after* // remove to op from the active list and release the op *after*
......
...@@ -85,6 +85,9 @@ class HttpLibcurl ...@@ -85,6 +85,9 @@ class HttpLibcurl
int getActiveCount() const; int getActiveCount() const;
int getActiveCountInClass(int policy_class) const; int getActiveCountInClass(int policy_class) const;
// Shadows HttpService's method
bool cancel(HttpHandle handle);
protected: protected:
/// Invoked when libcurl has indicated a request has been processed /// Invoked when libcurl has indicated a request has been processed
/// to completion and we need to move the request to a new state. /// to completion and we need to move the request to a new state.
......
...@@ -61,7 +61,11 @@ HttpOpCancel::~HttpOpCancel() ...@@ -61,7 +61,11 @@ HttpOpCancel::~HttpOpCancel()
void HttpOpCancel::stageFromRequest(HttpService * service) void HttpOpCancel::stageFromRequest(HttpService * service)
{ {
// *FIXME: Need cancel functionality into services if (! service->cancel(mHandle))
{
mStatus = HttpStatus(HttpStatus::LLCORE, HE_HANDLE_NOT_FOUND);
}
addAsReply(); addAsReply();
} }
......
...@@ -231,9 +231,12 @@ bool HttpPolicy::changePriority(HttpHandle handle, HttpRequest::priority_t prior ...@@ -231,9 +231,12 @@ bool HttpPolicy::changePriority(HttpHandle handle, HttpRequest::priority_t prior
for (int policy_class(0); policy_class < mActiveClasses; ++policy_class) for (int policy_class(0); policy_class < mActiveClasses; ++policy_class)
{ {
State & state(mState[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 // 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;) for (HttpReadyQueue::container_type::iterator iter(c.begin()); c.end() != iter;)
{ {
HttpReadyQueue::container_type::iterator cur(iter++); HttpReadyQueue::container_type::iterator cur(iter++);
...@@ -253,6 +256,48 @@ bool HttpPolicy::changePriority(HttpHandle handle, HttpRequest::priority_t prior ...@@ -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) bool HttpPolicy::stageAfterCompletion(HttpOpRequest * op)
{ {
static const HttpStatus cant_connect(HttpStatus::EXT_CURL_EASY, CURLE_COULDNT_CONNECT); static const HttpStatus cant_connect(HttpStatus::EXT_CURL_EASY, CURLE_COULDNT_CONNECT);
......
...@@ -92,6 +92,9 @@ class HttpPolicy ...@@ -92,6 +92,9 @@ class HttpPolicy
// Shadows HttpService's method // Shadows HttpService's method
bool changePriority(HttpHandle handle, HttpRequest::priority_t priority); 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 /// When transport is finished with an op and takes it off the
/// active queue, it is delivered here for dispatch. Policy /// active queue, it is delivered here for dispatch. Policy
/// may send it back to the ready/retry queues if it needs another /// 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 ...@@ -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. /// Threading: callable by worker thread.
void HttpService::shutdown() void HttpService::shutdown()
{ {
......
...@@ -154,6 +154,14 @@ class HttpService ...@@ -154,6 +154,14 @@ class HttpService
/// Threading: callable by worker thread. /// Threading: callable by worker thread.
bool changePriority(HttpHandle handle, HttpRequest::priority_t priority); 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. /// Threading: callable by worker thread.
HttpPolicy & getPolicy() HttpPolicy & getPolicy()
{ {
......
...@@ -1560,8 +1560,24 @@ bool LLTextureFetchWorker::doWork(S32 param) ...@@ -1560,8 +1560,24 @@ bool LLTextureFetchWorker::doWork(S32 param)
if(FETCHING_TIMEOUT < mRequestedTimer.getElapsedTimeF32()) if(FETCHING_TIMEOUT < mRequestedTimer.getElapsedTimeF32())
{ {
//timeout, abort. //timeout, abort.
mState = DONE; LL_WARNS("Texture") << "Fetch of texture " << mID << " timed out after "
return true; << 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); setPriority(LLWorkerThread::PRIORITY_LOW | mWorkPriority);
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment