1/* 2 * Copyright (C) 2012 Apple Inc. All rights reserved. 3 * 4 * Redistribution and use in source and binary forms, with or without 5 * modification, are permitted provided that the following conditions 6 * are met: 7 * 1. Redistributions of source code must retain the above copyright 8 * notice, this list of conditions and the following disclaimer. 9 * 2. Redistributions in binary form must reproduce the above copyright 10 * notice, this list of conditions and the following disclaimer in the 11 * documentation and/or other materials provided with the distribution. 12 * 13 * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' 14 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, 15 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 16 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS 17 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 18 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 19 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 20 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 21 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 22 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF 23 * THE POSSIBILITY OF SUCH DAMAGE. 24 */ 25 26#include "config.h" 27#include "HostRecord.h" 28 29#include "Logging.h" 30#include "NetworkConnectionToWebProcess.h" 31#include "NetworkProcess.h" 32#include "NetworkResourceLoadParameters.h" 33#include "NetworkResourceLoadScheduler.h" 34#include "NetworkResourceLoader.h" 35#include <wtf/MainThread.h> 36 37#if ENABLE(NETWORK_PROCESS) 38 39using namespace WebCore; 40 41namespace WebKit { 42 43HostRecord::HostRecord(const String& name, int maxRequestsInFlight) 44 : m_name(name) 45 , m_maxRequestsInFlight(maxRequestsInFlight) 46{ 47} 48 49HostRecord::~HostRecord() 50{ 51#ifndef NDEBUG 52 ASSERT(m_loadersInProgress.isEmpty()); 53 for (unsigned p = 0; p <= ResourceLoadPriorityHighest; p++) 54 ASSERT(m_loadersPending[p].isEmpty()); 55#endif 56} 57 58void HostRecord::scheduleResourceLoader(PassRefPtr<NetworkResourceLoader> loader) 59{ 60 ASSERT(RunLoop::isMain()); 61 62 loader->setHostRecord(this); 63 64 if (loader->isSynchronous()) 65 m_syncLoadersPending.append(loader); 66 else 67 m_loadersPending[loader->priority()].append(loader); 68} 69 70void HostRecord::addLoaderInProgress(NetworkResourceLoader* loader) 71{ 72 ASSERT(RunLoop::isMain()); 73 74 m_loadersInProgress.add(loader); 75 loader->setHostRecord(this); 76} 77 78inline bool removeLoaderFromQueue(NetworkResourceLoader* loader, LoaderQueue& queue) 79{ 80 LoaderQueue::iterator end = queue.end(); 81 for (LoaderQueue::iterator it = queue.begin(); it != end; ++it) { 82 if (it->get() == loader) { 83 loader->setHostRecord(0); 84 queue.remove(it); 85 return true; 86 } 87 } 88 return false; 89} 90 91void HostRecord::removeLoader(NetworkResourceLoader* loader) 92{ 93 ASSERT(RunLoop::isMain()); 94 95 // FIXME (NetworkProcess): Due to IPC race conditions, it's possible this HostRecord will be asked to remove the same loader twice. 96 // It would be nice to know the loader has already been removed and treat it as a no-op. 97 98 NetworkResourceLoaderSet::iterator i = m_loadersInProgress.find(loader); 99 if (i != m_loadersInProgress.end()) { 100 i->get()->setHostRecord(0); 101 m_loadersInProgress.remove(i); 102 return; 103 } 104 105 if (removeLoaderFromQueue(loader, m_syncLoadersPending)) 106 return; 107 108 for (int priority = ResourceLoadPriorityHighest; priority >= ResourceLoadPriorityLowest; --priority) { 109 if (removeLoaderFromQueue(loader, m_loadersPending[priority])) 110 return; 111 } 112} 113 114bool HostRecord::hasRequests() const 115{ 116 if (!m_loadersInProgress.isEmpty()) 117 return true; 118 119 for (unsigned p = 0; p <= ResourceLoadPriorityHighest; p++) { 120 if (!m_loadersPending[p].isEmpty()) 121 return true; 122 } 123 124 return false; 125} 126 127uint64_t HostRecord::pendingRequestCount() const 128{ 129 uint64_t count = 0; 130 131 for (unsigned p = 0; p <= ResourceLoadPriorityHighest; p++) 132 count += m_loadersPending[p].size(); 133 134 return count; 135} 136 137uint64_t HostRecord::activeLoadCount() const 138{ 139 return m_loadersInProgress.size(); 140} 141 142void HostRecord::servePendingRequestsForQueue(LoaderQueue& queue, ResourceLoadPriority priority) 143{ 144 // We only enforce the connection limit for http(s) hosts, which are the only ones with names. 145 bool shouldLimitRequests = !name().isNull(); 146 147 // For non-named hosts - everything but http(s) - we should only enforce the limit if the document 148 // isn't done parsing and we don't know all stylesheets yet. 149 150 // FIXME (NetworkProcess): The above comment about document parsing and stylesheets is a holdover 151 // from the WebCore::ResourceLoadScheduler. 152 // The behavior described was at one time important for WebCore's single threadedness. 153 // It's possible that we don't care about it with the NetworkProcess. 154 // We should either decide it's not important and change the above comment, or decide it is 155 // still important and somehow account for it. 156 157 // Very low priority loaders are only handled when no other loaders are in progress. 158 if (shouldLimitRequests && priority == ResourceLoadPriorityVeryLow && !m_loadersInProgress.isEmpty()) 159 return; 160 161 while (!queue.isEmpty()) { 162 RefPtr<NetworkResourceLoader> loader = queue.first(); 163 ASSERT(loader->hostRecord() == this); 164 165 // This request might be from WebProcess we've lost our connection to. 166 // If so we should just skip it. 167 if (!loader->connectionToWebProcess()) { 168 removeLoader(loader.get()); 169 continue; 170 } 171 172 if (shouldLimitRequests && limitsRequests(priority, loader.get())) 173 return; 174 175 m_loadersInProgress.add(loader); 176 queue.removeFirst(); 177 178 LOG(NetworkScheduling, "(NetworkProcess) HostRecord::servePendingRequestsForQueue - Starting load of %s\n", loader->request().url().string().utf8().data()); 179 loader->start(); 180 } 181} 182 183void HostRecord::servePendingRequests(ResourceLoadPriority minimumPriority) 184{ 185 LOG(NetworkScheduling, "(NetworkProcess) HostRecord::servePendingRequests Host name='%s'", name().utf8().data()); 186 187 // We serve synchronous requests before any other requests to improve responsiveness in any 188 // WebProcess that is waiting on a synchronous load. 189 servePendingRequestsForQueue(m_syncLoadersPending, ResourceLoadPriorityHighest); 190 191 for (int priority = ResourceLoadPriorityHighest; priority >= minimumPriority; --priority) 192 servePendingRequestsForQueue(m_loadersPending[priority], (ResourceLoadPriority)priority); 193} 194 195bool HostRecord::limitsRequests(ResourceLoadPriority priority, NetworkResourceLoader* loader) const 196{ 197 ASSERT(loader); 198 ASSERT(loader->connectionToWebProcess()); 199 200 if (priority == ResourceLoadPriorityVeryLow && !m_loadersInProgress.isEmpty()) 201 return true; 202 203 if (loader->connectionToWebProcess()->isSerialLoadingEnabled() && m_loadersInProgress.size() >= 1) 204 return true; 205 206 // If we're exactly at the limit for requests in flight, and this loader is asynchronous, then we're done serving new requests. 207 // The synchronous loader exception handles the case where a sync XHR is made while 6 other requests are already in flight. 208 if (m_loadersInProgress.size() == m_maxRequestsInFlight && !loader->isSynchronous()) 209 return true; 210 211 // If we're already past the limit of the number of loaders in flight, we won't even serve new synchronous requests right now. 212 if (m_loadersInProgress.size() > m_maxRequestsInFlight) { 213#ifndef NDEBUG 214 // If we have more loaders in progress than we should, at least one of them had better be synchronous. 215 NetworkResourceLoaderSet::iterator i = m_loadersInProgress.begin(); 216 NetworkResourceLoaderSet::iterator end = m_loadersInProgress.end(); 217 for (; i != end; ++i) { 218 if (i->get()->isSynchronous()) 219 break; 220 } 221 ASSERT(i != end); 222#endif 223 return true; 224 } 225 return false; 226} 227 228} // namespace WebKit 229 230#endif // ENABLE(NETWORK_PROCESS) 231