diff options
-rw-r--r-- | indra/llcorehttp/CMakeLists.txt | 2 | ||||
-rw-r--r-- | indra/llcorehttp/_httplibcurl.cpp | 51 | ||||
-rw-r--r-- | indra/llcorehttp/_httplibcurl.h | 7 | ||||
-rw-r--r-- | indra/llcorehttp/_httppolicy.cpp | 69 | ||||
-rw-r--r-- | indra/llcorehttp/_httppolicy.h | 22 | ||||
-rw-r--r-- | indra/llcorehttp/_httppolicyclass.cpp | 125 | ||||
-rw-r--r-- | indra/llcorehttp/_httppolicyclass.h | 59 | ||||
-rw-r--r-- | indra/llcorehttp/_httpservice.cpp | 21 | ||||
-rw-r--r-- | indra/llcorehttp/_httpservice.h | 28 | ||||
-rw-r--r-- | indra/llcorehttp/httprequest.cpp | 29 |
10 files changed, 351 insertions, 62 deletions
diff --git a/indra/llcorehttp/CMakeLists.txt b/indra/llcorehttp/CMakeLists.txt index 4c00cc04e9..f3df9bb94f 100644 --- a/indra/llcorehttp/CMakeLists.txt +++ b/indra/llcorehttp/CMakeLists.txt @@ -37,6 +37,7 @@ set(llcorehttp_SOURCE_FILES _httpopsetget.cpp _httpopsetpriority.cpp _httppolicy.cpp + _httppolicyclass.cpp _httppolicyglobal.cpp _httpreplyqueue.cpp _httprequestqueue.cpp @@ -63,6 +64,7 @@ set(llcorehttp_HEADER_FILES _httpopsetget.h _httpopsetpriority.h _httppolicy.h + _httppolicyclass.h _httppolicyglobal.h _httpreadyqueue.h _httpreplyqueue.h diff --git a/indra/llcorehttp/_httplibcurl.cpp b/indra/llcorehttp/_httplibcurl.cpp index 65eb642056..45e16d420e 100644 --- a/indra/llcorehttp/_httplibcurl.cpp +++ b/indra/llcorehttp/_httplibcurl.cpp @@ -39,17 +39,10 @@ namespace LLCore HttpLibcurl::HttpLibcurl(HttpService * service) - : mService(service) -{ - // *FIXME: Use active policy class count later - for (int policy_class(0); policy_class < LL_ARRAY_SIZE(mMultiHandles); ++policy_class) - { - mMultiHandles[policy_class] = 0; - } - - // Create multi handle for default class - mMultiHandles[0] = curl_multi_init(); -} + : mService(service), + mPolicyCount(0), + mMultiHandles(NULL) +{} HttpLibcurl::~HttpLibcurl() @@ -64,17 +57,23 @@ HttpLibcurl::~HttpLibcurl() mActiveOps.erase(item); } - for (int policy_class(0); policy_class < LL_ARRAY_SIZE(mMultiHandles); ++policy_class) + if (mMultiHandles) { - if (mMultiHandles[policy_class]) + for (int policy_class(0); policy_class < mPolicyCount; ++policy_class) { - // *FIXME: Do some multi cleanup here first + if (mMultiHandles[policy_class]) + { + // *FIXME: Do some multi cleanup here first - curl_multi_cleanup(mMultiHandles[policy_class]); - mMultiHandles[policy_class] = 0; + curl_multi_cleanup(mMultiHandles[policy_class]); + mMultiHandles[policy_class] = 0; + } } - } + delete [] mMultiHandles; + mMultiHandles = NULL; + } + mService = NULL; } @@ -87,12 +86,26 @@ void HttpLibcurl::term() {} +void HttpLibcurl::setPolicyCount(int policy_count) +{ + llassert_always(policy_count <= POLICY_CLASS_LIMIT); + llassert_always(! mMultiHandles); // One-time call only + + mPolicyCount = policy_count; + mMultiHandles = new CURLM * [mPolicyCount]; + for (int policy_class(0); policy_class < mPolicyCount; ++policy_class) + { + mMultiHandles[policy_class] = curl_multi_init(); + } +} + + HttpService::ELoopSpeed HttpLibcurl::processTransport() { HttpService::ELoopSpeed ret(HttpService::REQUEST_SLEEP); // Give libcurl some cycles to do I/O & callbacks - for (int policy_class(0); policy_class < LL_ARRAY_SIZE(mMultiHandles); ++policy_class) + for (int policy_class(0); policy_class < mPolicyCount; ++policy_class) { if (! mMultiHandles[policy_class]) continue; @@ -147,7 +160,7 @@ HttpService::ELoopSpeed HttpLibcurl::processTransport() void HttpLibcurl::addOp(HttpOpRequest * op) { - llassert_always(op->mReqPolicy < POLICY_CLASS_LIMIT); + llassert_always(op->mReqPolicy < mPolicyCount); llassert_always(mMultiHandles[op->mReqPolicy] != NULL); // Create standard handle diff --git a/indra/llcorehttp/_httplibcurl.h b/indra/llcorehttp/_httplibcurl.h index 0d0c4cad6d..5e1dd1bfbf 100644 --- a/indra/llcorehttp/_httplibcurl.h +++ b/indra/llcorehttp/_httplibcurl.h @@ -78,6 +78,10 @@ public: /// additional references will be added.) void addOp(HttpOpRequest * op); + /// One-time call to set the number of policy classes to be + /// serviced and to create the resources for each. + void setPolicyCount(int policy_count); + int getActiveCount() const; int getActiveCountInClass(int policy_class) const; @@ -92,7 +96,8 @@ protected: protected: HttpService * mService; // Simple reference, not owner active_set_t mActiveOps; - CURLM * mMultiHandles[POLICY_CLASS_LIMIT]; + int mPolicyCount; + CURLM ** mMultiHandles; }; // end class HttpLibcurl } // end namespace LLCore diff --git a/indra/llcorehttp/_httppolicy.cpp b/indra/llcorehttp/_httppolicy.cpp index 4be9f1d45f..1dae20add6 100644 --- a/indra/llcorehttp/_httppolicy.cpp +++ b/indra/llcorehttp/_httppolicy.cpp @@ -31,6 +31,7 @@ #include "_httpoprequest.h" #include "_httpservice.h" #include "_httplibcurl.h" +#include "_httppolicyclass.h" #include "lltimer.h" @@ -38,14 +39,44 @@ namespace LLCore { + +struct HttpPolicy::State +{ +public: + State() + : mConnMax(DEFAULT_CONNECTIONS), + mConnAt(DEFAULT_CONNECTIONS), + mConnMin(2), + mNextSample(0), + mErrorCount(0), + mErrorFactor(0) + {} + + HttpReadyQueue mReadyQueue; + HttpRetryQueue mRetryQueue; + + HttpPolicyClass mOptions; + + long mConnMax; + long mConnAt; + long mConnMin; + + HttpTime mNextSample; + unsigned long mErrorCount; + unsigned long mErrorFactor; +}; + + HttpPolicy::HttpPolicy(HttpService * service) - : mService(service) + : mActiveClasses(0), + mState(NULL), + mService(service) {} HttpPolicy::~HttpPolicy() { - for (int policy_class(0); policy_class < LL_ARRAY_SIZE(mState); ++policy_class) + for (int policy_class(0); policy_class < mActiveClasses; ++policy_class) { HttpRetryQueue & retryq(mState[policy_class].mRetryQueue); while (! retryq.empty()) @@ -67,13 +98,27 @@ HttpPolicy::~HttpPolicy() readyq.pop(); } } + delete [] mState; + mState = NULL; mService = NULL; } -void HttpPolicy::setPolicies(const HttpPolicyGlobal & global) +void HttpPolicy::setPolicies(const HttpPolicyGlobal & global, + const std::vector<HttpPolicyClass> & classes) { + llassert_always(! mState); + mGlobalOptions = global; + mActiveClasses = classes.size(); + mState = new State [mActiveClasses]; + for (int i(0); i < mActiveClasses; ++i) + { + mState[i].mOptions = classes[i]; + mState[i].mConnMax = classes[i].mConnectionLimit; + mState[i].mConnAt = mState[i].mConnMax; + mState[i].mConnMin = 2; + } } @@ -123,13 +168,14 @@ HttpService::ELoopSpeed HttpPolicy::processReadyQueue() HttpService::ELoopSpeed result(HttpService::REQUEST_SLEEP); HttpLibcurl & transport(mService->getTransport()); - for (int policy_class(0); policy_class < LL_ARRAY_SIZE(mState); ++policy_class) + for (int policy_class(0); policy_class < mActiveClasses; ++policy_class) { + State & state(mState[policy_class]); int active(transport.getActiveCountInClass(policy_class)); - int needed(DEFAULT_CONNECTIONS - active); // *FIXME: move to policy class + int needed(state.mConnAt - active); // Expect negatives here - HttpRetryQueue & retryq(mState[policy_class].mRetryQueue); - HttpReadyQueue & readyq(mState[policy_class].mReadyQueue); + HttpRetryQueue & retryq(state.mRetryQueue); + HttpReadyQueue & readyq(state.mReadyQueue); if (needed > 0) { @@ -174,9 +220,10 @@ HttpService::ELoopSpeed HttpPolicy::processReadyQueue() bool HttpPolicy::changePriority(HttpHandle handle, HttpRequest::priority_t priority) { - for (int policy_class(0); policy_class < LL_ARRAY_SIZE(mState); ++policy_class) + for (int policy_class(0); policy_class < mActiveClasses; ++policy_class) { - HttpReadyQueue::container_type & c(mState[policy_class].mReadyQueue.get_container()); + State & state(mState[policy_class]); + HttpReadyQueue::container_type & c(state.mReadyQueue.get_container()); // Scan ready queue for requests that match policy for (HttpReadyQueue::container_type::iterator iter(c.begin()); c.end() != iter;) @@ -188,7 +235,7 @@ bool HttpPolicy::changePriority(HttpHandle handle, HttpRequest::priority_t prior HttpOpRequest * op(*cur); c.erase(cur); // All iterators are now invalidated op->mReqPriority = priority; - mState[policy_class].mReadyQueue.push(op); // Re-insert using adapter class + state.mReadyQueue.push(op); // Re-insert using adapter class return true; } } @@ -242,7 +289,7 @@ bool HttpPolicy::stageAfterCompletion(HttpOpRequest * op) int HttpPolicy::getReadyCount(HttpRequest::policy_t policy_class) { - if (policy_class < POLICY_CLASS_LIMIT) // *FIXME: use actual active class count + if (policy_class < mActiveClasses) { return (mState[policy_class].mReadyQueue.size() + mState[policy_class].mRetryQueue.size()); diff --git a/indra/llcorehttp/_httppolicy.h b/indra/llcorehttp/_httppolicy.h index 05de9303b5..c93279bc83 100644 --- a/indra/llcorehttp/_httppolicy.h +++ b/indra/llcorehttp/_httppolicy.h @@ -33,6 +33,7 @@ #include "_httpreadyqueue.h" #include "_httpretryqueue.h" #include "_httppolicyglobal.h" +#include "_httppolicyclass.h" #include "_httpinternal.h" @@ -92,26 +93,25 @@ public: // Get pointer to global policy options. Caller is expected // to do context checks like no setting once running. - HttpPolicyGlobal & getGlobalOptions() + HttpPolicyGlobal & getGlobalOptions() { return mGlobalOptions; } - void setPolicies(const HttpPolicyGlobal & global); + void setPolicies(const HttpPolicyGlobal & global, + const std::vector<HttpPolicyClass> & classes); + // Get ready counts for a particular class int getReadyCount(HttpRequest::policy_t policy_class); protected: - struct State - { - HttpReadyQueue mReadyQueue; - HttpRetryQueue mRetryQueue; - }; - - State mState[POLICY_CLASS_LIMIT]; - HttpService * mService; // Naked pointer, not refcounted, not owner - HttpPolicyGlobal mGlobalOptions; + struct State; + + int mActiveClasses; + State * mState; + HttpService * mService; // Naked pointer, not refcounted, not owner + HttpPolicyGlobal mGlobalOptions; }; // end class HttpPolicy diff --git a/indra/llcorehttp/_httppolicyclass.cpp b/indra/llcorehttp/_httppolicyclass.cpp new file mode 100644 index 0000000000..8007468d3c --- /dev/null +++ b/indra/llcorehttp/_httppolicyclass.cpp @@ -0,0 +1,125 @@ +/** + * @file _httppolicyclass.cpp + * @brief Definitions for internal class defining class policy option. + * + * $LicenseInfo:firstyear=2012&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2012, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#include "_httppolicyclass.h" + +#include "_httpinternal.h" + + +namespace LLCore +{ + + +HttpPolicyClass::HttpPolicyClass() + : mSetMask(0UL), + mConnectionLimit(DEFAULT_CONNECTIONS), + mPerHostConnectionLimit(DEFAULT_CONNECTIONS), + mPipelining(0) +{} + + +HttpPolicyClass::~HttpPolicyClass() +{} + + +HttpPolicyClass & HttpPolicyClass::operator=(const HttpPolicyClass & other) +{ + if (this != &other) + { + mSetMask = other.mSetMask; + mConnectionLimit = other.mConnectionLimit; + mPerHostConnectionLimit = other.mPerHostConnectionLimit; + mPipelining = other.mPipelining; + } + return *this; +} + + +HttpPolicyClass::HttpPolicyClass(const HttpPolicyClass & other) + : mSetMask(other.mSetMask), + mConnectionLimit(other.mConnectionLimit), + mPerHostConnectionLimit(other.mPerHostConnectionLimit), + mPipelining(other.mPipelining) +{} + + +HttpStatus HttpPolicyClass::set(HttpRequest::EClassPolicy opt, long value) +{ + switch (opt) + { + case HttpRequest::CP_CONNECTION_LIMIT: + mConnectionLimit = llclamp(value, long(LIMIT_CONNECTIONS_MIN), long(LIMIT_CONNECTIONS_MAX)); + break; + + case HttpRequest::CP_PER_HOST_CONNECTION_LIMIT: + mPerHostConnectionLimit = llclamp(value, long(LIMIT_CONNECTIONS_MIN), mConnectionLimit); + break; + + case HttpRequest::CP_ENABLE_PIPELINING: + mPipelining = llclamp(value, 0L, 1L); + break; + + default: + return HttpStatus(HttpStatus::LLCORE, HE_INVALID_ARG); + } + + mSetMask |= 1UL << int(opt); + return HttpStatus(); +} + + +HttpStatus HttpPolicyClass::get(HttpRequest::EClassPolicy opt, long * value) +{ + static const HttpStatus not_set(HttpStatus::LLCORE, HE_OPT_NOT_SET); + long * src(NULL); + + switch (opt) + { + case HttpRequest::CP_CONNECTION_LIMIT: + src = &mConnectionLimit; + break; + + case HttpRequest::CP_PER_HOST_CONNECTION_LIMIT: + src = &mPerHostConnectionLimit; + break; + + case HttpRequest::CP_ENABLE_PIPELINING: + src = &mPipelining; + break; + + default: + return HttpStatus(HttpStatus::LLCORE, HE_INVALID_ARG); + } + + if (! (mSetMask & (1UL << int(opt)))) + return not_set; + + *value = *src; + return HttpStatus(); +} + + +} // end namespace LLCore diff --git a/indra/llcorehttp/_httppolicyclass.h b/indra/llcorehttp/_httppolicyclass.h new file mode 100644 index 0000000000..d175413cbd --- /dev/null +++ b/indra/llcorehttp/_httppolicyclass.h @@ -0,0 +1,59 @@ +/** + * @file _httppolicyclass.h + * @brief Declarations for internal class defining policy class options. + * + * $LicenseInfo:firstyear=2012&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2012, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#ifndef _LLCORE_HTTP_POLICY_CLASS_H_ +#define _LLCORE_HTTP_POLICY_CLASS_H_ + + +#include "httprequest.h" + + +namespace LLCore +{ + +class HttpPolicyClass +{ +public: + HttpPolicyClass(); + ~HttpPolicyClass(); + + HttpPolicyClass & operator=(const HttpPolicyClass &); + HttpPolicyClass(const HttpPolicyClass &); // Not defined + +public: + HttpStatus set(HttpRequest::EClassPolicy opt, long value); + HttpStatus get(HttpRequest::EClassPolicy opt, long * value); + +public: + unsigned long mSetMask; + long mConnectionLimit; + long mPerHostConnectionLimit; + long mPipelining; +}; // end class HttpPolicyClass + +} // end namespace LLCore + +#endif // _LLCORE_HTTP_POLICY_CLASS_H_ diff --git a/indra/llcorehttp/_httpservice.cpp b/indra/llcorehttp/_httpservice.cpp index faafd9a6c7..9c5c7bf9b4 100644 --- a/indra/llcorehttp/_httpservice.cpp +++ b/indra/llcorehttp/_httpservice.cpp @@ -53,6 +53,12 @@ HttpService::HttpService() mPolicy(NULL), mTransport(NULL) { + HttpPolicyClass pol_class; + pol_class.set(HttpRequest::CP_CONNECTION_LIMIT, DEFAULT_CONNECTIONS); + pol_class.set(HttpRequest::CP_PER_HOST_CONNECTION_LIMIT, DEFAULT_CONNECTIONS); + pol_class.set(HttpRequest::CP_ENABLE_PIPELINING, 0L); + + mPolicyClasses.push_back(pol_class); } @@ -114,6 +120,18 @@ void HttpService::term() } +HttpRequest::policy_t HttpService::createPolicyClass() +{ + const HttpRequest::policy_t policy_class(mPolicyClasses.size()); + if (policy_class >= POLICY_CLASS_LIMIT) + { + return 0; + } + mPolicyClasses.push_back(HttpPolicyClass()); + return policy_class; +} + + bool HttpService::isStopped() { // What is really wanted here is something like: @@ -142,7 +160,8 @@ void HttpService::startThread() } // Push current policy definitions - mPolicy->setPolicies(mPolicyGlobal); + mPolicy->setPolicies(mPolicyGlobal, mPolicyClasses); + mTransport->setPolicyCount(mPolicyClasses.size()); mThread = new LLCoreInt::HttpThread(boost::bind(&HttpService::threadRun, this, _1)); mThread->addRef(); // Need an explicit reference, implicit one is used internally diff --git a/indra/llcorehttp/_httpservice.h b/indra/llcorehttp/_httpservice.h index 3f953ec1a7..43044d97c0 100644 --- a/indra/llcorehttp/_httpservice.h +++ b/indra/llcorehttp/_httpservice.h @@ -28,9 +28,12 @@ #define _LLCORE_HTTP_SERVICE_H_ +#include <vector> + #include "httpcommon.h" #include "httprequest.h" #include "_httppolicyglobal.h" +#include "_httppolicyclass.h" namespace LLCoreInt @@ -163,6 +166,14 @@ public: { return mPolicyGlobal; } + + HttpRequest::policy_t createPolicyClass(); + + HttpPolicyClass & getClassOptions(HttpRequest::policy_t policy_class) + { + llassert(policy_class >= 0 && policy_class < mPolicyClasses.size()); + return mPolicyClasses[policy_class]; + } protected: void threadRun(LLCoreInt::HttpThread * thread); @@ -170,20 +181,21 @@ protected: ELoopSpeed processRequestQueue(ELoopSpeed loop); protected: - static HttpService * sInstance; + static HttpService * sInstance; // === shared data === - static volatile EState sState; - HttpRequestQueue * mRequestQueue; - volatile bool mExitRequested; + static volatile EState sState; + HttpRequestQueue * mRequestQueue; + volatile bool mExitRequested; // === calling-thread-only data === - LLCoreInt::HttpThread * mThread; - HttpPolicyGlobal mPolicyGlobal; + LLCoreInt::HttpThread * mThread; + HttpPolicyGlobal mPolicyGlobal; + std::vector<HttpPolicyClass> mPolicyClasses; // === working-thread-only data === - HttpPolicy * mPolicy; // Simple pointer, has ownership - HttpLibcurl * mTransport; // Simple pointer, has ownership + HttpPolicy * mPolicy; // Simple pointer, has ownership + HttpLibcurl * mTransport; // Simple pointer, has ownership }; // end class HttpService } // end namespace LLCore diff --git a/indra/llcorehttp/httprequest.cpp b/indra/llcorehttp/httprequest.cpp index 6d13a213f5..a525d8f9ea 100644 --- a/indra/llcorehttp/httprequest.cpp +++ b/indra/llcorehttp/httprequest.cpp @@ -92,26 +92,31 @@ HttpRequest::~HttpRequest() HttpStatus HttpRequest::setPolicyGlobalOption(EGlobalPolicy opt, long value) { - // *FIXME: Fail if thread is running. - + if (HttpService::RUNNING == HttpService::instanceOf()->getState()) + { + return HttpStatus(HttpStatus::LLCORE, HE_OPT_NOT_DYNAMIC); + } return HttpService::instanceOf()->getGlobalOptions().set(opt, value); } HttpStatus HttpRequest::setPolicyGlobalOption(EGlobalPolicy opt, const std::string & value) { - // *FIXME: Fail if thread is running. - + if (HttpService::RUNNING == HttpService::instanceOf()->getState()) + { + return HttpStatus(HttpStatus::LLCORE, HE_OPT_NOT_DYNAMIC); + } return HttpService::instanceOf()->getGlobalOptions().set(opt, value); } HttpRequest::policy_t HttpRequest::createPolicyClass() { - // *FIXME: Implement classes - policy_t policy_id = 1; - - return policy_id; + if (HttpService::RUNNING == HttpService::instanceOf()->getState()) + { + return 0; + } + return HttpService::instanceOf()->createPolicyClass(); } @@ -119,9 +124,11 @@ HttpStatus HttpRequest::setPolicyClassOption(policy_t policy_id, EClassPolicy opt, long value) { - HttpStatus status; - - return status; + if (HttpService::RUNNING == HttpService::instanceOf()->getState()) + { + return HttpStatus(HttpStatus::LLCORE, HE_OPT_NOT_DYNAMIC); + } + return HttpService::instanceOf()->getClassOptions(policy_id).set(opt, value); } |