diff --git a/indra/llcorehttp/_httplibcurl.cpp b/indra/llcorehttp/_httplibcurl.cpp index 39abca12c5c16013250d1e98ded51c22c5b188dc..3c69ae1c9698a4789d94078852a26b9f097e6c3b 100644 --- a/indra/llcorehttp/_httplibcurl.cpp +++ b/indra/llcorehttp/_httplibcurl.cpp @@ -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* diff --git a/indra/llcorehttp/_httplibcurl.h b/indra/llcorehttp/_httplibcurl.h index 69f7bb2b6d2ee2f2fccfdc1272fec3491f9c9ee9..53972b1ffac31617c2920c3e865b19c4889e5e87 100644 --- a/indra/llcorehttp/_httplibcurl.h +++ b/indra/llcorehttp/_httplibcurl.h @@ -85,6 +85,9 @@ class HttpLibcurl 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. diff --git a/indra/llcorehttp/_httpopcancel.cpp b/indra/llcorehttp/_httpopcancel.cpp index ad624d2e57154d6189cf7cf26b02ffc27bd44f7d..5c1f484109a80897dc32fd647ba47d26d25fa16e 100644 --- a/indra/llcorehttp/_httpopcancel.cpp +++ b/indra/llcorehttp/_httpopcancel.cpp @@ -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(); } diff --git a/indra/llcorehttp/_httppolicy.cpp b/indra/llcorehttp/_httppolicy.cpp index 4350ff617bbedf519fd646be6b2abee56b7ce611..1b10805b7255439b3c4c533858f50a0410f10ad5 100644 --- a/indra/llcorehttp/_httppolicy.cpp +++ b/indra/llcorehttp/_httppolicy.cpp @@ -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); diff --git a/indra/llcorehttp/_httppolicy.h b/indra/llcorehttp/_httppolicy.h index 90bb3b571db7722251e67a4eed1889c2959fbe2a..a02bf084c154e75ced85707b70cdb3aca8fc5298 100644 --- a/indra/llcorehttp/_httppolicy.h +++ b/indra/llcorehttp/_httppolicy.h @@ -92,6 +92,9 @@ class HttpPolicy // 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 diff --git a/indra/llcorehttp/_httpservice.cpp b/indra/llcorehttp/_httpservice.cpp index 92c15b5b8f686ece0e7395d52cba54a04a60890f..f7d9813db0376761e743f70da6240260766cb0de 100644 --- a/indra/llcorehttp/_httpservice.cpp +++ b/indra/llcorehttp/_httpservice.cpp @@ -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() { diff --git a/indra/llcorehttp/_httpservice.h b/indra/llcorehttp/_httpservice.h index d67e6e95a5f8c33c2227483becb437686955cae2..d24c497ca96a67dd99b00a3a424e863df39e0a23 100644 --- a/indra/llcorehttp/_httpservice.h +++ b/indra/llcorehttp/_httpservice.h @@ -154,6 +154,14 @@ class HttpService /// 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() { diff --git a/indra/newview/lltexturefetch.cpp b/indra/newview/lltexturefetch.cpp index 36b878d6f24251723fc49f69eaa38273a0632aa8..b30b25e5436d7f82905abab1e4cacbaabc52c667 100755 --- a/indra/newview/lltexturefetch.cpp +++ b/indra/newview/lltexturefetch.cpp @@ -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);