Merge branch 'master' of git://github.com/AlericInglewood/SingularityViewer

Conflicts:
	indra/llmessage/llavatarnamecache.cpp
This commit is contained in:
Siana Gearz
2012-11-12 04:12:28 +01:00
21 changed files with 668 additions and 185 deletions

View File

@@ -592,7 +592,7 @@ void AIStateMachine::flush(void)
AIStateMachine& statemachine(iter->statemachine());
if (statemachine.abortable())
{
// We can't safely call abort() here for non-running (run() was called, but they we're initialized yet) statemachines,
// We can't safely call abort() here for non-running (run() was called, but they weren't initialized yet) statemachines,
// because that might call kill() which in some cases is undesirable (ie, when it is owned by a partent that will
// also call abort() on it when it is aborted itself).
if (statemachine.running())
@@ -619,12 +619,10 @@ void AIStateMachine::flush(void)
AIReadAccess<csme_type> csme_r(sContinuedStateMachinesAndMainloopEnabled);
add_continued_statemachines(csme_r);
}
// Kill all state machines.
for (active_statemachines_type::iterator iter = active_statemachines.begin(); iter != active_statemachines.end(); ++iter)
{
AIStateMachine& statemachine(iter->statemachine());
if (statemachine.running())
statemachine.kill();
}
}
// At this point all statemachines should be idle.
AIReadAccess<csme_type> csme_r(sContinuedStateMachinesAndMainloopEnabled);
llinfos << "Current number of continued statemachines: " << csme_r->continued_statemachines.size() << llendl;
llinfos << "Current number of active statemachines: " << active_statemachines.size() << llendl;
llassert(csme_r->continued_statemachines.empty() && active_statemachines.empty());
}

View File

@@ -36,26 +36,39 @@
#include "llapr.h"
#include "llscopedvolatileaprpool.h"
LLFastTimer::DeclareTimer FT_WAIT_FOR_SCOPEDLOCK("LLScopedLock");
//---------------------------------------------------------------------
//
// LLScopedLock
//
LLScopedLock::LLScopedLock(apr_thread_mutex_t* mutex) : mMutex(mutex)
{
if(mutex)
mLocked = !!mutex;
if (LL_LIKELY(mutex))
{
if(ll_apr_warn_status(apr_thread_mutex_lock(mMutex)))
apr_status_t status = apr_thread_mutex_trylock(mMutex);
while (LL_UNLIKELY(status != APR_SUCCESS))
{
mLocked = false;
if (APR_STATUS_IS_EBUSY(status))
{
if (AIThreadID::in_main_thread_inline())
{
LLFastTimer ft1(FT_WAIT_FOR_SCOPEDLOCK);
status = apr_thread_mutex_lock(mMutex);
}
else
{
status = apr_thread_mutex_lock(mMutex);
}
}
else
{
ll_apr_warn_status(status);
mLocked = false;
return;
}
}
else
{
mLocked = true;
}
}
else
{
mLocked = false;
}
}

View File

@@ -382,9 +382,19 @@ LLCondition::~LLCondition()
mAPRCondp = NULL;
}
LLFastTimer::DeclareTimer FT_WAIT_FOR_CONDITION("LLCondition::wait()");
void LLCondition::wait()
{
apr_thread_cond_wait(mAPRCondp, mAPRMutexp);
if (AIThreadID::in_main_thread_inline())
{
LLFastTimer ft1(FT_WAIT_FOR_CONDITION);
apr_thread_cond_wait(mAPRCondp, mAPRMutexp);
}
else
{
apr_thread_cond_wait(mAPRCondp, mAPRMutexp);
}
}
void LLCondition::signal()
@@ -409,6 +419,8 @@ bool LLMutexBase::isSelfLocked() const
return mLockingThread.equals_current_thread_inline();
}
LLFastTimer::DeclareTimer FT_WAIT_FOR_MUTEX("LLMutexBase::lock()");
void LLMutexBase::lock()
{
if (mLockingThread.equals_current_thread_inline())
@@ -417,7 +429,18 @@ void LLMutexBase::lock()
return;
}
apr_thread_mutex_lock(mAPRMutexp);
if (APR_STATUS_IS_EBUSY(apr_thread_mutex_trylock(mAPRMutexp)))
{
if (AIThreadID::in_main_thread_inline())
{
LLFastTimer ft1(FT_WAIT_FOR_MUTEX);
apr_thread_mutex_lock(mAPRMutexp);
}
else
{
apr_thread_mutex_lock(mAPRMutexp);
}
}
mLockingThread.reset_inline();
}

View File

@@ -24,6 +24,7 @@ include_directories(
set(llmessage_SOURCE_FILES
aicurl.cpp
aicurleasyrequeststatemachine.cpp
aicurlperhost.cpp
aicurlthread.cpp
aihttpheaders.cpp
aihttptimeoutpolicy.cpp
@@ -110,6 +111,7 @@ set(llmessage_HEADER_FILES
aicurl.h
aicurleasyrequeststatemachine.h
aicurlprivate.h
aicurlperhost.h
aicurlthread.h
aihttpheaders.h
aihttptimeoutpolicy.h

View File

@@ -58,6 +58,7 @@
#include "aihttpheaders.h"
#include "aihttptimeoutpolicy.h"
#include "aicurleasyrequeststatemachine.h"
#include "aicurlperhost.h"
//==================================================================================
// Debug Settings
@@ -90,7 +91,6 @@ enum gSSLlib_type {
// No locking needed: initialized before threads are created, and subsequently only read.
gSSLlib_type gSSLlib;
bool gSetoptParamsNeedDup;
void (*statemachines_flush_hook)(void);
} // namespace
@@ -290,10 +290,39 @@ static unsigned int encoded_version(int major, int minor, int patch)
namespace AICurlInterface {
// MAIN-THREAD
void initCurl(void (*flush_hook)())
//static
LLAtomicU32 Stats::easy_calls;
LLAtomicU32 Stats::easy_errors;
LLAtomicU32 Stats::easy_init_calls;
LLAtomicU32 Stats::easy_init_errors;
LLAtomicU32 Stats::easy_cleanup_calls;
LLAtomicU32 Stats::multi_calls;
LLAtomicU32 Stats::multi_errors;
LLAtomicU32 Stats::AICurlEasyRequest_count;
LLAtomicU32 Stats::AICurlEasyRequestStateMachine_count;
LLAtomicU32 Stats::BufferedCurlEasyRequest_count;
LLAtomicU32 Stats::ResponderBase_count;
LLAtomicU32 Stats::ThreadSafeBufferedCurlEasyRequest_count;
LLAtomicU32 Stats::status_count[100];
LLAtomicU32 Stats::llsd_body_count;
LLAtomicU32 Stats::llsd_body_parse_error;
LLAtomicU32 Stats::raw_body_count;
U32 Stats::status2index(U32 status)
{
DoutEntering(dc::curl, "AICurlInterface::initCurl(" << (void*)flush_hook << ")");
llassert_always(status >= 100 && status < 600 && (status % 100) < 20); // Max value 519.
return (status - 100) / 100 * 20 + status % 100; // Returns 0..99 (for status 100..519).
}
U32 Stats::index2status(U32 index)
{
return 100 + (index / 20) * 100 + index % 20;
}
// MAIN-THREAD
void initCurl(void)
{
DoutEntering(dc::curl, "AICurlInterface::initCurl()");
llassert(LLThread::getRunning() == 0); // We must not call curl_global_init unless we are the only thread.
CURLcode res = curl_global_init(CURL_GLOBAL_ALL);
@@ -376,9 +405,6 @@ void initCurl(void (*flush_hook)())
}
llassert_always(!gSetoptParamsNeedDup); // Might add support later.
}
// Called in cleanupCurl.
statemachines_flush_hook = flush_hook;
}
// MAIN-THREAD
@@ -391,8 +417,7 @@ void cleanupCurl(void)
stopCurlThread();
if (CurlMultiHandle::getTotalMultiHandles() != 0)
llwarns << "Not all CurlMultiHandle objects were destroyed!" << llendl;
if (statemachines_flush_hook)
(*statemachines_flush_hook)();
AIStateMachine::flush();
Stats::print();
ssl_cleanup();
@@ -422,6 +447,58 @@ void setCAPath(std::string const& path)
CertificateAuthority_w->path = path;
}
//static
void Stats::print(void)
{
int const easy_handles = easy_init_calls - easy_init_errors - easy_cleanup_calls;
llinfos_nf << "============ CURL STATS ============" << llendl;
llinfos_nf << " Curl multi errors/calls : " << std::dec << multi_errors << "/" << multi_calls << llendl;
llinfos_nf << " Curl easy errors/calls : " << std::dec << easy_errors << "/" << easy_calls << llendl;
llinfos_nf << " curl_easy_init() errors/calls : " << std::dec << easy_init_errors << "/" << easy_init_calls << llendl;
llinfos_nf << " Current number of curl easy handles: " << std::dec << easy_handles << llendl;
#ifdef DEBUG_CURLIO
llinfos_nf << " Current number of BufferedCurlEasyRequest objects: " << BufferedCurlEasyRequest_count << llendl;
llinfos_nf << " Current number of ThreadSafeBufferedCurlEasyRequest objects: " << ThreadSafeBufferedCurlEasyRequest_count << llendl;
llinfos_nf << " Current number of AICurlEasyRequest objects: " << AICurlEasyRequest_count << llendl;
llinfos_nf << " Current number of AICurlEasyRequestStateMachine objects: " << AICurlEasyRequestStateMachine_count << llendl;
#endif
llinfos_nf << " Current number of Responders: " << ResponderBase_count << llendl;
llinfos_nf << " Received HTTP bodies LLSD / LLSD parse errors / non-LLSD: " << llsd_body_count << "/" << llsd_body_parse_error << "/" << raw_body_count << llendl;
llinfos_nf << " Received HTTP status codes: status (count) [...]: ";
bool first = true;
for (U32 index = 0; index < 100; ++index)
{
if (status_count[index] > 0)
{
if (!first)
{
llcont << ", ";
}
else
{
first = false;
}
llcont << index2status(index) << " (" << status_count[index] << ')';
}
}
llcont << llendl;
llinfos_nf << "========= END OF CURL STATS =========" << llendl;
// Leak tests.
// There is one easy handle per CurlEasyHandle, and BufferedCurlEasyRequest is derived from that.
// It is not allowed to create CurlEasyHandle (or CurlEasyRequest) directly, only by creating a BufferedCurlEasyRequest,
// therefore the number of existing easy handles must equal the number of BufferedCurlEasyRequest objects.
llassert(easy_handles == BufferedCurlEasyRequest_count);
// Even more strict, BufferedCurlEasyRequest may not be created directly either, only as
// base class of ThreadSafeBufferedCurlEasyRequest.
llassert(BufferedCurlEasyRequest_count == ThreadSafeBufferedCurlEasyRequest_count);
// Each AICurlEasyRequestStateMachine is responsible for exactly one easy handle.
llassert(easy_handles >= AICurlEasyRequest_count);
// Each AICurlEasyRequestStateMachine has one AICurlEasyRequest member.
llassert(AICurlEasyRequest_count >= AICurlEasyRequestStateMachine_count);
// AIFIXME: is this really always the case? And why?
llassert(easy_handles <= ResponderBase_count);
}
} // namespace AICurlInterface
//==================================================================================
@@ -431,31 +508,13 @@ void setCAPath(std::string const& path)
namespace AICurlPrivate {
using AICurlInterface::Stats;
#if defined(CWDEBUG) || defined(DEBUG_CURLIO)
// CURLOPT_DEBUGFUNCTION function.
extern int debug_callback(CURL*, curl_infotype infotype, char* buf, size_t size, void* user_ptr);
#endif
//static
LLAtomicU32 Stats::easy_calls;
LLAtomicU32 Stats::easy_errors;
LLAtomicU32 Stats::easy_init_calls;
LLAtomicU32 Stats::easy_init_errors;
LLAtomicU32 Stats::easy_cleanup_calls;
LLAtomicU32 Stats::multi_calls;
LLAtomicU32 Stats::multi_errors;
//static
void Stats::print(void)
{
llinfos_nf << "============ CURL STATS ============" << llendl;
llinfos_nf << " Curl multi errors/calls : " << std::dec << multi_errors << "/" << multi_calls << llendl;
llinfos_nf << " Curl easy errors/calls : " << std::dec << easy_errors << "/" << easy_calls << llendl;
llinfos_nf << " curl_easy_init() errors/calls : " << std::dec << easy_init_errors << "/" << easy_init_calls << llendl;
llinfos_nf << " Current number of curl easy handles: " << std::dec << (easy_init_calls - easy_init_errors - easy_cleanup_calls) << llendl;
llinfos_nf << "========= END OF CURL STATS =========" << llendl;
}
// THREAD-SAFE
void handle_multi_error(CURLMcode code)
{
@@ -856,6 +915,10 @@ CurlEasyRequest::~CurlEasyRequest()
// be available anymore.
send_handle_events_to(NULL);
revokeCallbacks();
if (mPerHostPtr)
{
PerHostRequestQueue::release(mPerHostPtr);
}
// This wasn't freed yet if the request never finished.
curl_slist_free_all(mHeaders);
}
@@ -1054,6 +1117,7 @@ void CurlEasyRequest::finalizeRequest(std::string const& url, AIHTTPTimeoutPolic
#endif
setopt(CURLOPT_HTTPHEADER, mHeaders);
setoptString(CURLOPT_URL, url);
llassert(!mPerHostPtr);
mLowercaseHostname = extract_canonical_hostname(url);
mTimeoutPolicy = &policy;
state_machine->setTotalDelayTimeout(policy.getTotalDelay());
@@ -1171,6 +1235,24 @@ void CurlEasyRequest::print_diagnostics(CURLcode code)
}
}
PerHostRequestQueuePtr CurlEasyRequest::getPerHostPtr(void)
{
if (!mPerHostPtr)
{
// mPerHostPtr is really just a speed-up cache.
// The reason we can cache it is because mLowercaseHostname is only set
// in finalizeRequest which may only be called once: it never changes.
mPerHostPtr = PerHostRequestQueue::instance(mLowercaseHostname);
}
return mPerHostPtr;
}
bool CurlEasyRequest::removeFromPerHostQueue(AICurlEasyRequest const& easy_request) const
{
// Note that easy_request (must) represent(s) this object; it's just passed for convenience.
return mPerHostPtr && PerHostRequestQueue_wat(*mPerHostPtr)->cancel(easy_request);
}
//-----------------------------------------------------------------------------
// BufferedCurlEasyRequest
@@ -1180,6 +1262,7 @@ LLChannelDescriptors const BufferedCurlEasyRequest::sChannels;
BufferedCurlEasyRequest::BufferedCurlEasyRequest() : mRequestTransferedBytes(0), mResponseTransferedBytes(0), mBufferEventsTarget(NULL)
{
AICurlInterface::Stats::BufferedCurlEasyRequest_count++;
}
#define llmaybeerrs lllog(LLApp::isRunning() ? LLError::LEVEL_ERROR : LLError::LEVEL_WARN, NULL, NULL, false, true)
@@ -1207,6 +1290,7 @@ BufferedCurlEasyRequest::~BufferedCurlEasyRequest()
timed_out();
}
}
--AICurlInterface::Stats::BufferedCurlEasyRequest_count;
}
void BufferedCurlEasyRequest::timed_out(void)

View File

@@ -127,19 +127,43 @@ class AICurlNoBody : public AICurlError {
// Things defined in this namespace are called from elsewhere in the viewer code.
namespace AICurlInterface {
struct Stats {
static LLAtomicU32 easy_calls;
static LLAtomicU32 easy_errors;
static LLAtomicU32 easy_init_calls;
static LLAtomicU32 easy_init_errors;
static LLAtomicU32 easy_cleanup_calls;
static LLAtomicU32 multi_calls;
static LLAtomicU32 multi_errors;
static LLAtomicU32 AICurlEasyRequest_count;
static LLAtomicU32 AICurlEasyRequestStateMachine_count;
static LLAtomicU32 BufferedCurlEasyRequest_count;
static LLAtomicU32 ResponderBase_count;
static LLAtomicU32 ThreadSafeBufferedCurlEasyRequest_count;
static LLAtomicU32 status_count[100];
static LLAtomicU32 llsd_body_count;
static LLAtomicU32 llsd_body_parse_error;
static LLAtomicU32 raw_body_count;
static void print(void);
static U32 status2index(U32 status);
static U32 index2status(U32 index);
};
//-----------------------------------------------------------------------------
// Global functions.
// Called to handle changes in Debug Settings.
bool handleCurlConcurrentConnections(LLSD const& newvalue);
bool handleCurlMaxTotalConcurrentConnections(LLSD const& newvalue);
bool handleCurlConcurrentConnectionsPerHost(LLSD const& newvalue);
bool handleNoVerifySSLCert(LLSD const& newvalue);
// Called once at start of application (from newview/llappviewer.cpp by main thread (before threads are created)),
// with main purpose to initialize curl.
void initCurl(void (*)(void) = NULL);
void initCurl(void);
// Called once at start of application (from LLAppViewer::initThreads), starts AICurlThread.
void startCurlThread(U32 CurlConcurrentConnections, bool NoVerifySSLCert);
void startCurlThread(U32 CurlMaxTotalConcurrentConnections, U32 CurlConcurrentConnectionsPerHost, bool NoVerifySSLCert);
// Called once at end of application (from newview/llappviewer.cpp by main thread),
// with purpose to stop curl threads, free curl resources and deinitialize curl.
@@ -221,11 +245,14 @@ class AICurlEasyRequest {
// 'new' never returned however and neither the constructor nor destructor of mBufferedCurlEasyRequest is called in this case.
// This might throw AICurlNoEasyHandle.
AICurlEasyRequest(void) :
mBufferedCurlEasyRequest(new AICurlPrivate::ThreadSafeBufferedCurlEasyRequest) { }
mBufferedCurlEasyRequest(new AICurlPrivate::ThreadSafeBufferedCurlEasyRequest) { AICurlInterface::Stats::AICurlEasyRequest_count++; }
public:
// Update stats.
~AICurlEasyRequest() { --AICurlInterface::Stats::AICurlEasyRequest_count; }
// Used for storing this object in a standard container (see MultiHandle::add_easy_request).
AICurlEasyRequest(AICurlEasyRequest const& orig) : mBufferedCurlEasyRequest(orig.mBufferedCurlEasyRequest) { }
AICurlEasyRequest(AICurlEasyRequest const& orig) : mBufferedCurlEasyRequest(orig.mBufferedCurlEasyRequest) { AICurlInterface::Stats::AICurlEasyRequest_count++; }
// For the rest, only allow read operations.
AIThreadSafeSimple<AICurlPrivate::BufferedCurlEasyRequest>& operator*(void) const { llassert(mBufferedCurlEasyRequest.get()); return *mBufferedCurlEasyRequest; }
@@ -271,11 +298,11 @@ class AICurlEasyRequest {
// then it's OK to construct a AICurlEasyRequest from it.
// Note that the external AICurlPrivate::BufferedCurlEasyRequestPtr needs its own locking, because
// it's not thread-safe in itself.
AICurlEasyRequest(AICurlPrivate::BufferedCurlEasyRequestPtr const& ptr) : mBufferedCurlEasyRequest(ptr) { }
AICurlEasyRequest(AICurlPrivate::BufferedCurlEasyRequestPtr const& ptr) : mBufferedCurlEasyRequest(ptr) { AICurlInterface::Stats::AICurlEasyRequest_count++; }
// This one is obviously dangerous. It's for use only in MultiHandle::check_msg_queue.
// See also the long comment in BufferedCurlEasyRequest::finalizeRequest with regard to CURLOPT_PRIVATE.
explicit AICurlEasyRequest(AICurlPrivate::ThreadSafeBufferedCurlEasyRequest* ptr) : mBufferedCurlEasyRequest(ptr) { }
explicit AICurlEasyRequest(AICurlPrivate::ThreadSafeBufferedCurlEasyRequest* ptr) : mBufferedCurlEasyRequest(ptr) { AICurlInterface::Stats::AICurlEasyRequest_count++; }
};
#define AICurlPrivate DONTUSE_AICurlPrivate

View File

@@ -249,6 +249,7 @@ AICurlEasyRequestStateMachine::AICurlEasyRequestStateMachine(void) :
mTimer(NULL), mTotalDelayTimeout(AIHTTPTimeoutPolicy::getDebugSettingsCurlTimeout().getTotalDelay())
{
Dout(dc::statemachine, "Calling AICurlEasyRequestStateMachine(void) [" << (void*)this << "] [" << (void*)mCurlEasyRequest.get() << "]");
AICurlInterface::Stats::AICurlEasyRequestStateMachine_count++;
}
void AICurlEasyRequestStateMachine::setTotalDelayTimeout(F32 totalDelayTimeout)
@@ -259,5 +260,6 @@ void AICurlEasyRequestStateMachine::setTotalDelayTimeout(F32 totalDelayTimeout)
AICurlEasyRequestStateMachine::~AICurlEasyRequestStateMachine()
{
Dout(dc::statemachine, "Calling ~AICurlEasyRequestStateMachine() [" << (void*)this << "] [" << (void*)mCurlEasyRequest.get() << "]");
--AICurlInterface::Stats::AICurlEasyRequestStateMachine_count;
}

View File

@@ -0,0 +1,171 @@
/**
* @file aiperhost.cpp
* @brief Implementation of PerHostRequestQueue
*
* Copyright (c) 2012, Aleric Inglewood.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* There are special exceptions to the terms and conditions of the GPL as
* it is applied to this Source Code. View the full text of the exception
* in the file doc/FLOSS-exception.txt in this software distribution.
*
* CHANGELOG
* and additional copyright holders.
*
* 04/11/2012
* Initial version, written by Aleric Inglewood @ SL
*/
#include "sys.h"
#include "aicurlperhost.h"
#include "aicurlthread.h"
#undef AICurlPrivate
namespace AICurlPrivate {
PerHostRequestQueue::threadsafe_instance_map_type PerHostRequestQueue::sInstanceMap;
U32 curl_concurrent_connections_per_host;
//static
PerHostRequestQueuePtr PerHostRequestQueue::instance(std::string const& hostname)
{
llassert(!hostname.empty());
instance_map_wat instance_map_w(sInstanceMap);
PerHostRequestQueue::iterator iter = instance_map_w->find(hostname);
if (iter == instance_map_w->end())
{
iter = instance_map_w->insert(instance_map_type::value_type(hostname, new RefCountedThreadSafePerHostRequestQueue)).first;
}
// Note: the creation of PerHostRequestQueuePtr MUST be protected by the lock on sInstanceMap (see release()).
return iter->second;
}
//static
void PerHostRequestQueue::release(PerHostRequestQueuePtr& instance)
{
if (instance->lastone())
{
instance_map_wat instance_map_w(sInstanceMap);
// It is possible that 'lastone' is not up to date anymore.
// Therefore, recheck the condition now that we have locked sInstanceMap.
if (!instance->lastone())
{
// Some other thread added this host in the meantime.
return;
}
// The reference in the map is the last one; that means there can't be any curl easy requests queued for this host.
llassert(PerHostRequestQueue_wat(*instance)->mQueuedRequests.empty());
// Find the host and erase it from the map.
iterator const end = instance_map_w->end();
for(iterator iter = instance_map_w->begin(); iter != end; ++iter)
{
if (instance == iter->second)
{
instance_map_w->erase(iter);
instance.reset();
return;
}
}
// We should always find the host.
llassert(false);
}
instance.reset();
}
bool PerHostRequestQueue::throttled() const
{
llassert(mAdded <= curl_concurrent_connections_per_host);
return mAdded == curl_concurrent_connections_per_host;
}
void PerHostRequestQueue::added_to_multi_handle(void)
{
llassert(mAdded < curl_concurrent_connections_per_host);
++mAdded;
}
void PerHostRequestQueue::removed_from_multi_handle(void)
{
--mAdded;
llassert(mAdded >= 0);
}
void PerHostRequestQueue::queue(AICurlEasyRequest const& easy_request)
{
mQueuedRequests.push_back(easy_request);
}
bool PerHostRequestQueue::cancel(AICurlEasyRequest const& easy_request)
{
std::deque<AICurlEasyRequest>::iterator const end = mQueuedRequests.end();
std::deque<AICurlEasyRequest>::iterator cur = std::find(mQueuedRequests.begin(), end, easy_request);
if (cur == end)
return false; // Not found.
// We can't use erase because that uses assignment to move elements, which is
// private because it isn't thread-safe for AICurlEasyRequest. Therefore, move
// the element that we found to the back with swap (could just swap with the
// end immediately, but I don't want to break the order in which requests where
// added). Swap is also not thread-safe, but OK here because it only touches the
// AICurlEasyRequest objects in the deque, and the deque is protected by the
// lock on the PerHostRequestQueue object.
std::deque<AICurlEasyRequest>::iterator prev = cur;
while (++cur != end)
{
prev->swap(*cur); // This is safe,
prev = cur;
}
mQueuedRequests.pop_back(); // if this is safe.
return true;
}
void PerHostRequestQueue::add_queued_to(curlthread::MultiHandle* multi_handle)
{
if (!mQueuedRequests.empty())
{
multi_handle->add_easy_request(mQueuedRequests.front());
mQueuedRequests.pop_front();
}
}
//static
void PerHostRequestQueue::purge(void)
{
instance_map_wat instance_map_w(sInstanceMap);
for (iterator host = instance_map_w->begin(); host != instance_map_w->end(); ++host)
{
Dout(dc::curl, "Purging queue of host \"" << host->first << "\".");
PerHostRequestQueue_wat(*host->second)->mQueuedRequests.clear();
}
}
// Friend functions of RefCountedThreadSafePerHostRequestQueue
void intrusive_ptr_add_ref(RefCountedThreadSafePerHostRequestQueue* per_host)
{
per_host->mReferenceCount++;
}
void intrusive_ptr_release(RefCountedThreadSafePerHostRequestQueue* per_host)
{
if (--per_host->mReferenceCount == 0)
{
delete per_host;
}
}
} // namespace AICurlPrivate

View File

@@ -0,0 +1,131 @@
/**
* @file aicurlperhost.h
* @brief Definition of class PerHostRequestQueue
*
* Copyright (c) 2012, Aleric Inglewood.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* There are special exceptions to the terms and conditions of the GPL as
* it is applied to this Source Code. View the full text of the exception
* in the file doc/FLOSS-exception.txt in this software distribution.
*
* CHANGELOG
* and additional copyright holders.
*
* 04/11/2012
* Initial version, written by Aleric Inglewood @ SL
*/
#ifndef AICURLPERHOST_H
#define AICURLPERHOST_H
#include "llerror.h" // llassert
#include <string>
#include <deque>
#include <map>
#include <boost/intrusive_ptr.hpp>
#include "aithreadsafe.h"
class AICurlEasyRequest;
namespace AICurlPrivate {
namespace curlthread { class MultiHandle; }
class PerHostRequestQueue;
class RefCountedThreadSafePerHostRequestQueue;
// PerHostRequestQueue objects are created by the curl thread and destructed by the main thread.
// We need locking.
typedef AIThreadSafeSimpleDC<PerHostRequestQueue> threadsafe_PerHostRequestQueue;
typedef AIAccessConst<PerHostRequestQueue> PerHostRequestQueue_crat;
typedef AIAccess<PerHostRequestQueue> PerHostRequestQueue_rat;
typedef AIAccess<PerHostRequestQueue> PerHostRequestQueue_wat;
// We can't put threadsafe_PerHostRequestQueue in a std::map because you can't copy a mutex.
// Therefore, use an intrusive pointer for the threadsafe type.
typedef boost::intrusive_ptr<RefCountedThreadSafePerHostRequestQueue> PerHostRequestQueuePtr;
//-----------------------------------------------------------------------------
// PerHostRequestQueue
// This class provides a static interface to create and maintain instances
// of PerHostRequestQueue objects, so that at any moment there is at most
// one instance per hostname. Those instances then are used to queue curl
// requests when the maximum number of connections for that host already
// have been reached.
class PerHostRequestQueue {
private:
typedef std::map<std::string, PerHostRequestQueuePtr> instance_map_type;
typedef AIThreadSafeSimpleDC<instance_map_type> threadsafe_instance_map_type;
typedef AIAccess<instance_map_type> instance_map_rat;
typedef AIAccess<instance_map_type> instance_map_wat;
static threadsafe_instance_map_type sInstanceMap; // Map of PerHostRequestQueue instances with the hostname as key.
friend class AIThreadSafeSimpleDC<PerHostRequestQueue>; //threadsafe_PerHostRequestQueue
PerHostRequestQueue(void) : mAdded(0) { }
public:
typedef instance_map_type::iterator iterator;
typedef instance_map_type::const_iterator const_iterator;
// Return (possibly create) a unique instance for the given hostname.
static PerHostRequestQueuePtr instance(std::string const& hostname);
// Release instance (object will be deleted if this was the last instance).
static void release(PerHostRequestQueuePtr& instance);
// Remove everything. Called upon viewer exit.
static void purge(void);
private:
typedef std::deque<AICurlEasyRequest> queued_request_type;
int mAdded; // Number of active easy handles with this host.
queued_request_type mQueuedRequests; // Waiting (throttled) requests.
public:
void added_to_multi_handle(void); // Called when an easy handle for this host has been added to the multi handle.
void removed_from_multi_handle(void); // Called when an easy handle for this host is removed again from the multi handle.
bool throttled(void) const; // Returns true if the maximum number of allowed requests for this host have been added to the multi handle.
void queue(AICurlEasyRequest const& easy_request); // Add easy_request to the queue.
bool cancel(AICurlEasyRequest const& easy_request); // Remove easy_request from the queue (if it's there).
void add_queued_to(curlthread::MultiHandle* mh); // Add queued easy handle (if any) to the multi handle. The request is removed from the queue,
// followed by either a call to added_to_multi_handle() or to queue() to add it back.
private:
// Disallow copying.
PerHostRequestQueue(PerHostRequestQueue const&) { }
};
class RefCountedThreadSafePerHostRequestQueue : public threadsafe_PerHostRequestQueue {
public:
RefCountedThreadSafePerHostRequestQueue(void) : mReferenceCount(0) { }
bool lastone(void) const { llassert(mReferenceCount >= 2); return mReferenceCount == 2; }
private:
// Used by PerHostRequestQueuePtr. Object is deleted when reference count reaches zero.
LLAtomicU32 mReferenceCount;
friend void intrusive_ptr_add_ref(RefCountedThreadSafePerHostRequestQueue* p);
friend void intrusive_ptr_release(RefCountedThreadSafePerHostRequestQueue* p);
};
extern U32 curl_concurrent_connections_per_host;
} // namespace AICurlPrivate
#endif // AICURLPERHOST_H

View File

@@ -34,9 +34,11 @@
#include <sstream>
#include "llatomic.h"
#include "llrefcount.h"
#include "aicurlperhost.h"
class AIHTTPHeaders;
class AIHTTPTimeoutPolicy;
class AICurlEasyRequest;
class AICurlEasyRequestStateMachine;
namespace AICurlPrivate {
@@ -94,7 +96,7 @@ class HTTPTimeout : public LLRefCount {
// Accessor.
bool has_stalled(void) const { return mStalled < sClockCount; }
// Called from CurlResponderBuffer::processOutput if a timeout occurred.
// Called from BufferedCurlEasyRequest::processOutput if a timeout occurred.
void print_diagnostics(CurlEasyRequest const* curl_easy_request);
#if defined(CWDEBUG) || defined(DEBUG_CURLIO)
@@ -111,20 +113,8 @@ class HTTPTimeout : public LLRefCount {
} // namespace curlthread
struct Stats {
static LLAtomicU32 easy_calls;
static LLAtomicU32 easy_errors;
static LLAtomicU32 easy_init_calls;
static LLAtomicU32 easy_init_errors;
static LLAtomicU32 easy_cleanup_calls;
static LLAtomicU32 multi_calls;
static LLAtomicU32 multi_errors;
static void print(void);
};
void handle_multi_error(CURLMcode code);
inline CURLMcode check_multi_code(CURLMcode code) { Stats::multi_calls++; if (code != CURLM_OK) handle_multi_error(code); return code; }
inline CURLMcode check_multi_code(CURLMcode code) { AICurlInterface::Stats::multi_calls++; if (code != CURLM_OK) handle_multi_error(code); return code; }
bool curlThreadIsRunning(void);
void wakeUpCurlThread(void);
@@ -248,7 +238,7 @@ class CurlEasyHandle : public boost::noncopyable, protected AICurlEasyHandleEven
// Always first call setErrorBuffer()!
static inline CURLcode check_easy_code(CURLcode code)
{
Stats::easy_calls++;
AICurlInterface::Stats::easy_calls++;
if (code != CURLE_OK)
handle_easy_error(code);
return code;
@@ -373,6 +363,7 @@ class CurlEasyRequest : public CurlEasyHandle {
AIHTTPTimeoutPolicy const* mTimeoutPolicy;
std::string mLowercaseHostname; // Lowercase hostname (canonicalized) extracted from the url.
PerHostRequestQueuePtr mPerHostPtr; // Pointer to the corresponding PerHostRequestQueue.
LLPointer<curlthread::HTTPTimeout> mTimeout;// Timeout administration object associated with last created CurlSocketInfo.
bool mTimeoutIsOrphan; // Set to true when mTimeout is not (yet) associated with a CurlSocketInfo.
#if defined(CWDEBUG) || defined(DEBUG_CURLIO)
@@ -414,6 +405,11 @@ class CurlEasyRequest : public CurlEasyHandle {
inline ThreadSafeBufferedCurlEasyRequest* get_lockobj(void);
inline ThreadSafeBufferedCurlEasyRequest const* get_lockobj(void) const;
// PerHost API.
PerHostRequestQueuePtr getPerHostPtr(void); // (Optionally create and) return a pointer to the unique
// PerHostRequestQueue corresponding to mLowercaseHostname.
bool removeFromPerHostQueue(AICurlEasyRequest const&) const; // Remove this request from the per-host queue, if queued at all.
// Returns true if it was queued.
protected:
// Pass events to parent.
/*virtual*/ void added_to_multi_handle(AICurlEasyRequest_wat& curl_easy_request_w);
@@ -444,7 +440,7 @@ class BufferedCurlEasyRequest : public CurlEasyRequest {
//void setBodyLimit(U32 size) { mBodyLimit = size; }
// Post-initialization, set the parent to pass the events to.
void send_buffer_events_to(AICurlResponderBufferEvents* target) { mBufferEventsTarget = target; }
void send_buffer_events_to(AIBufferedCurlEasyRequestEvents* target) { mBufferEventsTarget = target; }
protected:
// Events from this class.
@@ -462,7 +458,7 @@ class BufferedCurlEasyRequest : public CurlEasyRequest {
std::string mReason; // The "reason" from the same header line.
S32 mRequestTransferedBytes;
S32 mResponseTransferedBytes;
AICurlResponderBufferEvents* mBufferEventsTarget;
AIBufferedCurlEasyRequestEvents* mBufferEventsTarget;
public:
static LLChannelDescriptors const sChannels; // Channel object for mInput (channel out()) and mOutput (channel in()).
@@ -511,9 +507,11 @@ class ThreadSafeBufferedCurlEasyRequest : public AIThreadSafeSimple<BufferedCurl
// Throws AICurlNoEasyHandle.
ThreadSafeBufferedCurlEasyRequest(void) : mReferenceCount(0)
{ new (ptr()) BufferedCurlEasyRequest;
Dout(dc::curl, "Creating ThreadSafeBufferedCurlEasyRequest with this = " << (void*)this); }
virtual ~ThreadSafeBufferedCurlEasyRequest()
{ Dout(dc::curl, "Destructing ThreadSafeBufferedCurlEasyRequest with this = " << (void*)this); }
Dout(dc::curl, "Creating ThreadSafeBufferedCurlEasyRequest with this = " << (void*)this);
AICurlInterface::Stats::ThreadSafeBufferedCurlEasyRequest_count++; }
~ThreadSafeBufferedCurlEasyRequest()
{ Dout(dc::curl, "Destructing ThreadSafeBufferedCurlEasyRequest with this = " << (void*)this);
--AICurlInterface::Stats::ThreadSafeBufferedCurlEasyRequest_count; }
private:
LLAtomicU32 mReferenceCount;

View File

@@ -31,6 +31,7 @@
#include "linden_common.h"
#include "aicurlthread.h"
#include "aihttptimeoutpolicy.h"
#include "aicurlperhost.h"
#include "lltimer.h" // ms_sleep, get_clock_count
#include "llhttpstatuscodes.h"
#include "llbuffer.h"
@@ -1413,6 +1414,8 @@ void AICurlThread::run(void)
}
multi_handle_w->check_msg_queue();
}
// Clear the queued requests.
PerHostRequestQueue::purge();
}
AICurlMultiHandle::destroyInstance();
}
@@ -1527,31 +1530,39 @@ CURLMsg const* MultiHandle::info_read(int* msgs_in_queue) const
// never increment Stats::multi_errors. However, lets just increment multi_calls
// when it certainly wasn't an error...
if (ret)
Stats::multi_calls++;
AICurlInterface::Stats::multi_calls++;
return ret;
}
static U32 curl_concurrent_connections = 8; // Initialized on start up by startCurlThread().
static U32 curl_max_total_concurrent_connections = 32; // Initialized on start up by startCurlThread().
void MultiHandle::add_easy_request(AICurlEasyRequest const& easy_request)
{
if (mAddedEasyRequests.size() < curl_concurrent_connections) // Not throttled?
bool throttled = true; // Default.
PerHostRequestQueuePtr per_host;
{
CURLMcode ret;
AICurlEasyRequest_wat curl_easy_request_w(*easy_request);
per_host = curl_easy_request_w->getPerHostPtr();
PerHostRequestQueue_wat per_host_w(*per_host);
if (mAddedEasyRequests.size() < curl_max_total_concurrent_connections && !per_host_w->throttled())
{
AICurlEasyRequest_wat curl_easy_request_w(*easy_request);
curl_easy_request_w->set_timeout_opts();
ret = curl_easy_request_w->add_handle_to_multi(curl_easy_request_w, mMultiHandle);
}
if (ret == CURLM_OK)
{
std::pair<addedEasyRequests_type::iterator, bool> res = mAddedEasyRequests.insert(easy_request);
llassert(res.second); // May not have been added before.
Dout(dc::curl, "MultiHandle::add_easy_request: Added AICurlEasyRequest " << (void*)easy_request.get_ptr().get() << "; now processing " << mAddedEasyRequests.size() << " easy handles.");
return;
if (curl_easy_request_w->add_handle_to_multi(curl_easy_request_w, mMultiHandle) == CURLM_OK)
{
per_host_w->added_to_multi_handle(); // (About to be) added to mAddedEasyRequests.
throttled = false; // Fall through...
}
}
} // Release the lock on easy_request.
if (!throttled)
{ // ... to here.
std::pair<addedEasyRequests_type::iterator, bool> res = mAddedEasyRequests.insert(easy_request);
llassert(res.second); // May not have been added before.
Dout(dc::curl, "MultiHandle::add_easy_request: Added AICurlEasyRequest " << (void*)easy_request.get_ptr().get() << "; now processing " << mAddedEasyRequests.size() << " easy handles.");
return;
}
mQueuedRequests.push_back(easy_request);
// The request could not be added, we have to queue it.
PerHostRequestQueue_wat(*per_host)->queue(easy_request);
#ifdef SHOW_ASSERT
// Not active yet, but it's no longer an error if next we try to remove the request.
AICurlEasyRequest_wat(*easy_request)->mRemovedPerCommand = false;
@@ -1560,31 +1571,22 @@ void MultiHandle::add_easy_request(AICurlEasyRequest const& easy_request)
CURLMcode MultiHandle::remove_easy_request(AICurlEasyRequest const& easy_request, bool as_per_command)
{
AICurlEasyRequest_wat easy_request_w(*easy_request);
addedEasyRequests_type::iterator iter = mAddedEasyRequests.find(easy_request);
if (iter == mAddedEasyRequests.end())
{
// The request could be queued.
std::deque<AICurlEasyRequest>::iterator const end = mQueuedRequests.end();
std::deque<AICurlEasyRequest>::iterator cur = std::find(mQueuedRequests.begin(), end, easy_request);
if (cur != end)
{
// We can't use erase because that uses assignment to move elements, which is private because it isn't thread-safe for AICurlEasyRequest.
// Therefore, move the element that we found to the back with swap (could just swap with the end immediately,
// but I don't want to break the order in which requests where added). Swap is also not thread-safe, but OK here
// because it only touches the AICurlEasyRequest objects in the deque, and the deque is protected by the
// lock on MultiHandle.
std::deque<AICurlEasyRequest>::iterator prev = cur;
while (++cur != end)
{
prev->swap(*cur);
prev = cur;
}
#ifdef SHOW_ASSERT
// Now a second remove command would be an error again.
AICurlEasyRequest_wat(**prev)->mRemovedPerCommand = true;
bool removed =
#endif
mQueuedRequests.pop_back();
easy_request_w->removeFromPerHostQueue(easy_request);
#ifdef SHOW_ASSERT
if (removed)
{
// Now a second remove command would be an error again.
AICurlEasyRequest_wat(*easy_request)->mRemovedPerCommand = true;
}
#endif
return (CURLMcode)-2; // Was already removed before, or never added (queued).
}
return remove_easy_request(iter, as_per_command);
@@ -1593,9 +1595,12 @@ CURLMcode MultiHandle::remove_easy_request(AICurlEasyRequest const& easy_request
CURLMcode MultiHandle::remove_easy_request(addedEasyRequests_type::iterator const& iter, bool as_per_command)
{
CURLMcode res;
PerHostRequestQueuePtr per_host;
{
AICurlEasyRequest_wat curl_easy_request_w(**iter);
res = curl_easy_request_w->remove_handle_from_multi(curl_easy_request_w, mMultiHandle);
per_host = curl_easy_request_w->getPerHostPtr();
PerHostRequestQueue_wat(*per_host)->removed_from_multi_handle(); // (About to be) removed from mAddedEasyRequests.
#ifdef SHOW_ASSERT
curl_easy_request_w->mRemovedPerCommand = as_per_command;
#endif
@@ -1607,12 +1612,7 @@ CURLMcode MultiHandle::remove_easy_request(addedEasyRequests_type::iterator cons
Dout(dc::curl, "MultiHandle::remove_easy_request: Removed AICurlEasyRequest " << (void*)lockobj << "; now processing " << mAddedEasyRequests.size() << " easy handles.");
// Attempt to add a queued request, if any.
if (!mQueuedRequests.empty())
{
add_easy_request(mQueuedRequests.front());
mQueuedRequests.pop_front();
}
PerHostRequestQueue_wat(*per_host)->add_queued_to(this);
return res;
}
@@ -2043,6 +2043,7 @@ void BufferedCurlEasyRequest::setStatusAndReason(U32 status, std::string const&
{
mStatus = status;
mReason = reason;
AICurlInterface::Stats::status_count[AICurlInterface::Stats::status2index(mStatus)]++;
}
void BufferedCurlEasyRequest::processOutput(void)
@@ -2455,23 +2456,37 @@ void AICurlEasyRequest::removeRequest(void)
namespace AICurlInterface {
void startCurlThread(U32 CurlConcurrentConnections, bool NoVerifySSLCert)
void startCurlThread(U32 CurlMaxTotalConcurrentConnections, U32 CurlConcurrentConnectionsPerHost, bool NoVerifySSLCert)
{
using namespace AICurlPrivate;
using namespace AICurlPrivate::curlthread;
llassert(is_main_thread());
curl_concurrent_connections = CurlConcurrentConnections; // Debug Setting.
gNoVerifySSLCert = NoVerifySSLCert; // Debug Setting.
// Cache Debug Settings.
curl_max_total_concurrent_connections = CurlMaxTotalConcurrentConnections;
curl_concurrent_connections_per_host = CurlConcurrentConnectionsPerHost;
gNoVerifySSLCert = NoVerifySSLCert;
AICurlThread::sInstance = new AICurlThread;
AICurlThread::sInstance->start();
}
bool handleCurlConcurrentConnections(LLSD const& newvalue)
bool handleCurlMaxTotalConcurrentConnections(LLSD const& newvalue)
{
using namespace AICurlPrivate::curlthread;
curl_concurrent_connections = newvalue.asInteger();
llinfos << "CurlConcurrentConnections set to " << curl_concurrent_connections << llendl;
curl_max_total_concurrent_connections = newvalue.asInteger();
llinfos << "CurlMaxTotalConcurrentConnections set to " << curl_max_total_concurrent_connections << llendl;
return true;
}
bool handleCurlConcurrentConnectionsPerHost(LLSD const& newvalue)
{
using namespace AICurlPrivate;
curl_concurrent_connections_per_host = newvalue.asInteger();
llinfos << "CurlConcurrentConnectionsPerHost set to " << curl_concurrent_connections_per_host << llendl;
return true;
}

View File

@@ -33,7 +33,6 @@
#include "aicurl.h"
#include <vector>
#include <deque>
#undef AICurlPrivate
@@ -81,7 +80,7 @@ class MultiHandle : public CurlMultiHandle
// Store result and trigger events for easy request.
void finish_easy_request(AICurlEasyRequest const& easy_request, CURLcode result);
// Remove easy request at iter (must exist).
// Note that it's possible that a new request from mQueuedRequests is inserted before iter.
// Note that it's possible that a new request from a PerHostRequestQueue::mQueuedRequests is inserted before iter.
CURLMcode remove_easy_request(addedEasyRequests_type::iterator const& iter, bool as_per_command);
static int socket_callback(CURL* easy, curl_socket_t s, int action, void* userp, void* socketp);
@@ -103,10 +102,6 @@ class MultiHandle : public CurlMultiHandle
PollSet* mReadPollSet;
PollSet* mWritePollSet;
private:
// Temporary throttling hack.
std::deque<AICurlEasyRequest> mQueuedRequests; // Waiting (throttled) requests.
};
} // namespace curlthread

View File

@@ -182,7 +182,7 @@ private:
std::vector<LLUUID> mAgentIDs;
// Need the headers to look up Expires: and Retry-After:
AIHTTPReceivedHeaders mHeaders;
virtual bool needsHeaders(void) const { return true; }
public:
virtual AIHTTPTimeoutPolicy const& getHTTPTimeoutPolicy(void) const { return avatarNameResponder_timeout; }
@@ -191,17 +191,10 @@ public:
: mAgentIDs(agent_ids)
{ }
/*virtual*/ bool needsHeaders(void) const { return true; }
/*virtual*/ void completedHeaders(U32 status, std::string const& reason, AIHTTPReceivedHeaders const& headers)
{
mHeaders = headers;
}
/*virtual*/ void result(const LLSD& content)
{
// Pull expiration out of headers if available
F64 expires = LLAvatarNameCache::nameExpirationFromHeaders(mHeaders);
F64 expires = LLAvatarNameCache::nameExpirationFromHeaders(mReceivedHeaders);
F64 now = LLFrameTimer::getTotalSeconds();
LLSD agents = content["agents"];

View File

@@ -264,65 +264,76 @@ void LLHTTPClient::get(std::string const& url, LLSD const& query, ResponderPtr r
LLHTTPClient::ResponderBase::ResponderBase(void) : mReferenceCount(0), mCode(CURLE_FAILED_INIT), mFinished(false)
{
DoutEntering(dc::curl, "AICurlInterface::Responder() with this = " << (void*)this);
DoutEntering(dc::curl, "AICurlInterface::Responder() with this = " << (void*)this);
AICurlInterface::Stats::ResponderBase_count++;
}
LLHTTPClient::ResponderBase::~ResponderBase()
{
DoutEntering(dc::curl, "AICurlInterface::ResponderBase::~ResponderBase() with this = " << (void*)this << "; mReferenceCount = " << mReferenceCount);
llassert(mReferenceCount == 0);
DoutEntering(dc::curl, "AICurlInterface::ResponderBase::~ResponderBase() with this = " << (void*)this << "; mReferenceCount = " << mReferenceCount);
llassert(mReferenceCount == 0);
--AICurlInterface::Stats::ResponderBase_count;
}
void LLHTTPClient::ResponderBase::setURL(std::string const& url)
{
// setURL is called from llhttpclient.cpp (request()), before calling any of the below (of course).
// We don't need locking here therefore; it's a case of initializing before use.
mURL = url;
// setURL is called from llhttpclient.cpp (request()), before calling any of the below (of course).
// We don't need locking here therefore; it's a case of initializing before use.
mURL = url;
}
AIHTTPTimeoutPolicy const& LLHTTPClient::ResponderBase::getHTTPTimeoutPolicy(void) const
{
return AIHTTPTimeoutPolicy::getDebugSettingsCurlTimeout();
return AIHTTPTimeoutPolicy::getDebugSettingsCurlTimeout();
}
void LLHTTPClient::ResponderBase::decode_llsd_body(U32 status, std::string const& reason, LLChannelDescriptors const& channels, buffer_ptr_t const& buffer, LLSD& content)
{
// If the status indicates success (and we get here) then we expect the body to be LLSD.
bool const should_be_llsd = (200 <= status && status < 300);
if (should_be_llsd)
{
LLBufferStream istr(channels, buffer.get());
if (LLSDSerialize::fromXML(content, istr) == LLSDParser::PARSE_FAILURE)
AICurlInterface::Stats::llsd_body_count++;
// If the status indicates success (and we get here) then we expect the body to be LLSD.
bool const should_be_llsd = (200 <= status && status < 300);
if (should_be_llsd)
{
// Unfortunately we can't show the body of the message... I think this is a pretty serious error
// though, so if this ever happens it has to be investigated by making a copy of the buffer
// before serializing it, as is done below.
llwarns << "Failed to deserialize LLSD. " << mURL << " [" << status << "]: " << reason << llendl;
LLBufferStream istr(channels, buffer.get());
if (LLSDSerialize::fromXML(content, istr) == LLSDParser::PARSE_FAILURE)
{
// Unfortunately we can't show the body of the message... I think this is a pretty serious error
// though, so if this ever happens it has to be investigated by making a copy of the buffer
// before serializing it, as is done below.
llwarns << "Failed to deserialize LLSD. " << mURL << " [" << status << "]: " << reason << llendl;
AICurlInterface::Stats::llsd_body_parse_error++;
}
// LLSDSerialize::fromXML destructed buffer, we can't initialize content now.
return;
}
// LLSDSerialize::fromXML destructed buffer, we can't initialize content now.
return;
}
// Put the body in content as-is.
std::stringstream ss;
buffer->writeChannelTo(ss, channels.in());
content = ss.str();
// Put the body in content as-is.
std::stringstream ss;
buffer->writeChannelTo(ss, channels.in());
content = ss.str();
#ifdef SHOW_ASSERT
if (!should_be_llsd)
{
// Make sure that the server indeed never returns LLSD as body when the http status is an error.
LLSD dummy;
bool server_sent_llsd_with_http_error = LLSDSerialize::fromXML(dummy, ss) > 0;
if (server_sent_llsd_with_http_error)
if (!should_be_llsd)
{
llwarns << "The server sent us a response with http status " << status << " and LLSD(!) body: \"" << ss.str() << "\"!" << llendl;
char const* str = ss.str().c_str();
// Make sure that the server indeed never returns LLSD as body when the http status is an error.
LLSD dummy;
bool server_sent_llsd_with_http_error =
strncmp(str, "<!DOCTYPE", 9) && // LLSD does never start with "<!"; short circuits 97% of the replies.
strncmp(str, "cap not found:", 14) && // Most of the other 3%.
str[0] && // Empty happens too and aint LLSD either.
strncmp(str, "Not Found", 9) &&
LLSDSerialize::fromXML(dummy, ss) > 0;
if (server_sent_llsd_with_http_error)
{
llwarns << "The server sent us a response with http status " << status << " and LLSD(!) body: \"" << ss.str() << "\"!" << llendl;
}
llassert(!server_sent_llsd_with_http_error);
}
llassert(!server_sent_llsd_with_http_error);
}
#endif
}
void LLHTTPClient::ResponderBase::decode_raw_body(U32 status, std::string const& reason, LLChannelDescriptors const& channels, buffer_ptr_t const& buffer, std::string& content)
{
AICurlInterface::Stats::raw_body_count++;
LLMutexLock lock(buffer->getMutex());
LLBufferArray::const_segment_iterator_t const end = buffer->endSegment();
for (LLBufferArray::const_segment_iterator_t iter = buffer->beginSegment(); iter != end; ++iter)

View File

@@ -59,8 +59,8 @@ struct AITransferInfo {
F64 mSpeedDownload;
};
// Events generated by AICurlPrivate::CurlResponderBuffer.
struct AICurlResponderBufferEvents {
// Events generated by AICurlPrivate::BufferedCurlEasyRequest
struct AIBufferedCurlEasyRequestEvents {
virtual void received_HTTP_header(void) = 0; // For example "HTTP/1.0 200 OK", the first header of a reply.
virtual void received_header(std::string const& key, std::string const& value) = 0; // Subsequent headers.
virtual void completed_headers(U32 status, std::string const& reason, AITransferInfo* info) = 0; // Transaction completed.
@@ -79,13 +79,13 @@ public:
* The life cycle of classes derived from this class is as follows:
* They are allocated with new on the line where get(), getByteRange() or post() is called,
* and the pointer to the allocated object is then put in a reference counting ResponderPtr.
* This ResponderPtr is passed to CurlResponderBuffer::prepRequest which stores it in its
* This ResponderPtr is passed to BufferedCurlEasyRequest::prepRequest which stores it in its
* member mResponder. Hence, the life time of a Responder is never longer than its
* associated CurlResponderBuffer, however, if everything works correctly, then normally a
* responder is deleted in CurlResponderBuffer::removed_from_multi_handle by setting
* associated BufferedCurlEasyRequest, however, if everything works correctly, then normally a
* responder is deleted in BufferedCurlEasyRequest::processOutput by setting
* mReponder to NULL.
*/
class ResponderBase : public AICurlResponderBufferEvents {
class ResponderBase : public AIBufferedCurlEasyRequestEvents {
public:
typedef boost::shared_ptr<LLBufferArray> buffer_ptr_t;
@@ -121,7 +121,7 @@ public:
std::string const& getURL(void) const { return mURL; }
CURLcode result_code(void) const { return mCode; }
// Called by CurlResponderBuffer::timed_out or CurlResponderBuffer::processOutput.
// Called by BufferedCurlEasyRequest::timed_out or BufferedCurlEasyRequest::processOutput.
virtual void finished(CURLcode code, U32 http_status, std::string const& reason, LLChannelDescriptors const& channels, buffer_ptr_t const& buffer) = 0;
// Return true if the curl thread is done with this transaction.
@@ -132,7 +132,8 @@ public:
bool is_finished(void) const { return mFinished; }
protected:
// AICurlResponderBufferEvents
// AIBufferedCurlEasyRequestEvents
// These three events are only actually called for classes that implement a needsHeaders() that returns true.
// Called when the "HTTP/1.x <status> <reason>" header is received.
/*virtual*/ void received_HTTP_header(void)
@@ -167,6 +168,7 @@ public:
protected:
// Derived classes can override this to get the HTML headers that were received, when the message is completed.
// Only actually called for classes that implement a needsHeaders() that returns true.
virtual void completedHeaders(U32 status, std::string const& reason, AIHTTPReceivedHeaders const& headers)
{
// The default does nothing.

View File

@@ -352,8 +352,11 @@ void LLLineEditor::setText(const LLStringExplicit &new_text)
}
setCursor(llmin((S32)mText.length(), getCursor()));
// Set current history line to end of history.
mCurrentHistoryLine = mLineHistory.end() - 1;
if (!mLineHistory.empty())
{
// Set current history line to end of history.
mCurrentHistoryLine = mLineHistory.end() - 1;
}
mPrevText = mText;
}

View File

@@ -4205,15 +4205,26 @@
<key>Value</key>
<integer>0</integer>
</map>
<key>CurlConcurrentConnections</key>
<key>CurlMaxTotalConcurrentConnections</key>
<map>
<key>Comment</key>
<string>Maximum number of simultaneous curl connections</string>
<string>Maximum total number of simultaneous curl connections</string>
<key>Persist</key>
<integer>1</integer>
<key>Type</key>
<string>U32</string>
<key>Value</key>
<integer>64</integer>
</map>
<key>CurlConcurrentConnectionsPerHost</key>
<map>
<key>Comment</key>
<string>Maximum number of simultaneous curl connections per host</string>
<key>Persist</key>
<integer>0</integer>
<key>Type</key>
<string>U32</string>
<key>Value</key>
<integer>16</integer>
</map>
<key>CurlMaximumNumberOfHandles</key>

View File

@@ -617,7 +617,7 @@ bool LLAppViewer::init()
// <edit>
// Curl must be initialized before any thread is running.
AICurlInterface::initCurl(&AIStateMachine::flush);
AICurlInterface::initCurl();
// Logging is initialized. Now it's safe to start the error thread.
startErrorThread();
@@ -1836,7 +1836,9 @@ bool LLAppViewer::initThreads()
LLWatchdog::getInstance()->init(watchdog_killer_callback);
}
AICurlInterface::startCurlThread(gSavedSettings.getU32("CurlConcurrentConnections"), gSavedSettings.getBOOL("NoVerifySSLCert"));
AICurlInterface::startCurlThread(gSavedSettings.getU32("CurlMaxTotalConcurrentConnections"),
gSavedSettings.getU32("CurlConcurrentConnectionsPerHost"),
gSavedSettings.getBOOL("NoVerifySSLCert"));
LLImage::initClass();

View File

@@ -796,7 +796,8 @@ void settings_setup_listeners()
gSavedSettings.getControl("AscentAvatarYModifier")->getSignal()->connect(boost::bind(&handleAscentAvatarModifier, _2));
gSavedSettings.getControl("AscentAvatarZModifier")->getSignal()->connect(boost::bind(&handleAscentAvatarModifier, _2));
gSavedSettings.getControl("CurlConcurrentConnections")->getSignal()->connect(boost::bind(&AICurlInterface::handleCurlConcurrentConnections, _2));
gSavedSettings.getControl("CurlMaxTotalConcurrentConnections")->getSignal()->connect(boost::bind(&AICurlInterface::handleCurlMaxTotalConcurrentConnections, _2));
gSavedSettings.getControl("CurlConcurrentConnectionsPerHost")->getSignal()->connect(boost::bind(&AICurlInterface::handleCurlConcurrentConnectionsPerHost, _2));
gSavedSettings.getControl("NoVerifySSLCert")->getSignal()->connect(boost::bind(&AICurlInterface::handleNoVerifySSLCert, _2));
gSavedSettings.getControl("CurlTimeoutDNSLookup")->getValidateSignal()->connect(boost::bind(&validateCurlTimeoutDNSLookup, _2));

View File

@@ -1773,7 +1773,7 @@ U32 LLViewerObject::processUpdateMessage(LLMessageSystem *mesgsys,
static std::map<LLUUID,bool> state_map;
std::map<LLUUID,bool>::iterator it = state_map.find(getID());
if(it == state_map.end() || (!it->second && sent_parentp))
if(it != state_map.end() && (!it->second && sent_parentp))
{
it->second = sent_parentp != NULL;
LL_INFOS("Attachment") << getID() << " ("<<getAttachmentPointName()<<") has " << (sent_parentp ? "" : "no ") << "parent." << llendl;

View File

@@ -105,6 +105,7 @@ public:
XMLRPC_REQUEST response(void) const { return mResponse; }
LLXMLRPCValue responseValue(void) const;
/*virtual*/ bool needsHeaders(void) const { return true; }
/*virtual*/ void received_HTTP_header(void) { mReceivedHTTPHeader = true; LLHTTPClient::ResponderBase::received_HTTP_header(); }
/*virtual*/ void completed_headers(U32 status, std::string const& reason, AITransferInfo* info);
/*virtual*/ void completedRaw(U32 status, std::string const& reason, LLChannelDescriptors const& channels, buffer_ptr_t const& buffer);