summaryrefslogtreecommitdiff
path: root/indra
diff options
context:
space:
mode:
authorMonty Brandenberg <monty@lindenlab.com>2014-09-19 15:34:09 -0400
committerMonty Brandenberg <monty@lindenlab.com>2014-09-19 15:34:09 -0400
commit79ab7c20703c092a4416a4f9a885e0246fc17ee0 (patch)
tree66c8adf1bc148dc8a2e5a422c44bc77eea52284e /indra
parent1294825de61d3dadecea74eb18256269da09b033 (diff)
Introduce libcurl handle cache. Create a private cache
of used handles and a fast handle factory that's thread- correct.
Diffstat (limited to 'indra')
-rwxr-xr-xindra/llcorehttp/_httplibcurl.cpp83
-rwxr-xr-xindra/llcorehttp/_httplibcurl.h78
-rwxr-xr-xindra/llcorehttp/_httpoprequest.cpp5
3 files changed, 158 insertions, 8 deletions
diff --git a/indra/llcorehttp/_httplibcurl.cpp b/indra/llcorehttp/_httplibcurl.cpp
index cfbe0fd2bb..81b44ab90b 100755
--- a/indra/llcorehttp/_httplibcurl.cpp
+++ b/indra/llcorehttp/_httplibcurl.cpp
@@ -51,6 +51,7 @@ namespace LLCore
HttpLibcurl::HttpLibcurl(HttpService * service)
: mService(service),
+ mHandleCache(),
mPolicyCount(0),
mMultiHandles(NULL),
mActiveHandles(NULL),
@@ -61,7 +62,7 @@ HttpLibcurl::HttpLibcurl(HttpService * service)
HttpLibcurl::~HttpLibcurl()
{
shutdown();
-
+
mService = NULL;
}
@@ -279,7 +280,7 @@ void HttpLibcurl::cancelRequest(HttpOpRequest * op)
// Detach from multi and recycle handle
curl_multi_remove_handle(mMultiHandles[op->mReqPolicy], op->mCurlHandle);
- curl_easy_cleanup(op->mCurlHandle);
+ mHandleCache.freeHandle(op->mCurlHandle);
op->mCurlHandle = NULL;
// Tracing
@@ -356,7 +357,7 @@ bool HttpLibcurl::completeRequest(CURLM * multi_handle, CURL * handle, CURLcode
// Detach from multi and recycle handle
curl_multi_remove_handle(multi_handle, handle);
- curl_easy_cleanup(handle);
+ mHandleCache.freeHandle(op->mCurlHandle);
op->mCurlHandle = NULL;
// Tracing
@@ -471,6 +472,82 @@ void HttpLibcurl::policyUpdated(int policy_class)
}
}
+// ---------------------------------------
+// HttpLibcurl::HandleCache
+// ---------------------------------------
+
+HttpLibcurl::HandleCache::HandleCache()
+ : mHandleTemplate(NULL)
+{
+ mCache.reserve(50);
+}
+
+
+HttpLibcurl::HandleCache::~HandleCache()
+{
+ if (mHandleTemplate)
+ {
+ curl_easy_cleanup(mHandleTemplate);
+ mHandleTemplate = NULL;
+ }
+
+ for (handle_cache_t::iterator it(mCache.begin()); mCache.end() != it; ++it)
+ {
+ curl_easy_cleanup(*it);
+ }
+ mCache.clear();
+}
+
+
+CURL * HttpLibcurl::HandleCache::getHandle()
+{
+ CURL * ret(NULL);
+
+ if (! mCache.empty())
+ {
+ // Fastest path to handle
+ ret = mCache.back();
+ mCache.pop_back();
+ }
+ else if (mHandleTemplate)
+ {
+ // Still fast path
+ ret = curl_easy_duphandle(mHandleTemplate);
+ }
+ else
+ {
+ // When all else fails
+ ret = curl_easy_init();
+ }
+
+ return ret;
+}
+
+
+void HttpLibcurl::HandleCache::freeHandle(CURL * handle)
+{
+ if (! handle)
+ {
+ return;
+ }
+
+ curl_easy_reset(handle);
+ if (! mHandleTemplate)
+ {
+ // Save the first freed handle as a template.
+ mHandleTemplate = handle;
+ }
+ else
+ {
+ // Otherwise add it to the cache
+ if (mCache.size() >= mCache.capacity())
+ {
+ mCache.reserve(mCache.capacity() + 50);
+ }
+ mCache.push_back(handle);
+ }
+}
+
// ---------------------------------------
// Free functions
diff --git a/indra/llcorehttp/_httplibcurl.h b/indra/llcorehttp/_httplibcurl.h
index 2c7ad1fa8e..ffc24c63a8 100755
--- a/indra/llcorehttp/_httplibcurl.h
+++ b/indra/llcorehttp/_httplibcurl.h
@@ -124,6 +124,23 @@ public:
/// Threading: called by worker thread.
void policyUpdated(int policy_class);
+ /// Allocate a curl handle for caller. May be freed using
+ /// either the freeHandle() method or calling curl_easy_cleanup()
+ /// directly.
+ ///
+ /// @return Libcurl handle (CURL *) or NULL on allocation
+ /// problem. Handle will be in curl_easy_reset()
+ /// condition.
+ ///
+ /// Threading: callable by worker thread.
+ ///
+ /// Deprecation: Expect this to go away after _httpoprequest is
+ /// refactored bringing code into this class.
+ CURL * getHandle()
+ {
+ return mHandleCache.getHandle();
+ }
+
protected:
/// Invoked when libcurl has indicated a request has been processed
/// to completion and we need to move the request to a new state.
@@ -135,14 +152,67 @@ protected:
protected:
typedef std::set<HttpOpRequest *> active_set_t;
+
+ /// Simple request handle cache for libcurl.
+ ///
+ /// Handle creation is somewhat slow and chunky in libcurl and there's
+ /// a pretty good speedup to be had from handle re-use. So, a simple
+ /// vector is kept of 'freed' handles to be reused as needed. When
+ /// that is empty, the first freed handle is kept as a template for
+ /// handle duplication. This is still faster than creation from nothing.
+ /// And when that fails, we init fresh from curl_easy_init().
+ ///
+ /// Handles allocated with getHandle() may be freed with either
+ /// freeHandle() or curl_easy_cleanup(). Choice may be dictated
+ /// by thread constraints.
+ ///
+ /// Threading: Single-threaded. May only be used by a single thread,
+ /// typically the worker thread. If freeing requests' handles in an
+ /// unknown threading context, use curl_easy_cleanup() for safety.
+
+ class HandleCache
+ {
+ public:
+ HandleCache();
+ ~HandleCache();
+
+ private:
+ HandleCache(const HandleCache &); // Not defined
+ void operator=(const HandleCache &); // Not defined
+
+ public:
+ /// Allocate a curl handle for caller. May be freed using
+ /// either the freeHandle() method or calling curl_easy_cleanup()
+ /// directly.
+ ///
+ /// @return Libcurl handle (CURL *) or NULL on allocation
+ /// problem.
+ ///
+ /// Threading: Single-thread (worker) only.
+ CURL * getHandle();
+
+ /// Free a libcurl handle acquired by whatever means. Thread
+ /// safety is left to the caller.
+ ///
+ /// Threading: Single-thread (worker) only.
+ void freeHandle(CURL * handle);
+
+ protected:
+ typedef std::vector<CURL *> handle_cache_t;
+
+ protected:
+ CURL * mHandleTemplate; // Template for duplicating new handles
+ handle_cache_t mCache; // Cache of old handles
+ }; // end class HandleCache
protected:
- HttpService * mService; // Simple reference, not owner
+ HttpService * mService; // Simple reference, not owner
+ HandleCache mHandleCache; // Handle allocator, owner
active_set_t mActiveOps;
int mPolicyCount;
- CURLM ** mMultiHandles; // One handle per policy class
- int * mActiveHandles; // Active count per policy class
- bool * mDirtyPolicy; // Dirty policy update waiting for stall (per pc)
+ CURLM ** mMultiHandles; // One handle per policy class
+ int * mActiveHandles; // Active count per policy class
+ bool * mDirtyPolicy; // Dirty policy update waiting for stall (per pc)
}; // end class HttpLibcurl
diff --git a/indra/llcorehttp/_httpoprequest.cpp b/indra/llcorehttp/_httpoprequest.cpp
index 4453bf2922..bbda0b82fd 100755
--- a/indra/llcorehttp/_httpoprequest.cpp
+++ b/indra/llcorehttp/_httpoprequest.cpp
@@ -170,6 +170,8 @@ HttpOpRequest::~HttpOpRequest()
if (mCurlHandle)
{
+ // Uncertain of thread context so free using
+ // safest method.
curl_easy_cleanup(mCurlHandle);
mCurlHandle = NULL;
}
@@ -429,7 +431,7 @@ HttpStatus HttpOpRequest::prepareRequest(HttpService * service)
HttpPolicyGlobal & gpolicy(service->getPolicy().getGlobalOptions());
HttpPolicyClass & cpolicy(service->getPolicy().getClassOptions(mReqPolicy));
- mCurlHandle = LLCurl::createStandardCurlHandle();
+ mCurlHandle = service->getTransport().getHandle();
if (! mCurlHandle)
{
// We're in trouble. We'll continue but it won't go well.
@@ -437,6 +439,7 @@ HttpStatus HttpOpRequest::prepareRequest(HttpService * service)
<< LL_ENDL;
return HttpStatus(HttpStatus::LLCORE, HE_BAD_ALLOC);
}
+
code = curl_easy_setopt(mCurlHandle, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V4);
check_curl_easy_code(code, CURLOPT_IPRESOLVE);
code = curl_easy_setopt(mCurlHandle, CURLOPT_NOSIGNAL, 1);