diff --git a/indra/aistatemachine/aicurleasyrequeststatemachine.cpp b/indra/aistatemachine/aicurleasyrequeststatemachine.cpp index 868419e91..85a993351 100644 --- a/indra/aistatemachine/aicurleasyrequeststatemachine.cpp +++ b/indra/aistatemachine/aicurleasyrequeststatemachine.cpp @@ -88,6 +88,9 @@ void AICurlEasyRequestStateMachine::finished(AICurlEasyRequest_wat&) // CURL-THREAD void AICurlEasyRequestStateMachine::removed_from_multi_handle(AICurlEasyRequest_wat&) { + llassert(mFinished || mTimedOut); // If we neither finished nor timed out, then why is this being removed? + // Note that allowing this would cause an assertion later on for removing + // a CurlResponderBuffer with a still active Responder. set_state(mFinished ? AICurlEasyRequestStateMachine_removed_after_finished : AICurlEasyRequestStateMachine_removed); } diff --git a/indra/aistatemachine/aicurlthread.cpp b/indra/aistatemachine/aicurlthread.cpp index 3e9788c7b..35e424fbc 100644 --- a/indra/aistatemachine/aicurlthread.cpp +++ b/indra/aistatemachine/aicurlthread.cpp @@ -1374,13 +1374,16 @@ MultiHandle::~MultiHandle() void MultiHandle::handle_stalls(void) { - for(addedEasyRequests_type::iterator iter = mAddedEasyRequests.begin(); iter != mAddedEasyRequests.end(); iter = mAddedEasyRequests.begin()) + for(addedEasyRequests_type::iterator iter = mAddedEasyRequests.begin(); iter != mAddedEasyRequests.end();) { if (AICurlEasyRequest_wat(**iter)->timeout_has_stalled()) { Dout(dc::curl, "MultiHandle::handle_stalls(): Easy request stalled! [" << (void*)iter->get_ptr().get() << "]"); - remove_easy_request(*iter, false); + finish_easy_request(*iter, CURLE_OPERATION_TIMEDOUT); + remove_easy_request(iter++, false); } + else + ++iter; } } @@ -1527,6 +1530,11 @@ CURLMcode MultiHandle::remove_easy_request(AICurlEasyRequest const& easy_request } return (CURLMcode)-2; // Was already removed before, or never added (queued). } + return remove_easy_request(iter, as_per_command); +} + +CURLMcode MultiHandle::remove_easy_request(addedEasyRequests_type::iterator const& iter, bool as_per_command) +{ CURLMcode res; { AICurlEasyRequest_wat curl_easy_request_w(**iter); @@ -1535,9 +1543,12 @@ CURLMcode MultiHandle::remove_easy_request(AICurlEasyRequest const& easy_request curl_easy_request_w->mRemovedPerCommand = as_per_command; #endif } +#if CWDEBUG + ThreadSafeCurlEasyRequest* lockobj = iter->get_ptr().get(); +#endif mAddedEasyRequests.erase(iter); mHandleAddedOrRemoved = true; - Dout(dc::curl, "MultiHandle::remove_easy_request: Removed AICurlEasyRequest " << (void*)easy_request.get_ptr().get() << "; 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. if (!mQueuedRequests.empty()) @@ -1565,20 +1576,8 @@ void MultiHandle::check_run_count(void) llassert_always(rese == CURLE_OK); AICurlEasyRequest easy_request(ptr); llassert(*AICurlEasyRequest_wat(*easy_request) == easy); - // Store the result and transfer info in the easy handle. - { - AICurlEasyRequest_wat curl_easy_request_w(*easy_request); - curl_easy_request_w->store_result(msg->data.result); -#ifdef CWDEBUG - char* eff_url; - curl_easy_request_w->getinfo(CURLINFO_EFFECTIVE_URL, &eff_url); - Dout(dc::curl, "Finished: " << eff_url << " (" << curl_easy_strerror(msg->data.result) << ") [CURLINFO_PRIVATE = " << (void*)ptr << "]"); -#endif - // Update timeout administration. - curl_easy_request_w->timeout_done(msg->data.result); - // Signal that this easy handle finished. - curl_easy_request_w->done(curl_easy_request_w); - } + // Store result and trigger events for the easy request. + finish_easy_request(easy_request, msg->data.result); // This invalidates msg, but not easy_request. CURLMcode res = remove_easy_request(easy_request); // This should hold, I think, because the handles are obviously ok and @@ -1602,6 +1601,22 @@ void MultiHandle::check_run_count(void) mPrevRunningHandles = mRunningHandles; } +void MultiHandle::finish_easy_request(AICurlEasyRequest const& easy_request, CURLcode result) +{ + AICurlEasyRequest_wat curl_easy_request_w(*easy_request); + // Store the result in the easy handle. + curl_easy_request_w->store_result(result); +#ifdef CWDEBUG + char* eff_url; + curl_easy_request_w->getinfo(CURLINFO_EFFECTIVE_URL, &eff_url); + Dout(dc::curl, "Finished: " << eff_url << " (" << curl_easy_strerror(result) << ") [CURLINFO_PRIVATE = " << (void*)easy_request.get_ptr().get() << "]"); +#endif + // Update timeout administration. + curl_easy_request_w->timeout_done(result); + // Signal that this easy handle finished. + curl_easy_request_w->done(curl_easy_request_w); +} + } // namespace curlthread } // namespace AICurlPrivate diff --git a/indra/aistatemachine/aicurlthread.h b/indra/aistatemachine/aicurlthread.h index 4e1ff9ff2..b8800440f 100644 --- a/indra/aistatemachine/aicurlthread.h +++ b/indra/aistatemachine/aicurlthread.h @@ -82,6 +82,12 @@ class MultiHandle : public CurlMultiHandle long mTimeOut; // The last time out in ms as set by the callback CURLMOPT_TIMERFUNCTION. private: + // Store result and trigger events for easy request. + void finish_easy_request(AICurlEasyRequest const& easy_request, CURLcode result); + // Remove easy request at iter (must exist). + // Note that it's possible that a new request from mQueuedRequests is inserted before iter. + 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 timer_callback(CURLM* multi, long timeout_ms, void* userp);