ResourceHandleManager.cpp [plain text]
#include "config.h"
#include "ResourceHandleManager.h"
#if USE(CURL)
#include "CredentialStorage.h"
#include "HTTPHeaderNames.h"
#include "HTTPParsers.h"
#include "MultipartHandle.h"
#include "ResourceError.h"
#include "ResourceHandle.h"
#include "ResourceHandleClient.h"
#include "ResourceHandleInternal.h"
#include "ResourceRequest.h"
#include "ResourceResponse.h"
#include "SSLHandle.h"
#include "TextEncoding.h"
#include <wtf/text/CString.h>
#include <wtf/text/StringView.h>
#if OS(WINDOWS)
#else
#include <sys/param.h>
#define MAX_PATH MAXPATHLEN
#endif
#include <errno.h>
#include <stdio.h>
#if ENABLE(WEB_TIMING)
#include <wtf/CurrentTime.h>
#endif
#include <wtf/Lock.h>
#include <wtf/Threading.h>
#include <wtf/Vector.h>
namespace WebCore {
const int selectTimeoutMS = 5;
static const Seconds pollTime { 50_ms };
const int maxRunningJobs = 128;
ResourceHandleManager::ResourceHandleManager()
: m_downloadTimer(*this, &ResourceHandleManager::downloadTimerCallback)
, m_runningJobs(0)
{
}
ResourceHandleManager::~ResourceHandleManager()
{
}
ResourceHandleManager* ResourceHandleManager::sharedInstance()
{
static ResourceHandleManager* sharedInstance = 0;
if (!sharedInstance)
sharedInstance = new ResourceHandleManager();
return sharedInstance;
}
void ResourceHandleManager::downloadTimerCallback()
{
startScheduledJobs();
fd_set fdread;
fd_set fdwrite;
fd_set fdexcep;
int maxfd = 0;
struct timeval timeout;
timeout.tv_sec = 0;
timeout.tv_usec = selectTimeoutMS * 1000;
int rc = 0;
do {
m_curlMultiHandle.getFdSet(fdread, fdwrite, fdexcep, maxfd);
if (maxfd >= 0)
rc = ::select(maxfd + 1, &fdread, &fdwrite, &fdexcep, &timeout);
} while (rc == -1 && errno == EINTR);
if (-1 == rc) {
#ifndef NDEBUG
perror("bad: select() returned -1: ");
#endif
return;
}
int runningHandles = 0;
while (m_curlMultiHandle.perform(runningHandles) == CURLM_CALL_MULTI_PERFORM) { }
while (true) {
int messagesInQueue = 0;
CURLMsg* msg = m_curlMultiHandle.readInfo(messagesInQueue);
if (!msg)
break;
CURL* handle = msg->easy_handle;
ASSERT(handle);
ResourceHandle* job = nullptr;
CURLcode err = curl_easy_getinfo(handle, CURLINFO_PRIVATE, &job);
ASSERT_UNUSED(err, CURLE_OK == err);
ASSERT(job);
if (!job)
continue;
ResourceHandleInternal* d = job->getInternal();
if (d->m_cancelled) {
removeFromCurl(job);
continue;
}
if (CURLMSG_DONE != msg->msg)
continue;
job->handleCurlMsg(msg);
removeFromCurl(job);
}
bool started = startScheduledJobs();
if (!m_downloadTimer.isActive() && (started || (runningHandles > 0)))
m_downloadTimer.startOneShot(pollTime);
}
void ResourceHandleManager::removeFromCurl(ResourceHandle* job)
{
ResourceHandleInternal* d = job->getInternal();
ASSERT(d->m_curlHandle.handle());
if (!d->m_curlHandle.handle())
return;
m_runningJobs--;
m_curlMultiHandle.removeHandle(d->m_curlHandle.handle());
job->deref();
}
void ResourceHandleManager::add(ResourceHandle* job)
{
job->ref();
m_resourceHandleList.append(job);
if (!m_downloadTimer.isActive())
m_downloadTimer.startOneShot(pollTime);
}
bool ResourceHandleManager::removeScheduledJob(ResourceHandle* job)
{
int size = m_resourceHandleList.size();
for (int i = 0; i < size; i++) {
if (job == m_resourceHandleList[i]) {
m_resourceHandleList.remove(i);
job->deref();
return true;
}
}
return false;
}
bool ResourceHandleManager::startScheduledJobs()
{
bool started = false;
while (!m_resourceHandleList.isEmpty() && m_runningJobs < maxRunningJobs) {
ResourceHandle* job = m_resourceHandleList[0];
m_resourceHandleList.remove(0);
startJob(job);
started = true;
}
return started;
}
void ResourceHandleManager::startJob(ResourceHandle* job)
{
job->initialize();
m_runningJobs++;
CURLMcode ret = m_curlMultiHandle.addHandle(job->getInternal()->m_curlHandle.handle());
if (ret && ret != CURLM_CALL_MULTI_PERFORM) {
#ifndef NDEBUG
fprintf(stderr, "Error %d starting job %s\n", ret, encodeWithURLEscapeSequences(job->firstRequest().url().string()).latin1().data());
#endif
job->cancel();
return;
}
}
void ResourceHandleManager::cancel(ResourceHandle* job)
{
if (removeScheduledJob(job))
return;
ResourceHandleInternal* d = job->getInternal();
d->m_cancelled = true;
if (!m_downloadTimer.isActive())
m_downloadTimer.startOneShot(pollTime);
}
}
#endif