LLCurl::Multi::~Multi() { llassert(isStopped()); if (LLCurl::sMultiThreaded) { LLCurl::Easy::sMultiMutex->lock(); } delete mSignal; mSignal = NULL; // Clean up active for(easy_active_list_t::iterator iter = mEasyActiveList.begin(); iter != mEasyActiveList.end(); ++iter) { Easy* easy = *iter; check_curl_multi_code(curl_multi_remove_handle(mCurlMultiHandle, easy->getCurlHandle())); delete easy; } mEasyActiveList.clear(); mEasyActiveMap.clear(); // Clean up freed for_each(mEasyFreeList.begin(), mEasyFreeList.end(), DeletePointer()); mEasyFreeList.clear(); check_curl_multi_code(curl_multi_cleanup(mCurlMultiHandle)); --gCurlMultiCount; if (LLCurl::sMultiThreaded) { LLCurl::Easy::sMultiMutex->unlock(); } }
void LLCurl::Multi::removeEasy(Easy* easy) { { LLMutexLock lock(mMutexp) ; check_curl_multi_code(curl_multi_remove_handle(mCurlMultiHandle, easy->getCurlHandle())); } easyFree(easy); }
void LLCurl::Multi::cleanup(bool deleted) { if(!mCurlMultiHandle) { return ; //nothing to clean. } llassert_always(deleted || !mValid) ; LLMutexLock lock(mDeletionMutexp); // Clean up active for(easy_active_list_t::iterator iter = mEasyActiveList.begin(); iter != mEasyActiveList.end(); ++iter) { Easy* easy = *iter; check_curl_multi_code(curl_multi_remove_handle(mCurlMultiHandle, easy->getCurlHandle())); if(deleted) { easy->mResponder = NULL ; //avoid triggering mResponder. } delete easy; } mEasyActiveList.clear(); mEasyActiveMap.clear(); // Clean up freed for_each(mEasyFreeList.begin(), mEasyFreeList.end(), DeletePointer()); mEasyFreeList.clear(); check_curl_multi_code(LLCurl::deleteMultiHandle(mCurlMultiHandle)); mCurlMultiHandle = NULL ; delete mMutexp ; mMutexp = NULL ; delete mEasyMutexp ; mEasyMutexp = NULL ; mQueued = 0 ; mState = STATE_COMPLETED; --gCurlMultiCount; return ; }
bool LLCurl::Multi::addEasy(Easy* easy) { CURLMcode mcode = curl_multi_add_handle(mCurlMultiHandle, easy->getCurlHandle()); check_curl_multi_code(mcode); //if (mcode != CURLM_OK) //{ // llwarns << "Curl Error: " << curl_multi_strerror(mcode) << llendl; // return false; //} return true; }
void LLCurl::Multi::cleanup() { if(!mCurlMultiHandle) { return ; //nothing to clean. } // Clean up active for(easy_active_list_t::iterator iter = mEasyActiveList.begin(); iter != mEasyActiveList.end(); ++iter) { Easy* easy = *iter; check_curl_multi_code(curl_multi_remove_handle(mCurlMultiHandle, easy->getCurlHandle())); delete easy; } mEasyActiveList.clear(); mEasyActiveMap.clear(); // Clean up freed for_each(mEasyFreeList.begin(), mEasyFreeList.end(), DeletePointer()); mEasyFreeList.clear(); check_curl_multi_code(LLCurl::deleteMultiHandle(mCurlMultiHandle)); mCurlMultiHandle = NULL ; delete mMutexp ; mMutexp = NULL ; delete mDeletionMutexp ; mDeletionMutexp = NULL ; delete mEasyMutexp ; mEasyMutexp = NULL ; mQueued = 0 ; mState = STATE_COMPLETED; --gCurlMultiCount; return ; }
//return true if dead bool LLCurl::Multi::doPerform() { LLMutexLock lock(mDeletionMutexp) ; bool dead = mDead ; if(mDead) { setState(STATE_COMPLETED); mQueued = 0 ; } else if(getState() != STATE_COMPLETED) { setState(STATE_PERFORMING); S32 q = 0; for (S32 call_count = 0; call_count < MULTI_PERFORM_CALL_REPEAT; call_count++) { LLMutexLock lock(mMutexp) ; //WARNING: curl_multi_perform will block for many hundreds of milliseconds // NEVER call this from the main thread, and NEVER allow the main thread to // wait on a mutex held by this thread while curl_multi_perform is executing CURLMcode code = curl_multi_perform(mCurlMultiHandle, &q); if (CURLM_CALL_MULTI_PERFORM != code || q == 0) { check_curl_multi_code(code); break; } } mQueued = q; setState(STATE_COMPLETED) ; mIdleTimer.reset() ; } else if(!mValid && mIdleTimer.getElapsedTimeF32() > mIdleTimeOut) //idle for too long, remove it. { dead = true ; } else if(mValid && mIdleTimer.getElapsedTimeF32() > mIdleTimeOut - 1.f) //idle for too long, mark it invalid. { mValid = FALSE ; } return dead ; }
void LLCurl::Multi::doPerform() { S32 q = 0; if (mThreaded) mSignal->unlock(); for (S32 call_count = 0; call_count < MULTI_PERFORM_CALL_REPEAT; call_count += 1) { CURLMcode code = curl_multi_perform(mCurlMultiHandle, &q); if (CURLM_CALL_MULTI_PERFORM != code || q == 0) { check_curl_multi_code(code); break; } } if (mThreaded) mSignal->lock(); mQueued = q; mPerformState = PERFORM_STATE_COMPLETED; }
// Caller has provided us with a ref count on op. void HttpLibcurl::addOp(HttpOpRequest * op) { llassert_always(op->mReqPolicy < mPolicyCount); llassert_always(mMultiHandles[op->mReqPolicy] != NULL); // Create standard handle if (! op->prepareRequest(mService)) { // Couldn't issue request, fail with notification // *TODO: Need failure path return; } // Make the request live CURLMcode code; code = curl_multi_add_handle(mMultiHandles[op->mReqPolicy], op->mCurlHandle); if (CURLM_OK != code) { // *TODO: Better cleanup and recovery but not much we can do here. check_curl_multi_code(code); return; } op->mCurlActive = true; mActiveOps.insert(op); ++mActiveHandles[op->mReqPolicy]; if (op->mTracing > HTTP_TRACE_OFF) { HttpPolicy & policy(mService->getPolicy()); LL_INFOS(LOG_CORE) << "TRACE, ToActiveQueue, Handle: " << static_cast<HttpHandle>(op) << ", Actives: " << mActiveOps.size() << ", Readies: " << policy.getReadyCount(op->mReqPolicy) << LL_ENDL; } }
void LLCurlFF::check_multi_code(CURLMcode code) { check_curl_multi_code(code); }
void HttpLibcurl::policyUpdated(int policy_class) { if (policy_class < 0 || policy_class >= mPolicyCount || ! mMultiHandles) { return; } HttpPolicy & policy(mService->getPolicy()); if (! mActiveHandles[policy_class]) { // Clear to set options. As of libcurl 7.37.0, if a pipelining // multi handle has active requests and you try to set the // multi handle to non-pipelining, the library gets very angry // and goes off the rails corrupting memory. A clue that you're // about to crash is that you'll get a missing server response // error (curl code 9). So, if options are to be set, we let // the multi handle run out of requests, then set options, and // re-enable request processing. // // All of this stall mechanism exists for this reason. If // libcurl becomes more resilient later, it should be possible // to remove all of this. The connection limit settings are fine, // it's just that pipelined-to-non-pipelined transition that // is fatal at the moment. HttpPolicyClass & options(policy.getClassOptions(policy_class)); CURLM * multi_handle(mMultiHandles[policy_class]); CURLMcode code; // Enable policy if stalled policy.stallPolicy(policy_class, false); mDirtyPolicy[policy_class] = false; if (options.mPipelining > 1) { // We'll try to do pipelining on this multihandle code = curl_multi_setopt(multi_handle, CURLMOPT_PIPELINING, 1L); check_curl_multi_code(code, CURLMOPT_PIPELINING); code = curl_multi_setopt(multi_handle, CURLMOPT_MAX_PIPELINE_LENGTH, long(options.mPipelining)); check_curl_multi_code(code, CURLMOPT_MAX_PIPELINE_LENGTH); code = curl_multi_setopt(multi_handle, CURLMOPT_MAX_HOST_CONNECTIONS, long(options.mPerHostConnectionLimit)); check_curl_multi_code(code, CURLMOPT_MAX_HOST_CONNECTIONS); code = curl_multi_setopt(multi_handle, CURLMOPT_MAX_TOTAL_CONNECTIONS, long(options.mConnectionLimit)); check_curl_multi_code(code, CURLMOPT_MAX_TOTAL_CONNECTIONS); } else { code = curl_multi_setopt(multi_handle, CURLMOPT_PIPELINING, 0L); check_curl_multi_code(code, CURLMOPT_PIPELINING); code = curl_multi_setopt(multi_handle, CURLMOPT_MAX_HOST_CONNECTIONS, 0L); check_curl_multi_code(code, CURLMOPT_MAX_HOST_CONNECTIONS); code = curl_multi_setopt(multi_handle, CURLMOPT_MAX_TOTAL_CONNECTIONS, long(options.mConnectionLimit)); check_curl_multi_code(code, CURLMOPT_MAX_TOTAL_CONNECTIONS); } } else if (! mDirtyPolicy[policy_class]) { // Mark policy dirty and request a stall in the policy. // When policy goes idle, we'll re-invoke this method // and perform the change. Don't allow this thread to // sleep while we're waiting for quiescence, we'll just // stop processing. mDirtyPolicy[policy_class] = true; policy.stallPolicy(policy_class, true); } }
void LLCurl::Multi::removeEasy(Easy* easy) { check_curl_multi_code(curl_multi_remove_handle(mCurlMultiHandle, easy->getCurlHandle())); easyFree(easy); }