Merge branch 'master' of git://github.com/AlericInglewood/SingularityViewer
Conflicts: indra/llmessage/llavatarnamecache.cpp
This commit is contained in:
@@ -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());
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
171
indra/llmessage/aicurlperhost.cpp
Normal file
171
indra/llmessage/aicurlperhost.cpp
Normal 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
|
||||
131
indra/llmessage/aicurlperhost.h
Normal file
131
indra/llmessage/aicurlperhost.h
Normal 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
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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"];
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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();
|
||||
|
||||
|
||||
@@ -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));
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user