Timer, time out, and clean up improvements.

This commit is contained in:
Aleric Inglewood
2012-07-11 19:25:29 +02:00
parent 0419f8bee9
commit 7416d2aaf1
13 changed files with 254 additions and 78 deletions

View File

@@ -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

View File

@@ -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);

View File

@@ -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);

View File

@@ -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

View File

@@ -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);