1#include "config.h" 2#include "NetworkResourceLoadScheduler.h" 3 4#include "HostRecord.h" 5#include "Logging.h" 6#include "NetworkProcess.h" 7#include "NetworkResourceLoadParameters.h" 8#include "NetworkResourceLoader.h" 9#include <wtf/MainThread.h> 10#include <wtf/text/CString.h> 11 12#if ENABLE(NETWORK_PROCESS) 13 14using namespace WebCore; 15 16namespace WebKit { 17 18static const unsigned maxRequestsInFlightForNonHTTPProtocols = 20; 19 20NetworkResourceLoadScheduler::NetworkResourceLoadScheduler() 21 : m_nonHTTPProtocolHost(HostRecord::create(String(), maxRequestsInFlightForNonHTTPProtocols)) 22 , m_requestTimer(this, &NetworkResourceLoadScheduler::requestTimerFired) 23 24{ 25 platformInitializeMaximumHTTPConnectionCountPerHost(); 26} 27 28void NetworkResourceLoadScheduler::scheduleServePendingRequests() 29{ 30 if (!m_requestTimer.isActive()) 31 m_requestTimer.startOneShot(0); 32} 33 34void NetworkResourceLoadScheduler::requestTimerFired(WebCore::Timer<NetworkResourceLoadScheduler>*) 35{ 36 servePendingRequests(); 37} 38 39void NetworkResourceLoadScheduler::scheduleLoader(PassRefPtr<NetworkResourceLoader> loader) 40{ 41 ResourceLoadPriority priority = loader->priority(); 42 const ResourceRequest& resourceRequest = loader->request(); 43 44 LOG(NetworkScheduling, "(NetworkProcess) NetworkResourceLoadScheduler::scheduleLoader resource '%s'", resourceRequest.url().string().utf8().data()); 45 46 HostRecord* host = hostForURL(resourceRequest.url(), CreateIfNotFound); 47 bool hadRequests = host->hasRequests(); 48 host->scheduleResourceLoader(loader); 49 50 if (priority > ResourceLoadPriorityLow || !resourceRequest.url().protocolIsInHTTPFamily() || (priority == ResourceLoadPriorityLow && !hadRequests)) { 51 // Try to request important resources immediately. 52 host->servePendingRequests(priority); 53 return; 54 } 55 56 // Handle asynchronously so early low priority requests don't get scheduled before later high priority ones. 57 scheduleServePendingRequests(); 58} 59 60HostRecord* NetworkResourceLoadScheduler::hostForURL(const WebCore::URL& url, CreateHostPolicy createHostPolicy) 61{ 62 if (!url.protocolIsInHTTPFamily()) 63 return m_nonHTTPProtocolHost.get(); 64 65 m_hosts.checkConsistency(); 66 String hostName = url.host(); 67 HostRecord* host = m_hosts.get(hostName); 68 if (!host && createHostPolicy == CreateIfNotFound) { 69 RefPtr<HostRecord> newHost = HostRecord::create(hostName, m_maxRequestsInFlightPerHost); 70 host = newHost.get(); 71 m_hosts.add(hostName, newHost.release()); 72 } 73 74 return host; 75} 76 77void NetworkResourceLoadScheduler::removeLoader(NetworkResourceLoader* loader) 78{ 79 ASSERT(RunLoop::isMain()); 80 ASSERT(loader); 81 82 LOG(NetworkScheduling, "(NetworkProcess) NetworkResourceLoadScheduler::removeLoadIdentifier removing loader %s", loader->request().url().string().utf8().data()); 83 84 HostRecord* host = loader->hostRecord(); 85 86 // Due to a race condition the WebProcess might have messaged the NetworkProcess to remove this identifier 87 // after the NetworkProcess has already removed it internally. 88 // In this situation we might not have a HostRecord to clean up. 89 if (host) 90 host->removeLoader(loader); 91 92 scheduleServePendingRequests(); 93} 94 95void NetworkResourceLoadScheduler::receivedRedirect(NetworkResourceLoader* loader, const WebCore::URL& redirectURL) 96{ 97 ASSERT(RunLoop::isMain()); 98 LOG(NetworkScheduling, "(NetworkProcess) NetworkResourceLoadScheduler::receivedRedirect loader originally for '%s' redirected to '%s'", loader->request().url().string().utf8().data(), redirectURL.string().utf8().data()); 99 100 HostRecord* oldHost = loader->hostRecord(); 101 102 // The load may have been cancelled while the message was in flight from network thread to main thread. 103 if (!oldHost) 104 return; 105 106 HostRecord* newHost = hostForURL(redirectURL, CreateIfNotFound); 107 108 if (oldHost->name() == newHost->name()) 109 return; 110 111 oldHost->removeLoader(loader); 112 newHost->addLoaderInProgress(loader); 113} 114 115void NetworkResourceLoadScheduler::servePendingRequests(ResourceLoadPriority minimumPriority) 116{ 117 LOG(NetworkScheduling, "(NetworkProcess) NetworkResourceLoadScheduler::servePendingRequests Serving requests for up to %i hosts with minimum priority %i", m_hosts.size(), minimumPriority); 118 119 m_requestTimer.stop(); 120 121 m_nonHTTPProtocolHost->servePendingRequests(minimumPriority); 122 123 m_hosts.checkConsistency(); 124 Vector<RefPtr<HostRecord>> hostsToServe; 125 copyValuesToVector(m_hosts, hostsToServe); 126 127 size_t size = hostsToServe.size(); 128 for (size_t i = 0; i < size; ++i) { 129 HostRecord* host = hostsToServe[i].get(); 130 if (host->hasRequests()) 131 host->servePendingRequests(minimumPriority); 132 else 133 m_hosts.remove(host->name()); 134 } 135} 136 137static bool removeScheduledLoadersCalled = false; 138 139void NetworkResourceLoadScheduler::removeScheduledLoaders(void* context) 140{ 141 ASSERT(RunLoop::isMain()); 142 ASSERT(removeScheduledLoadersCalled); 143 144 NetworkResourceLoadScheduler* scheduler = static_cast<NetworkResourceLoadScheduler*>(context); 145 scheduler->removeScheduledLoaders(); 146} 147 148void NetworkResourceLoadScheduler::removeScheduledLoaders() 149{ 150 Vector<RefPtr<NetworkResourceLoader>> loadersToRemove; 151 { 152 MutexLocker locker(m_loadersToRemoveMutex); 153 loadersToRemove = m_loadersToRemove; 154 m_loadersToRemove.clear(); 155 removeScheduledLoadersCalled = false; 156 } 157 158 for (size_t i = 0; i < loadersToRemove.size(); ++i) 159 removeLoader(loadersToRemove[i].get()); 160} 161 162void NetworkResourceLoadScheduler::scheduleRemoveLoader(NetworkResourceLoader* loader) 163{ 164 MutexLocker locker(m_loadersToRemoveMutex); 165 166 m_loadersToRemove.append(loader); 167 168 if (!removeScheduledLoadersCalled) { 169 removeScheduledLoadersCalled = true; 170 callOnMainThread(NetworkResourceLoadScheduler::removeScheduledLoaders, this); 171 } 172} 173 174uint64_t NetworkResourceLoadScheduler::hostsPendingCount() const 175{ 176 uint64_t count = m_nonHTTPProtocolHost->pendingRequestCount() ? 1 : 0; 177 178 HostMap::const_iterator end = m_hosts.end(); 179 for (HostMap::const_iterator i = m_hosts.begin(); i != end; ++i) { 180 if (i->value->pendingRequestCount()) 181 ++count; 182 } 183 184 return count; 185} 186 187uint64_t NetworkResourceLoadScheduler::loadsPendingCount() const 188{ 189 uint64_t count = m_nonHTTPProtocolHost->pendingRequestCount(); 190 191 HostMap::const_iterator end = m_hosts.end(); 192 for (HostMap::const_iterator i = m_hosts.begin(); i != end; ++i) 193 count += i->value->pendingRequestCount(); 194 195 return count; 196} 197 198uint64_t NetworkResourceLoadScheduler::hostsActiveCount() const 199{ 200 uint64_t count = 0; 201 202 if (m_nonHTTPProtocolHost->activeLoadCount()) 203 count = 1; 204 205 HostMap::const_iterator end = m_hosts.end(); 206 for (HostMap::const_iterator i = m_hosts.begin(); i != end; ++i) { 207 if (i->value->activeLoadCount()) 208 ++count; 209 } 210 211 return count; 212} 213 214uint64_t NetworkResourceLoadScheduler::loadsActiveCount() const 215{ 216 uint64_t count = m_nonHTTPProtocolHost->activeLoadCount(); 217 218 HostMap::const_iterator end = m_hosts.end(); 219 for (HostMap::const_iterator i = m_hosts.begin(); i != end; ++i) 220 count += i->value->activeLoadCount(); 221 222 return count; 223} 224 225} // namespace WebKit 226 227#endif // ENABLE(NETWORK_PROCESS) 228