#include "config.h" #include "NetworkResourceLoadScheduler.h" #include "HostRecord.h" #include "Logging.h" #include "NetworkProcess.h" #include "NetworkResourceLoadParameters.h" #include "NetworkResourceLoader.h" #include #include #if ENABLE(NETWORK_PROCESS) using namespace WebCore; namespace WebKit { static const unsigned maxRequestsInFlightForNonHTTPProtocols = 20; NetworkResourceLoadScheduler::NetworkResourceLoadScheduler() : m_nonHTTPProtocolHost(HostRecord::create(String(), maxRequestsInFlightForNonHTTPProtocols)) , m_requestTimer(this, &NetworkResourceLoadScheduler::requestTimerFired) { platformInitializeMaximumHTTPConnectionCountPerHost(); } void NetworkResourceLoadScheduler::scheduleServePendingRequests() { if (!m_requestTimer.isActive()) m_requestTimer.startOneShot(0); } void NetworkResourceLoadScheduler::requestTimerFired(WebCore::Timer*) { servePendingRequests(); } void NetworkResourceLoadScheduler::scheduleLoader(PassRefPtr loader) { ResourceLoadPriority priority = loader->priority(); const ResourceRequest& resourceRequest = loader->request(); LOG(NetworkScheduling, "(NetworkProcess) NetworkResourceLoadScheduler::scheduleLoader resource '%s'", resourceRequest.url().string().utf8().data()); HostRecord* host = hostForURL(resourceRequest.url(), CreateIfNotFound); bool hadRequests = host->hasRequests(); host->scheduleResourceLoader(loader); if (priority > ResourceLoadPriorityLow || !resourceRequest.url().protocolIsInHTTPFamily() || (priority == ResourceLoadPriorityLow && !hadRequests)) { // Try to request important resources immediately. host->servePendingRequests(priority); return; } // Handle asynchronously so early low priority requests don't get scheduled before later high priority ones. scheduleServePendingRequests(); } HostRecord* NetworkResourceLoadScheduler::hostForURL(const WebCore::KURL& url, CreateHostPolicy createHostPolicy) { if (!url.protocolIsInHTTPFamily()) return m_nonHTTPProtocolHost.get(); m_hosts.checkConsistency(); String hostName = url.host(); HostRecord* host = m_hosts.get(hostName); if (!host && createHostPolicy == CreateIfNotFound) { RefPtr newHost = HostRecord::create(hostName, m_maxRequestsInFlightPerHost); host = newHost.get(); m_hosts.add(hostName, newHost.release()); } return host; } void NetworkResourceLoadScheduler::removeLoader(NetworkResourceLoader* loader) { ASSERT(isMainThread()); ASSERT(loader); LOG(NetworkScheduling, "(NetworkProcess) NetworkResourceLoadScheduler::removeLoadIdentifier removing loader %s", loader->request().url().string().utf8().data()); HostRecord* host = loader->hostRecord(); // Due to a race condition the WebProcess might have messaged the NetworkProcess to remove this identifier // after the NetworkProcess has already removed it internally. // In this situation we might not have a HostRecord to clean up. if (host) host->removeLoader(loader); scheduleServePendingRequests(); } void NetworkResourceLoadScheduler::receivedRedirect(NetworkResourceLoader* loader, const WebCore::KURL& redirectURL) { ASSERT(isMainThread()); LOG(NetworkScheduling, "(NetworkProcess) NetworkResourceLoadScheduler::receivedRedirect loader originally for '%s' redirected to '%s'", loader->request().url().string().utf8().data(), redirectURL.string().utf8().data()); HostRecord* oldHost = loader->hostRecord(); // The load may have been cancelled while the message was in flight from network thread to main thread. if (!oldHost) return; HostRecord* newHost = hostForURL(redirectURL, CreateIfNotFound); if (oldHost->name() == newHost->name()) return; oldHost->removeLoader(loader); newHost->addLoaderInProgress(loader); } void NetworkResourceLoadScheduler::servePendingRequests(ResourceLoadPriority minimumPriority) { LOG(NetworkScheduling, "(NetworkProcess) NetworkResourceLoadScheduler::servePendingRequests Serving requests for up to %i hosts with minimum priority %i", m_hosts.size(), minimumPriority); m_requestTimer.stop(); m_nonHTTPProtocolHost->servePendingRequests(minimumPriority); m_hosts.checkConsistency(); Vector> hostsToServe; copyValuesToVector(m_hosts, hostsToServe); size_t size = hostsToServe.size(); for (size_t i = 0; i < size; ++i) { HostRecord* host = hostsToServe[i].get(); if (host->hasRequests()) host->servePendingRequests(minimumPriority); else m_hosts.remove(host->name()); } } static bool removeScheduledLoadersCalled = false; void NetworkResourceLoadScheduler::removeScheduledLoaders(void* context) { ASSERT(isMainThread()); ASSERT(removeScheduledLoadersCalled); NetworkResourceLoadScheduler* scheduler = static_cast(context); scheduler->removeScheduledLoaders(); } void NetworkResourceLoadScheduler::removeScheduledLoaders() { Vector> loadersToRemove; { MutexLocker locker(m_loadersToRemoveMutex); loadersToRemove = m_loadersToRemove; m_loadersToRemove.clear(); removeScheduledLoadersCalled = false; } for (size_t i = 0; i < loadersToRemove.size(); ++i) removeLoader(loadersToRemove[i].get()); } void NetworkResourceLoadScheduler::scheduleRemoveLoader(NetworkResourceLoader* loader) { MutexLocker locker(m_loadersToRemoveMutex); m_loadersToRemove.append(loader); if (!removeScheduledLoadersCalled) { removeScheduledLoadersCalled = true; callOnMainThread(NetworkResourceLoadScheduler::removeScheduledLoaders, this); } } uint64_t NetworkResourceLoadScheduler::hostsPendingCount() const { uint64_t count = m_nonHTTPProtocolHost->pendingRequestCount() ? 1 : 0; HostMap::const_iterator end = m_hosts.end(); for (HostMap::const_iterator i = m_hosts.begin(); i != end; ++i) { if (i->value->pendingRequestCount()) ++count; } return count; } uint64_t NetworkResourceLoadScheduler::loadsPendingCount() const { uint64_t count = m_nonHTTPProtocolHost->pendingRequestCount(); HostMap::const_iterator end = m_hosts.end(); for (HostMap::const_iterator i = m_hosts.begin(); i != end; ++i) count += i->value->pendingRequestCount(); return count; } uint64_t NetworkResourceLoadScheduler::hostsActiveCount() const { uint64_t count = 0; if (m_nonHTTPProtocolHost->activeLoadCount()) count = 1; HostMap::const_iterator end = m_hosts.end(); for (HostMap::const_iterator i = m_hosts.begin(); i != end; ++i) { if (i->value->activeLoadCount()) ++count; } return count; } uint64_t NetworkResourceLoadScheduler::loadsActiveCount() const { uint64_t count = m_nonHTTPProtocolHost->activeLoadCount(); HostMap::const_iterator end = m_hosts.end(); for (HostMap::const_iterator i = m_hosts.begin(); i != end; ++i) count += i->value->activeLoadCount(); return count; } } // namespace WebKit #endif // ENABLE(NETWORK_PROCESS)