Timer, time out, and clean up improvements.
This commit is contained in:
@@ -80,6 +80,7 @@ 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
|
||||
|
||||
@@ -264,9 +265,9 @@ static unsigned int encoded_version(int major, int minor, int patch)
|
||||
namespace AICurlInterface {
|
||||
|
||||
// MAIN-THREAD
|
||||
void initCurl(F32 curl_request_timeout, S32 max_number_handles)
|
||||
void initCurl(void (*flush_hook)())
|
||||
{
|
||||
DoutEntering(dc::curl, "AICurlInterface::initCurl(" << curl_request_timeout << ", " << max_number_handles << ")");
|
||||
DoutEntering(dc::curl, "AICurlInterface::initCurl(" << (void*)flush_hook << ")");
|
||||
|
||||
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);
|
||||
@@ -340,17 +341,24 @@ void initCurl(F32 curl_request_timeout, S32 max_number_handles)
|
||||
}
|
||||
llassert_always(!gSetoptParamsNeedDup); // Might add support later.
|
||||
}
|
||||
|
||||
// Called in cleanupCurl.
|
||||
statemachines_flush_hook = flush_hook;
|
||||
}
|
||||
|
||||
// MAIN-THREAD
|
||||
void cleanupCurl(void)
|
||||
{
|
||||
using AICurlPrivate::stopCurlThread;
|
||||
using AICurlPrivate::curlThreadIsRunning;
|
||||
using namespace AICurlPrivate;
|
||||
|
||||
DoutEntering(dc::curl, "AICurlInterface::cleanupCurl()");
|
||||
|
||||
stopCurlThread();
|
||||
if (CurlMultiHandle::getTotalMultiHandles() != 0)
|
||||
llwarns << "Not all CurlMultiHandle objects were destroyed!" << llendl;
|
||||
if (statemachines_flush_hook)
|
||||
(*statemachines_flush_hook)();
|
||||
Stats::print();
|
||||
ssl_cleanup();
|
||||
|
||||
llassert(LLThread::getRunning() <= (curlThreadIsRunning() ? 1 : 0)); // We must not call curl_global_cleanup unless we are the only thread left.
|
||||
@@ -547,6 +555,9 @@ void CurlEasyHandle::handle_easy_error(CURLcode code)
|
||||
|
||||
// Throws AICurlNoEasyHandle.
|
||||
CurlEasyHandle::CurlEasyHandle(void) : mActiveMultiHandle(NULL), mErrorBuffer(NULL)
|
||||
#ifdef SHOW_ASSERT
|
||||
, mRemovedPerCommand(true)
|
||||
#endif
|
||||
{
|
||||
mEasyHandle = curl_easy_init();
|
||||
#if 0
|
||||
@@ -566,6 +577,22 @@ CurlEasyHandle::CurlEasyHandle(void) : mActiveMultiHandle(NULL), mErrorBuffer(NU
|
||||
}
|
||||
}
|
||||
|
||||
#if 0 // Not used
|
||||
CurlEasyHandle::CurlEasyHandle(CurlEasyHandle const& orig) : mActiveMultiHandle(NULL), mErrorBuffer(NULL)
|
||||
#ifdef SHOW_ASSERT
|
||||
, mRemovedPerCommand(true)
|
||||
#endif
|
||||
{
|
||||
mEasyHandle = curl_easy_duphandle(orig.mEasyHandle);
|
||||
Stats::easy_init_calls++;
|
||||
if (!mEasyHandle)
|
||||
{
|
||||
Stats::easy_init_errors++;
|
||||
throw AICurlNoEasyHandle("curl_easy_duphandle() returned NULL");
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
CurlEasyHandle::~CurlEasyHandle()
|
||||
{
|
||||
llassert(!mActiveMultiHandle);
|
||||
@@ -1075,24 +1102,25 @@ CurlResponderBuffer::~CurlResponderBuffer()
|
||||
curl_easy_request_w->revokeCallbacks();
|
||||
if (mResponder)
|
||||
{
|
||||
llwarns << "Calling ~CurlResponderBuffer() with active responder!" << llendl;
|
||||
llassert(false); // Does this ever happen? And if so, what does it mean?
|
||||
// FIXME: Does this really mean it timed out?
|
||||
mResponder->completedRaw(HTTP_REQUEST_TIME_OUT, "Request timeout, aborted.", sChannels, mOutput);
|
||||
mResponder = NULL;
|
||||
// If the responder is still alive, then that means that CurlResponderBuffer::processOutput was
|
||||
// never called, which means that the removed_from_multi_handle event never happened.
|
||||
// This is definitely an internal error as it can only happen when libcurl is too slow,
|
||||
// in which case AICurlEasyRequestStateMachine::mTimer times out, but that already
|
||||
// calls CurlResponderBuffer::timed_out(). So, this really should never happen.
|
||||
llerrs << "Calling ~CurlResponderBuffer() with active responder!" << llendl;
|
||||
timed_out();
|
||||
}
|
||||
}
|
||||
|
||||
void CurlResponderBuffer::timed_out(void)
|
||||
{
|
||||
mResponder->completedRaw(HTTP_INTERNAL_ERROR, "Request timeout, aborted.", sChannels, mOutput);
|
||||
mResponder = NULL;
|
||||
}
|
||||
|
||||
void CurlResponderBuffer::resetState(AICurlEasyRequest_wat& curl_easy_request_w)
|
||||
{
|
||||
if (mResponder)
|
||||
{
|
||||
llwarns << "Calling CurlResponderBuffer::resetState() for active easy handle!" << llendl;
|
||||
llassert(false); // Does this ever happen? And if so, what does it mean?
|
||||
// FIXME: Does this really mean it timed out?
|
||||
mResponder->completedRaw(HTTP_REQUEST_TIME_OUT, "Request timeout, aborted.", sChannels, mOutput);
|
||||
mResponder = NULL;
|
||||
}
|
||||
llassert(!mResponder);
|
||||
|
||||
curl_easy_request_w->resetState();
|
||||
|
||||
@@ -1287,8 +1315,6 @@ CurlMultiHandle::~CurlMultiHandle()
|
||||
Stats::multi_calls++;
|
||||
int total = --sTotalMultiHandles;
|
||||
Dout(dc::curl, "Called CurlMultiHandle::~CurlMultiHandle() [" << (void*)this << "], " << total << " remaining.");
|
||||
if (total == 0)
|
||||
Stats::print();
|
||||
}
|
||||
|
||||
} // namespace AICurlPrivate
|
||||
|
||||
@@ -90,7 +90,7 @@ struct TransferInfo {
|
||||
|
||||
// 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(F32 curl_request_timeout = 120.f, S32 max_number_handles = 256);
|
||||
void initCurl(void (*)(void) = NULL);
|
||||
|
||||
// Called once at start of application (from LLAppViewer::initThreads), starts AICurlThread.
|
||||
void startCurlThread(void);
|
||||
|
||||
@@ -79,7 +79,7 @@ class CurlEasyHandle : public boost::noncopyable, protected AICurlEasyHandleEven
|
||||
CURLcode setopt(CURLoption option, BUILTIN parameter);
|
||||
|
||||
// Clone a libcurl session handle using all the options previously set.
|
||||
CurlEasyHandle(CurlEasyHandle const& orig) : mEasyHandle(curl_easy_duphandle(orig.mEasyHandle)), mActiveMultiHandle(NULL), mErrorBuffer(NULL) { }
|
||||
//CurlEasyHandle(CurlEasyHandle const& orig);
|
||||
|
||||
// URL encode/decode the given string.
|
||||
char* escape(char* url, int length);
|
||||
@@ -102,6 +102,10 @@ class CurlEasyHandle : public boost::noncopyable, protected AICurlEasyHandleEven
|
||||
CURL* mEasyHandle;
|
||||
CURLM* mActiveMultiHandle;
|
||||
char* mErrorBuffer;
|
||||
#ifdef SHOW_ASSERT
|
||||
public:
|
||||
bool mRemovedPerCommand; // Set if mActiveMultiHandle was reset as per command from the main thread.
|
||||
#endif
|
||||
|
||||
private:
|
||||
// This should only be called from MultiHandle; add/remove an easy handle to/from a multi handle.
|
||||
@@ -279,6 +283,9 @@ class CurlResponderBuffer : protected AICurlEasyHandleEvents {
|
||||
std::stringstream& getHeaderOutput() { return mHeaderOutput; }
|
||||
LLIOPipe::buffer_ptr_t& getOutput() { return mOutput; }
|
||||
|
||||
// Called if libcurl doesn't deliver within CurlRequestTimeOut seconds.
|
||||
void timed_out(void);
|
||||
|
||||
// Called after removed_from_multi_handle was called.
|
||||
void processOutput(AICurlEasyRequest_wat& curl_easy_request_w);
|
||||
|
||||
|
||||
@@ -702,7 +702,7 @@ void AICurlThread::wakeup(AICurlMultiHandle_wat const& multi_handle_w)
|
||||
multi_handle_w->add_easy_request(AICurlEasyRequest(command_being_processed_r->easy_request()));
|
||||
break;
|
||||
case cmd_remove:
|
||||
multi_handle_w->remove_easy_request(AICurlEasyRequest(command_being_processed_r->easy_request()));
|
||||
multi_handle_w->remove_easy_request(AICurlEasyRequest(command_being_processed_r->easy_request()), true);
|
||||
break;
|
||||
}
|
||||
// Done processing.
|
||||
@@ -858,6 +858,8 @@ MultiHandle::MultiHandle(void) : mHandleAddedOrRemoved(false), mPrevRunningHandl
|
||||
|
||||
MultiHandle::~MultiHandle()
|
||||
{
|
||||
llinfos << "Destructing MultiHandle with " << mAddedEasyRequests.size() << " active curl easy handles." << llendl;
|
||||
|
||||
// This thread was terminated.
|
||||
// Curl demands that all handles are removed from the multi session handle before calling curl_multi_cleanup.
|
||||
for(addedEasyRequests_type::iterator iter = mAddedEasyRequests.begin(); iter != mAddedEasyRequests.end(); iter = mAddedEasyRequests.begin())
|
||||
@@ -958,7 +960,7 @@ CURLMcode MultiHandle::add_easy_request(AICurlEasyRequest const& easy_request)
|
||||
return ret;
|
||||
}
|
||||
|
||||
CURLMcode MultiHandle::remove_easy_request(AICurlEasyRequest const& easy_request)
|
||||
CURLMcode MultiHandle::remove_easy_request(AICurlEasyRequest const& easy_request, bool as_per_command)
|
||||
{
|
||||
addedEasyRequests_type::iterator iter = mAddedEasyRequests.find(easy_request);
|
||||
if (iter == mAddedEasyRequests.end())
|
||||
@@ -967,6 +969,9 @@ CURLMcode MultiHandle::remove_easy_request(AICurlEasyRequest const& easy_request
|
||||
{
|
||||
AICurlEasyRequest_wat curl_easy_request_w(**iter);
|
||||
res = curl_easy_request_w->remove_handle_from_multi(curl_easy_request_w, mMultiHandle);
|
||||
#ifdef SHOW_ASSERT
|
||||
curl_easy_request_w->mRemovedPerCommand = as_per_command;
|
||||
#endif
|
||||
}
|
||||
mAddedEasyRequests.erase(iter);
|
||||
mHandleAddedOrRemoved = true;
|
||||
@@ -1173,8 +1178,11 @@ void AICurlEasyRequest::removeRequest(void)
|
||||
}
|
||||
else
|
||||
{
|
||||
// May not already have been removed from multi session handle.
|
||||
llassert(AICurlEasyRequest_wat(*get())->active());
|
||||
// May not already have been removed from multi session handle as per command from the main thread (through this function thus).
|
||||
{
|
||||
AICurlEasyRequest_wat curl_easy_request_w(*get());
|
||||
llassert(curl_easy_request_w->active() || !curl_easy_request_w->mRemovedPerCommand);
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -123,7 +123,7 @@ class MultiHandle : public CurlMultiHandle
|
||||
|
||||
// Add/remove an easy handle to/from a multi session.
|
||||
CURLMcode add_easy_request(AICurlEasyRequest const& easy_request);
|
||||
CURLMcode remove_easy_request(AICurlEasyRequest const& easy_request);
|
||||
CURLMcode remove_easy_request(AICurlEasyRequest const& easy_request, bool as_per_command = false);
|
||||
|
||||
// Reads/writes available data from a particular socket (non-blocking).
|
||||
CURLMcode socket_action(curl_socket_t sockfd, int ev_bitmask);
|
||||
|
||||
Reference in New Issue
Block a user