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());
|
AIStateMachine& statemachine(iter->statemachine());
|
||||||
if (statemachine.abortable())
|
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
|
// 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).
|
// also call abort() on it when it is aborted itself).
|
||||||
if (statemachine.running())
|
if (statemachine.running())
|
||||||
@@ -619,12 +619,10 @@ void AIStateMachine::flush(void)
|
|||||||
AIReadAccess<csme_type> csme_r(sContinuedStateMachinesAndMainloopEnabled);
|
AIReadAccess<csme_type> csme_r(sContinuedStateMachinesAndMainloopEnabled);
|
||||||
add_continued_statemachines(csme_r);
|
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 "llapr.h"
|
||||||
#include "llscopedvolatileaprpool.h"
|
#include "llscopedvolatileaprpool.h"
|
||||||
|
|
||||||
|
LLFastTimer::DeclareTimer FT_WAIT_FOR_SCOPEDLOCK("LLScopedLock");
|
||||||
|
|
||||||
//---------------------------------------------------------------------
|
//---------------------------------------------------------------------
|
||||||
//
|
//
|
||||||
// LLScopedLock
|
// LLScopedLock
|
||||||
//
|
//
|
||||||
LLScopedLock::LLScopedLock(apr_thread_mutex_t* mutex) : mMutex(mutex)
|
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;
|
mAPRCondp = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
LLFastTimer::DeclareTimer FT_WAIT_FOR_CONDITION("LLCondition::wait()");
|
||||||
|
|
||||||
void 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()
|
void LLCondition::signal()
|
||||||
@@ -409,6 +419,8 @@ bool LLMutexBase::isSelfLocked() const
|
|||||||
return mLockingThread.equals_current_thread_inline();
|
return mLockingThread.equals_current_thread_inline();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
LLFastTimer::DeclareTimer FT_WAIT_FOR_MUTEX("LLMutexBase::lock()");
|
||||||
|
|
||||||
void LLMutexBase::lock()
|
void LLMutexBase::lock()
|
||||||
{
|
{
|
||||||
if (mLockingThread.equals_current_thread_inline())
|
if (mLockingThread.equals_current_thread_inline())
|
||||||
@@ -417,7 +429,18 @@ void LLMutexBase::lock()
|
|||||||
return;
|
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();
|
mLockingThread.reset_inline();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -24,6 +24,7 @@ include_directories(
|
|||||||
set(llmessage_SOURCE_FILES
|
set(llmessage_SOURCE_FILES
|
||||||
aicurl.cpp
|
aicurl.cpp
|
||||||
aicurleasyrequeststatemachine.cpp
|
aicurleasyrequeststatemachine.cpp
|
||||||
|
aicurlperhost.cpp
|
||||||
aicurlthread.cpp
|
aicurlthread.cpp
|
||||||
aihttpheaders.cpp
|
aihttpheaders.cpp
|
||||||
aihttptimeoutpolicy.cpp
|
aihttptimeoutpolicy.cpp
|
||||||
@@ -110,6 +111,7 @@ set(llmessage_HEADER_FILES
|
|||||||
aicurl.h
|
aicurl.h
|
||||||
aicurleasyrequeststatemachine.h
|
aicurleasyrequeststatemachine.h
|
||||||
aicurlprivate.h
|
aicurlprivate.h
|
||||||
|
aicurlperhost.h
|
||||||
aicurlthread.h
|
aicurlthread.h
|
||||||
aihttpheaders.h
|
aihttpheaders.h
|
||||||
aihttptimeoutpolicy.h
|
aihttptimeoutpolicy.h
|
||||||
|
|||||||
@@ -58,6 +58,7 @@
|
|||||||
#include "aihttpheaders.h"
|
#include "aihttpheaders.h"
|
||||||
#include "aihttptimeoutpolicy.h"
|
#include "aihttptimeoutpolicy.h"
|
||||||
#include "aicurleasyrequeststatemachine.h"
|
#include "aicurleasyrequeststatemachine.h"
|
||||||
|
#include "aicurlperhost.h"
|
||||||
|
|
||||||
//==================================================================================
|
//==================================================================================
|
||||||
// Debug Settings
|
// Debug Settings
|
||||||
@@ -90,7 +91,6 @@ enum gSSLlib_type {
|
|||||||
// No locking needed: initialized before threads are created, and subsequently only read.
|
// No locking needed: initialized before threads are created, and subsequently only read.
|
||||||
gSSLlib_type gSSLlib;
|
gSSLlib_type gSSLlib;
|
||||||
bool gSetoptParamsNeedDup;
|
bool gSetoptParamsNeedDup;
|
||||||
void (*statemachines_flush_hook)(void);
|
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
@@ -290,10 +290,39 @@ static unsigned int encoded_version(int major, int minor, int patch)
|
|||||||
|
|
||||||
namespace AICurlInterface {
|
namespace AICurlInterface {
|
||||||
|
|
||||||
// MAIN-THREAD
|
//static
|
||||||
void initCurl(void (*flush_hook)())
|
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.
|
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);
|
CURLcode res = curl_global_init(CURL_GLOBAL_ALL);
|
||||||
@@ -376,9 +405,6 @@ void initCurl(void (*flush_hook)())
|
|||||||
}
|
}
|
||||||
llassert_always(!gSetoptParamsNeedDup); // Might add support later.
|
llassert_always(!gSetoptParamsNeedDup); // Might add support later.
|
||||||
}
|
}
|
||||||
|
|
||||||
// Called in cleanupCurl.
|
|
||||||
statemachines_flush_hook = flush_hook;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// MAIN-THREAD
|
// MAIN-THREAD
|
||||||
@@ -391,8 +417,7 @@ void cleanupCurl(void)
|
|||||||
stopCurlThread();
|
stopCurlThread();
|
||||||
if (CurlMultiHandle::getTotalMultiHandles() != 0)
|
if (CurlMultiHandle::getTotalMultiHandles() != 0)
|
||||||
llwarns << "Not all CurlMultiHandle objects were destroyed!" << llendl;
|
llwarns << "Not all CurlMultiHandle objects were destroyed!" << llendl;
|
||||||
if (statemachines_flush_hook)
|
AIStateMachine::flush();
|
||||||
(*statemachines_flush_hook)();
|
|
||||||
Stats::print();
|
Stats::print();
|
||||||
ssl_cleanup();
|
ssl_cleanup();
|
||||||
|
|
||||||
@@ -422,6 +447,58 @@ void setCAPath(std::string const& path)
|
|||||||
CertificateAuthority_w->path = 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
|
} // namespace AICurlInterface
|
||||||
//==================================================================================
|
//==================================================================================
|
||||||
|
|
||||||
@@ -431,31 +508,13 @@ void setCAPath(std::string const& path)
|
|||||||
|
|
||||||
namespace AICurlPrivate {
|
namespace AICurlPrivate {
|
||||||
|
|
||||||
|
using AICurlInterface::Stats;
|
||||||
|
|
||||||
#if defined(CWDEBUG) || defined(DEBUG_CURLIO)
|
#if defined(CWDEBUG) || defined(DEBUG_CURLIO)
|
||||||
// CURLOPT_DEBUGFUNCTION function.
|
// CURLOPT_DEBUGFUNCTION function.
|
||||||
extern int debug_callback(CURL*, curl_infotype infotype, char* buf, size_t size, void* user_ptr);
|
extern int debug_callback(CURL*, curl_infotype infotype, char* buf, size_t size, void* user_ptr);
|
||||||
#endif
|
#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
|
// THREAD-SAFE
|
||||||
void handle_multi_error(CURLMcode code)
|
void handle_multi_error(CURLMcode code)
|
||||||
{
|
{
|
||||||
@@ -856,6 +915,10 @@ CurlEasyRequest::~CurlEasyRequest()
|
|||||||
// be available anymore.
|
// be available anymore.
|
||||||
send_handle_events_to(NULL);
|
send_handle_events_to(NULL);
|
||||||
revokeCallbacks();
|
revokeCallbacks();
|
||||||
|
if (mPerHostPtr)
|
||||||
|
{
|
||||||
|
PerHostRequestQueue::release(mPerHostPtr);
|
||||||
|
}
|
||||||
// This wasn't freed yet if the request never finished.
|
// This wasn't freed yet if the request never finished.
|
||||||
curl_slist_free_all(mHeaders);
|
curl_slist_free_all(mHeaders);
|
||||||
}
|
}
|
||||||
@@ -1054,6 +1117,7 @@ void CurlEasyRequest::finalizeRequest(std::string const& url, AIHTTPTimeoutPolic
|
|||||||
#endif
|
#endif
|
||||||
setopt(CURLOPT_HTTPHEADER, mHeaders);
|
setopt(CURLOPT_HTTPHEADER, mHeaders);
|
||||||
setoptString(CURLOPT_URL, url);
|
setoptString(CURLOPT_URL, url);
|
||||||
|
llassert(!mPerHostPtr);
|
||||||
mLowercaseHostname = extract_canonical_hostname(url);
|
mLowercaseHostname = extract_canonical_hostname(url);
|
||||||
mTimeoutPolicy = &policy;
|
mTimeoutPolicy = &policy;
|
||||||
state_machine->setTotalDelayTimeout(policy.getTotalDelay());
|
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
|
// BufferedCurlEasyRequest
|
||||||
|
|
||||||
@@ -1180,6 +1262,7 @@ LLChannelDescriptors const BufferedCurlEasyRequest::sChannels;
|
|||||||
|
|
||||||
BufferedCurlEasyRequest::BufferedCurlEasyRequest() : mRequestTransferedBytes(0), mResponseTransferedBytes(0), mBufferEventsTarget(NULL)
|
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)
|
#define llmaybeerrs lllog(LLApp::isRunning() ? LLError::LEVEL_ERROR : LLError::LEVEL_WARN, NULL, NULL, false, true)
|
||||||
@@ -1207,6 +1290,7 @@ BufferedCurlEasyRequest::~BufferedCurlEasyRequest()
|
|||||||
timed_out();
|
timed_out();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
--AICurlInterface::Stats::BufferedCurlEasyRequest_count;
|
||||||
}
|
}
|
||||||
|
|
||||||
void BufferedCurlEasyRequest::timed_out(void)
|
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.
|
// Things defined in this namespace are called from elsewhere in the viewer code.
|
||||||
namespace AICurlInterface {
|
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.
|
// Global functions.
|
||||||
|
|
||||||
// Called to handle changes in Debug Settings.
|
// 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);
|
bool handleNoVerifySSLCert(LLSD const& newvalue);
|
||||||
|
|
||||||
// Called once at start of application (from newview/llappviewer.cpp by main thread (before threads are created)),
|
// Called once at start of application (from newview/llappviewer.cpp by main thread (before threads are created)),
|
||||||
// with main purpose to initialize curl.
|
// 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.
|
// 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),
|
// 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.
|
// 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.
|
// 'new' never returned however and neither the constructor nor destructor of mBufferedCurlEasyRequest is called in this case.
|
||||||
// This might throw AICurlNoEasyHandle.
|
// This might throw AICurlNoEasyHandle.
|
||||||
AICurlEasyRequest(void) :
|
AICurlEasyRequest(void) :
|
||||||
mBufferedCurlEasyRequest(new AICurlPrivate::ThreadSafeBufferedCurlEasyRequest) { }
|
mBufferedCurlEasyRequest(new AICurlPrivate::ThreadSafeBufferedCurlEasyRequest) { AICurlInterface::Stats::AICurlEasyRequest_count++; }
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
// Update stats.
|
||||||
|
~AICurlEasyRequest() { --AICurlInterface::Stats::AICurlEasyRequest_count; }
|
||||||
|
|
||||||
// Used for storing this object in a standard container (see MultiHandle::add_easy_request).
|
// 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.
|
// For the rest, only allow read operations.
|
||||||
AIThreadSafeSimple<AICurlPrivate::BufferedCurlEasyRequest>& operator*(void) const { llassert(mBufferedCurlEasyRequest.get()); return *mBufferedCurlEasyRequest; }
|
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.
|
// then it's OK to construct a AICurlEasyRequest from it.
|
||||||
// Note that the external AICurlPrivate::BufferedCurlEasyRequestPtr needs its own locking, because
|
// Note that the external AICurlPrivate::BufferedCurlEasyRequestPtr needs its own locking, because
|
||||||
// it's not thread-safe in itself.
|
// 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.
|
// 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.
|
// 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
|
#define AICurlPrivate DONTUSE_AICurlPrivate
|
||||||
|
|||||||
@@ -249,6 +249,7 @@ AICurlEasyRequestStateMachine::AICurlEasyRequestStateMachine(void) :
|
|||||||
mTimer(NULL), mTotalDelayTimeout(AIHTTPTimeoutPolicy::getDebugSettingsCurlTimeout().getTotalDelay())
|
mTimer(NULL), mTotalDelayTimeout(AIHTTPTimeoutPolicy::getDebugSettingsCurlTimeout().getTotalDelay())
|
||||||
{
|
{
|
||||||
Dout(dc::statemachine, "Calling AICurlEasyRequestStateMachine(void) [" << (void*)this << "] [" << (void*)mCurlEasyRequest.get() << "]");
|
Dout(dc::statemachine, "Calling AICurlEasyRequestStateMachine(void) [" << (void*)this << "] [" << (void*)mCurlEasyRequest.get() << "]");
|
||||||
|
AICurlInterface::Stats::AICurlEasyRequestStateMachine_count++;
|
||||||
}
|
}
|
||||||
|
|
||||||
void AICurlEasyRequestStateMachine::setTotalDelayTimeout(F32 totalDelayTimeout)
|
void AICurlEasyRequestStateMachine::setTotalDelayTimeout(F32 totalDelayTimeout)
|
||||||
@@ -259,5 +260,6 @@ void AICurlEasyRequestStateMachine::setTotalDelayTimeout(F32 totalDelayTimeout)
|
|||||||
AICurlEasyRequestStateMachine::~AICurlEasyRequestStateMachine()
|
AICurlEasyRequestStateMachine::~AICurlEasyRequestStateMachine()
|
||||||
{
|
{
|
||||||
Dout(dc::statemachine, "Calling ~AICurlEasyRequestStateMachine() [" << (void*)this << "] [" << (void*)mCurlEasyRequest.get() << "]");
|
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 <sstream>
|
||||||
#include "llatomic.h"
|
#include "llatomic.h"
|
||||||
#include "llrefcount.h"
|
#include "llrefcount.h"
|
||||||
|
#include "aicurlperhost.h"
|
||||||
|
|
||||||
class AIHTTPHeaders;
|
class AIHTTPHeaders;
|
||||||
class AIHTTPTimeoutPolicy;
|
class AIHTTPTimeoutPolicy;
|
||||||
|
class AICurlEasyRequest;
|
||||||
class AICurlEasyRequestStateMachine;
|
class AICurlEasyRequestStateMachine;
|
||||||
|
|
||||||
namespace AICurlPrivate {
|
namespace AICurlPrivate {
|
||||||
@@ -94,7 +96,7 @@ class HTTPTimeout : public LLRefCount {
|
|||||||
// Accessor.
|
// Accessor.
|
||||||
bool has_stalled(void) const { return mStalled < sClockCount; }
|
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);
|
void print_diagnostics(CurlEasyRequest const* curl_easy_request);
|
||||||
|
|
||||||
#if defined(CWDEBUG) || defined(DEBUG_CURLIO)
|
#if defined(CWDEBUG) || defined(DEBUG_CURLIO)
|
||||||
@@ -111,20 +113,8 @@ class HTTPTimeout : public LLRefCount {
|
|||||||
|
|
||||||
} // namespace curlthread
|
} // 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);
|
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);
|
bool curlThreadIsRunning(void);
|
||||||
void wakeUpCurlThread(void);
|
void wakeUpCurlThread(void);
|
||||||
@@ -248,7 +238,7 @@ class CurlEasyHandle : public boost::noncopyable, protected AICurlEasyHandleEven
|
|||||||
// Always first call setErrorBuffer()!
|
// Always first call setErrorBuffer()!
|
||||||
static inline CURLcode check_easy_code(CURLcode code)
|
static inline CURLcode check_easy_code(CURLcode code)
|
||||||
{
|
{
|
||||||
Stats::easy_calls++;
|
AICurlInterface::Stats::easy_calls++;
|
||||||
if (code != CURLE_OK)
|
if (code != CURLE_OK)
|
||||||
handle_easy_error(code);
|
handle_easy_error(code);
|
||||||
return code;
|
return code;
|
||||||
@@ -373,6 +363,7 @@ class CurlEasyRequest : public CurlEasyHandle {
|
|||||||
|
|
||||||
AIHTTPTimeoutPolicy const* mTimeoutPolicy;
|
AIHTTPTimeoutPolicy const* mTimeoutPolicy;
|
||||||
std::string mLowercaseHostname; // Lowercase hostname (canonicalized) extracted from the url.
|
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.
|
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.
|
bool mTimeoutIsOrphan; // Set to true when mTimeout is not (yet) associated with a CurlSocketInfo.
|
||||||
#if defined(CWDEBUG) || defined(DEBUG_CURLIO)
|
#if defined(CWDEBUG) || defined(DEBUG_CURLIO)
|
||||||
@@ -414,6 +405,11 @@ class CurlEasyRequest : public CurlEasyHandle {
|
|||||||
inline ThreadSafeBufferedCurlEasyRequest* get_lockobj(void);
|
inline ThreadSafeBufferedCurlEasyRequest* get_lockobj(void);
|
||||||
inline ThreadSafeBufferedCurlEasyRequest const* get_lockobj(void) const;
|
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:
|
protected:
|
||||||
// Pass events to parent.
|
// Pass events to parent.
|
||||||
/*virtual*/ void added_to_multi_handle(AICurlEasyRequest_wat& curl_easy_request_w);
|
/*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; }
|
//void setBodyLimit(U32 size) { mBodyLimit = size; }
|
||||||
|
|
||||||
// Post-initialization, set the parent to pass the events to.
|
// 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:
|
protected:
|
||||||
// Events from this class.
|
// Events from this class.
|
||||||
@@ -462,7 +458,7 @@ class BufferedCurlEasyRequest : public CurlEasyRequest {
|
|||||||
std::string mReason; // The "reason" from the same header line.
|
std::string mReason; // The "reason" from the same header line.
|
||||||
S32 mRequestTransferedBytes;
|
S32 mRequestTransferedBytes;
|
||||||
S32 mResponseTransferedBytes;
|
S32 mResponseTransferedBytes;
|
||||||
AICurlResponderBufferEvents* mBufferEventsTarget;
|
AIBufferedCurlEasyRequestEvents* mBufferEventsTarget;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
static LLChannelDescriptors const sChannels; // Channel object for mInput (channel out()) and mOutput (channel in()).
|
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.
|
// Throws AICurlNoEasyHandle.
|
||||||
ThreadSafeBufferedCurlEasyRequest(void) : mReferenceCount(0)
|
ThreadSafeBufferedCurlEasyRequest(void) : mReferenceCount(0)
|
||||||
{ new (ptr()) BufferedCurlEasyRequest;
|
{ new (ptr()) BufferedCurlEasyRequest;
|
||||||
Dout(dc::curl, "Creating ThreadSafeBufferedCurlEasyRequest with this = " << (void*)this); }
|
Dout(dc::curl, "Creating ThreadSafeBufferedCurlEasyRequest with this = " << (void*)this);
|
||||||
virtual ~ThreadSafeBufferedCurlEasyRequest()
|
AICurlInterface::Stats::ThreadSafeBufferedCurlEasyRequest_count++; }
|
||||||
{ Dout(dc::curl, "Destructing ThreadSafeBufferedCurlEasyRequest with this = " << (void*)this); }
|
~ThreadSafeBufferedCurlEasyRequest()
|
||||||
|
{ Dout(dc::curl, "Destructing ThreadSafeBufferedCurlEasyRequest with this = " << (void*)this);
|
||||||
|
--AICurlInterface::Stats::ThreadSafeBufferedCurlEasyRequest_count; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
LLAtomicU32 mReferenceCount;
|
LLAtomicU32 mReferenceCount;
|
||||||
|
|||||||
@@ -31,6 +31,7 @@
|
|||||||
#include "linden_common.h"
|
#include "linden_common.h"
|
||||||
#include "aicurlthread.h"
|
#include "aicurlthread.h"
|
||||||
#include "aihttptimeoutpolicy.h"
|
#include "aihttptimeoutpolicy.h"
|
||||||
|
#include "aicurlperhost.h"
|
||||||
#include "lltimer.h" // ms_sleep, get_clock_count
|
#include "lltimer.h" // ms_sleep, get_clock_count
|
||||||
#include "llhttpstatuscodes.h"
|
#include "llhttpstatuscodes.h"
|
||||||
#include "llbuffer.h"
|
#include "llbuffer.h"
|
||||||
@@ -1413,6 +1414,8 @@ void AICurlThread::run(void)
|
|||||||
}
|
}
|
||||||
multi_handle_w->check_msg_queue();
|
multi_handle_w->check_msg_queue();
|
||||||
}
|
}
|
||||||
|
// Clear the queued requests.
|
||||||
|
PerHostRequestQueue::purge();
|
||||||
}
|
}
|
||||||
AICurlMultiHandle::destroyInstance();
|
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
|
// never increment Stats::multi_errors. However, lets just increment multi_calls
|
||||||
// when it certainly wasn't an error...
|
// when it certainly wasn't an error...
|
||||||
if (ret)
|
if (ret)
|
||||||
Stats::multi_calls++;
|
AICurlInterface::Stats::multi_calls++;
|
||||||
return ret;
|
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)
|
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();
|
curl_easy_request_w->set_timeout_opts();
|
||||||
ret = curl_easy_request_w->add_handle_to_multi(curl_easy_request_w, mMultiHandle);
|
if (curl_easy_request_w->add_handle_to_multi(curl_easy_request_w, mMultiHandle) == CURLM_OK)
|
||||||
}
|
{
|
||||||
if (ret == CURLM_OK)
|
per_host_w->added_to_multi_handle(); // (About to be) added to mAddedEasyRequests.
|
||||||
{
|
throttled = false; // Fall through...
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
} // 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
|
#ifdef SHOW_ASSERT
|
||||||
// Not active yet, but it's no longer an error if next we try to remove the request.
|
// Not active yet, but it's no longer an error if next we try to remove the request.
|
||||||
AICurlEasyRequest_wat(*easy_request)->mRemovedPerCommand = false;
|
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)
|
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);
|
addedEasyRequests_type::iterator iter = mAddedEasyRequests.find(easy_request);
|
||||||
if (iter == mAddedEasyRequests.end())
|
if (iter == mAddedEasyRequests.end())
|
||||||
{
|
{
|
||||||
// The request could be queued.
|
// 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
|
#ifdef SHOW_ASSERT
|
||||||
// Now a second remove command would be an error again.
|
bool removed =
|
||||||
AICurlEasyRequest_wat(**prev)->mRemovedPerCommand = true;
|
|
||||||
#endif
|
#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 (CURLMcode)-2; // Was already removed before, or never added (queued).
|
||||||
}
|
}
|
||||||
return remove_easy_request(iter, as_per_command);
|
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 MultiHandle::remove_easy_request(addedEasyRequests_type::iterator const& iter, bool as_per_command)
|
||||||
{
|
{
|
||||||
CURLMcode res;
|
CURLMcode res;
|
||||||
|
PerHostRequestQueuePtr per_host;
|
||||||
{
|
{
|
||||||
AICurlEasyRequest_wat curl_easy_request_w(**iter);
|
AICurlEasyRequest_wat curl_easy_request_w(**iter);
|
||||||
res = curl_easy_request_w->remove_handle_from_multi(curl_easy_request_w, mMultiHandle);
|
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
|
#ifdef SHOW_ASSERT
|
||||||
curl_easy_request_w->mRemovedPerCommand = as_per_command;
|
curl_easy_request_w->mRemovedPerCommand = as_per_command;
|
||||||
#endif
|
#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.");
|
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.
|
// Attempt to add a queued request, if any.
|
||||||
if (!mQueuedRequests.empty())
|
PerHostRequestQueue_wat(*per_host)->add_queued_to(this);
|
||||||
{
|
|
||||||
add_easy_request(mQueuedRequests.front());
|
|
||||||
mQueuedRequests.pop_front();
|
|
||||||
}
|
|
||||||
|
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2043,6 +2043,7 @@ void BufferedCurlEasyRequest::setStatusAndReason(U32 status, std::string const&
|
|||||||
{
|
{
|
||||||
mStatus = status;
|
mStatus = status;
|
||||||
mReason = reason;
|
mReason = reason;
|
||||||
|
AICurlInterface::Stats::status_count[AICurlInterface::Stats::status2index(mStatus)]++;
|
||||||
}
|
}
|
||||||
|
|
||||||
void BufferedCurlEasyRequest::processOutput(void)
|
void BufferedCurlEasyRequest::processOutput(void)
|
||||||
@@ -2455,23 +2456,37 @@ void AICurlEasyRequest::removeRequest(void)
|
|||||||
|
|
||||||
namespace AICurlInterface {
|
namespace AICurlInterface {
|
||||||
|
|
||||||
void startCurlThread(U32 CurlConcurrentConnections, bool NoVerifySSLCert)
|
void startCurlThread(U32 CurlMaxTotalConcurrentConnections, U32 CurlConcurrentConnectionsPerHost, bool NoVerifySSLCert)
|
||||||
{
|
{
|
||||||
|
using namespace AICurlPrivate;
|
||||||
using namespace AICurlPrivate::curlthread;
|
using namespace AICurlPrivate::curlthread;
|
||||||
|
|
||||||
llassert(is_main_thread());
|
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 = new AICurlThread;
|
||||||
AICurlThread::sInstance->start();
|
AICurlThread::sInstance->start();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool handleCurlConcurrentConnections(LLSD const& newvalue)
|
bool handleCurlMaxTotalConcurrentConnections(LLSD const& newvalue)
|
||||||
{
|
{
|
||||||
using namespace AICurlPrivate::curlthread;
|
using namespace AICurlPrivate::curlthread;
|
||||||
|
|
||||||
curl_concurrent_connections = newvalue.asInteger();
|
curl_max_total_concurrent_connections = newvalue.asInteger();
|
||||||
llinfos << "CurlConcurrentConnections set to " << curl_concurrent_connections << llendl;
|
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;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -33,7 +33,6 @@
|
|||||||
|
|
||||||
#include "aicurl.h"
|
#include "aicurl.h"
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include <deque>
|
|
||||||
|
|
||||||
#undef AICurlPrivate
|
#undef AICurlPrivate
|
||||||
|
|
||||||
@@ -81,7 +80,7 @@ class MultiHandle : public CurlMultiHandle
|
|||||||
// Store result and trigger events for easy request.
|
// Store result and trigger events for easy request.
|
||||||
void finish_easy_request(AICurlEasyRequest const& easy_request, CURLcode result);
|
void finish_easy_request(AICurlEasyRequest const& easy_request, CURLcode result);
|
||||||
// Remove easy request at iter (must exist).
|
// 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);
|
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);
|
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* mReadPollSet;
|
||||||
PollSet* mWritePollSet;
|
PollSet* mWritePollSet;
|
||||||
|
|
||||||
private:
|
|
||||||
// Temporary throttling hack.
|
|
||||||
std::deque<AICurlEasyRequest> mQueuedRequests; // Waiting (throttled) requests.
|
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace curlthread
|
} // namespace curlthread
|
||||||
|
|||||||
@@ -182,7 +182,7 @@ private:
|
|||||||
std::vector<LLUUID> mAgentIDs;
|
std::vector<LLUUID> mAgentIDs;
|
||||||
|
|
||||||
// Need the headers to look up Expires: and Retry-After:
|
// Need the headers to look up Expires: and Retry-After:
|
||||||
AIHTTPReceivedHeaders mHeaders;
|
virtual bool needsHeaders(void) const { return true; }
|
||||||
|
|
||||||
public:
|
public:
|
||||||
virtual AIHTTPTimeoutPolicy const& getHTTPTimeoutPolicy(void) const { return avatarNameResponder_timeout; }
|
virtual AIHTTPTimeoutPolicy const& getHTTPTimeoutPolicy(void) const { return avatarNameResponder_timeout; }
|
||||||
@@ -191,17 +191,10 @@ public:
|
|||||||
: mAgentIDs(agent_ids)
|
: 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)
|
/*virtual*/ void result(const LLSD& content)
|
||||||
{
|
{
|
||||||
// Pull expiration out of headers if available
|
// Pull expiration out of headers if available
|
||||||
F64 expires = LLAvatarNameCache::nameExpirationFromHeaders(mHeaders);
|
F64 expires = LLAvatarNameCache::nameExpirationFromHeaders(mReceivedHeaders);
|
||||||
F64 now = LLFrameTimer::getTotalSeconds();
|
F64 now = LLFrameTimer::getTotalSeconds();
|
||||||
|
|
||||||
LLSD agents = content["agents"];
|
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)
|
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()
|
LLHTTPClient::ResponderBase::~ResponderBase()
|
||||||
{
|
{
|
||||||
DoutEntering(dc::curl, "AICurlInterface::ResponderBase::~ResponderBase() with this = " << (void*)this << "; mReferenceCount = " << mReferenceCount);
|
DoutEntering(dc::curl, "AICurlInterface::ResponderBase::~ResponderBase() with this = " << (void*)this << "; mReferenceCount = " << mReferenceCount);
|
||||||
llassert(mReferenceCount == 0);
|
llassert(mReferenceCount == 0);
|
||||||
|
--AICurlInterface::Stats::ResponderBase_count;
|
||||||
}
|
}
|
||||||
|
|
||||||
void LLHTTPClient::ResponderBase::setURL(std::string const& url)
|
void LLHTTPClient::ResponderBase::setURL(std::string const& url)
|
||||||
{
|
{
|
||||||
// setURL is called from llhttpclient.cpp (request()), before calling any of the below (of course).
|
// 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.
|
// We don't need locking here therefore; it's a case of initializing before use.
|
||||||
mURL = url;
|
mURL = url;
|
||||||
}
|
}
|
||||||
|
|
||||||
AIHTTPTimeoutPolicy const& LLHTTPClient::ResponderBase::getHTTPTimeoutPolicy(void) const
|
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)
|
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.
|
AICurlInterface::Stats::llsd_body_count++;
|
||||||
bool const should_be_llsd = (200 <= status && status < 300);
|
// If the status indicates success (and we get here) then we expect the body to be LLSD.
|
||||||
if (should_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)
|
|
||||||
{
|
{
|
||||||
// Unfortunately we can't show the body of the message... I think this is a pretty serious error
|
LLBufferStream istr(channels, buffer.get());
|
||||||
// though, so if this ever happens it has to be investigated by making a copy of the buffer
|
if (LLSDSerialize::fromXML(content, istr) == LLSDParser::PARSE_FAILURE)
|
||||||
// before serializing it, as is done below.
|
{
|
||||||
llwarns << "Failed to deserialize LLSD. " << mURL << " [" << status << "]: " << reason << llendl;
|
// 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.
|
// Put the body in content as-is.
|
||||||
return;
|
std::stringstream ss;
|
||||||
}
|
buffer->writeChannelTo(ss, channels.in());
|
||||||
// Put the body in content as-is.
|
content = ss.str();
|
||||||
std::stringstream ss;
|
|
||||||
buffer->writeChannelTo(ss, channels.in());
|
|
||||||
content = ss.str();
|
|
||||||
#ifdef SHOW_ASSERT
|
#ifdef SHOW_ASSERT
|
||||||
if (!should_be_llsd)
|
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)
|
|
||||||
{
|
{
|
||||||
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
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
void LLHTTPClient::ResponderBase::decode_raw_body(U32 status, std::string const& reason, LLChannelDescriptors const& channels, buffer_ptr_t const& buffer, std::string& content)
|
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());
|
LLMutexLock lock(buffer->getMutex());
|
||||||
LLBufferArray::const_segment_iterator_t const end = buffer->endSegment();
|
LLBufferArray::const_segment_iterator_t const end = buffer->endSegment();
|
||||||
for (LLBufferArray::const_segment_iterator_t iter = buffer->beginSegment(); iter != end; ++iter)
|
for (LLBufferArray::const_segment_iterator_t iter = buffer->beginSegment(); iter != end; ++iter)
|
||||||
|
|||||||
@@ -59,8 +59,8 @@ struct AITransferInfo {
|
|||||||
F64 mSpeedDownload;
|
F64 mSpeedDownload;
|
||||||
};
|
};
|
||||||
|
|
||||||
// Events generated by AICurlPrivate::CurlResponderBuffer.
|
// Events generated by AICurlPrivate::BufferedCurlEasyRequest
|
||||||
struct AICurlResponderBufferEvents {
|
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_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 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.
|
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:
|
* 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,
|
* 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.
|
* 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
|
* member mResponder. Hence, the life time of a Responder is never longer than its
|
||||||
* associated CurlResponderBuffer, however, if everything works correctly, then normally a
|
* associated BufferedCurlEasyRequest, however, if everything works correctly, then normally a
|
||||||
* responder is deleted in CurlResponderBuffer::removed_from_multi_handle by setting
|
* responder is deleted in BufferedCurlEasyRequest::processOutput by setting
|
||||||
* mReponder to NULL.
|
* mReponder to NULL.
|
||||||
*/
|
*/
|
||||||
class ResponderBase : public AICurlResponderBufferEvents {
|
class ResponderBase : public AIBufferedCurlEasyRequestEvents {
|
||||||
public:
|
public:
|
||||||
typedef boost::shared_ptr<LLBufferArray> buffer_ptr_t;
|
typedef boost::shared_ptr<LLBufferArray> buffer_ptr_t;
|
||||||
|
|
||||||
@@ -121,7 +121,7 @@ public:
|
|||||||
std::string const& getURL(void) const { return mURL; }
|
std::string const& getURL(void) const { return mURL; }
|
||||||
CURLcode result_code(void) const { return mCode; }
|
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;
|
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.
|
// Return true if the curl thread is done with this transaction.
|
||||||
@@ -132,7 +132,8 @@ public:
|
|||||||
bool is_finished(void) const { return mFinished; }
|
bool is_finished(void) const { return mFinished; }
|
||||||
|
|
||||||
protected:
|
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.
|
// Called when the "HTTP/1.x <status> <reason>" header is received.
|
||||||
/*virtual*/ void received_HTTP_header(void)
|
/*virtual*/ void received_HTTP_header(void)
|
||||||
@@ -167,6 +168,7 @@ public:
|
|||||||
|
|
||||||
protected:
|
protected:
|
||||||
// Derived classes can override this to get the HTML headers that were received, when the message is completed.
|
// 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)
|
virtual void completedHeaders(U32 status, std::string const& reason, AIHTTPReceivedHeaders const& headers)
|
||||||
{
|
{
|
||||||
// The default does nothing.
|
// The default does nothing.
|
||||||
|
|||||||
@@ -352,8 +352,11 @@ void LLLineEditor::setText(const LLStringExplicit &new_text)
|
|||||||
}
|
}
|
||||||
setCursor(llmin((S32)mText.length(), getCursor()));
|
setCursor(llmin((S32)mText.length(), getCursor()));
|
||||||
|
|
||||||
// Set current history line to end of history.
|
if (!mLineHistory.empty())
|
||||||
mCurrentHistoryLine = mLineHistory.end() - 1;
|
{
|
||||||
|
// Set current history line to end of history.
|
||||||
|
mCurrentHistoryLine = mLineHistory.end() - 1;
|
||||||
|
}
|
||||||
|
|
||||||
mPrevText = mText;
|
mPrevText = mText;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4205,15 +4205,26 @@
|
|||||||
<key>Value</key>
|
<key>Value</key>
|
||||||
<integer>0</integer>
|
<integer>0</integer>
|
||||||
</map>
|
</map>
|
||||||
<key>CurlConcurrentConnections</key>
|
<key>CurlMaxTotalConcurrentConnections</key>
|
||||||
<map>
|
<map>
|
||||||
<key>Comment</key>
|
<key>Comment</key>
|
||||||
<string>Maximum number of simultaneous curl connections</string>
|
<string>Maximum total number of simultaneous curl connections</string>
|
||||||
<key>Persist</key>
|
<key>Persist</key>
|
||||||
<integer>1</integer>
|
<integer>1</integer>
|
||||||
<key>Type</key>
|
<key>Type</key>
|
||||||
<string>U32</string>
|
<string>U32</string>
|
||||||
<key>Value</key>
|
<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>
|
<integer>16</integer>
|
||||||
</map>
|
</map>
|
||||||
<key>CurlMaximumNumberOfHandles</key>
|
<key>CurlMaximumNumberOfHandles</key>
|
||||||
|
|||||||
@@ -617,7 +617,7 @@ bool LLAppViewer::init()
|
|||||||
|
|
||||||
// <edit>
|
// <edit>
|
||||||
// Curl must be initialized before any thread is running.
|
// 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.
|
// Logging is initialized. Now it's safe to start the error thread.
|
||||||
startErrorThread();
|
startErrorThread();
|
||||||
@@ -1836,7 +1836,9 @@ bool LLAppViewer::initThreads()
|
|||||||
LLWatchdog::getInstance()->init(watchdog_killer_callback);
|
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();
|
LLImage::initClass();
|
||||||
|
|
||||||
|
|||||||
@@ -796,7 +796,8 @@ void settings_setup_listeners()
|
|||||||
gSavedSettings.getControl("AscentAvatarYModifier")->getSignal()->connect(boost::bind(&handleAscentAvatarModifier, _2));
|
gSavedSettings.getControl("AscentAvatarYModifier")->getSignal()->connect(boost::bind(&handleAscentAvatarModifier, _2));
|
||||||
gSavedSettings.getControl("AscentAvatarZModifier")->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("NoVerifySSLCert")->getSignal()->connect(boost::bind(&AICurlInterface::handleNoVerifySSLCert, _2));
|
||||||
|
|
||||||
gSavedSettings.getControl("CurlTimeoutDNSLookup")->getValidateSignal()->connect(boost::bind(&validateCurlTimeoutDNSLookup, _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;
|
static std::map<LLUUID,bool> state_map;
|
||||||
std::map<LLUUID,bool>::iterator it = state_map.find(getID());
|
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;
|
it->second = sent_parentp != NULL;
|
||||||
LL_INFOS("Attachment") << getID() << " ("<<getAttachmentPointName()<<") has " << (sent_parentp ? "" : "no ") << "parent." << llendl;
|
LL_INFOS("Attachment") << getID() << " ("<<getAttachmentPointName()<<") has " << (sent_parentp ? "" : "no ") << "parent." << llendl;
|
||||||
|
|||||||
@@ -105,6 +105,7 @@ public:
|
|||||||
XMLRPC_REQUEST response(void) const { return mResponse; }
|
XMLRPC_REQUEST response(void) const { return mResponse; }
|
||||||
LLXMLRPCValue responseValue(void) const;
|
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 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 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);
|
/*virtual*/ void completedRaw(U32 status, std::string const& reason, LLChannelDescriptors const& channels, buffer_ptr_t const& buffer);
|
||||||
|
|||||||
Reference in New Issue
Block a user