Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(755)

Side by Side Diff: content/browser/renderer_host/async_resource_handler.cc

Issue 10911297: Modify AsyncResourceHandler to use a single shared memory buffer (in ring (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src/
Patch Set: Created 8 years, 3 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "content/browser/renderer_host/async_resource_handler.h" 5 #include "content/browser/renderer_host/async_resource_handler.h"
6 6
7 #include <algorithm> 7 #include <algorithm>
8 #include <vector> 8 #include <vector>
9 9
10 #include "base/command_line.h"
10 #include "base/debug/alias.h" 11 #include "base/debug/alias.h"
11 #include "base/hash_tables.h" 12 #include "base/hash_tables.h"
12 #include "base/logging.h" 13 #include "base/logging.h"
13 #include "base/metrics/histogram.h" 14 #include "base/metrics/histogram.h"
14 #include "base/shared_memory.h" 15 #include "base/shared_memory.h"
16 #include "base/string_number_conversions.h"
15 #include "content/browser/debugger/devtools_netlog_observer.h" 17 #include "content/browser/debugger/devtools_netlog_observer.h"
16 #include "content/browser/host_zoom_map_impl.h" 18 #include "content/browser/host_zoom_map_impl.h"
19 #include "content/browser/renderer_host/resource_buffer.h"
17 #include "content/browser/renderer_host/resource_dispatcher_host_impl.h" 20 #include "content/browser/renderer_host/resource_dispatcher_host_impl.h"
18 #include "content/browser/renderer_host/resource_message_filter.h" 21 #include "content/browser/renderer_host/resource_message_filter.h"
19 #include "content/browser/renderer_host/resource_request_info_impl.h" 22 #include "content/browser/renderer_host/resource_request_info_impl.h"
20 #include "content/browser/resource_context_impl.h" 23 #include "content/browser/resource_context_impl.h"
21 #include "content/common/resource_messages.h" 24 #include "content/common/resource_messages.h"
22 #include "content/common/view_messages.h" 25 #include "content/common/view_messages.h"
23 #include "content/public/browser/global_request_id.h" 26 #include "content/public/browser/global_request_id.h"
24 #include "content/public/browser/resource_dispatcher_host_delegate.h" 27 #include "content/public/browser/resource_dispatcher_host_delegate.h"
25 #include "content/public/common/resource_response.h" 28 #include "content/public/common/resource_response.h"
26 #include "net/base/io_buffer.h" 29 #include "net/base/io_buffer.h"
27 #include "net/base/load_flags.h" 30 #include "net/base/load_flags.h"
28 #include "net/base/net_log.h" 31 #include "net/base/net_log.h"
29 #include "webkit/glue/resource_loader_bridge.h" 32 #include "webkit/glue/resource_loader_bridge.h"
30 33
31 using base::TimeTicks; 34 using base::TimeTicks;
32 35
33 namespace content { 36 namespace content {
34 namespace { 37 namespace {
35 38
36 // When reading, we don't know if we are going to get EOF (0 bytes read), so 39 static int kBufferSize = 1024 * 512;
37 // we typically have a buffer that we allocated but did not use. We keep 40 static int kMinAllocationSize = 1024 * 4;
38 // this buffer around for the next read as a small optimization. 41 static int kMaxAllocationSize = 1024 * 32;
39 SharedIOBuffer* g_spare_read_buffer = NULL;
40 42
41 // The initial size of the shared memory buffer. (32 kilobytes). 43 void GetNumericArg(const std::string& name, int* result) {
42 const int kInitialReadBufSize = 32768; 44 const std::string& value =
45 CommandLine::ForCurrentProcess()->GetSwitchValueASCII(name);
46 if (!value.empty())
47 base::StringToInt(value, result);
48 }
43 49
44 // The maximum size of the shared memory buffer. (512 kilobytes). 50 void InitializeResourceBufferConstants() {
45 const int kMaxReadBufSize = 524288; 51 static bool did_init = false;
52 if (did_init)
53 return;
54 did_init = true;
46 55
47 // Maximum number of pending data messages sent to the renderer at any 56 GetNumericArg("resource-buffer-size", &kBufferSize);
48 // given time for a given request. 57 GetNumericArg("resource-buffer-min-allocation-size", &kMinAllocationSize);
49 const int kMaxPendingDataMessages = 20; 58 GetNumericArg("resource-buffer-max-allocation-size", &kMaxAllocationSize);
59 }
50 60
51 int CalcUsedPercentage(int bytes_read, int buffer_size) { 61 int CalcUsedPercentage(int bytes_read, int buffer_size) {
52 double ratio = static_cast<double>(bytes_read) / buffer_size; 62 double ratio = static_cast<double>(bytes_read) / buffer_size;
53 return static_cast<int>(ratio * 100.0 + 0.5); // Round to nearest integer. 63 return static_cast<int>(ratio * 100.0 + 0.5); // Round to nearest integer.
54 } 64 }
55 65
56 } // namespace 66 } // namespace
57 67
58 // Our version of IOBuffer that uses shared memory. 68 class DependentIOBuffer : public net::WrappedIOBuffer {
59 class SharedIOBuffer : public net::IOBuffer {
60 public: 69 public:
61 explicit SharedIOBuffer(int buffer_size) 70 DependentIOBuffer(ResourceBuffer* backing, char* memory)
62 : net::IOBuffer(), 71 : net::WrappedIOBuffer(memory),
63 ok_(false), 72 backing_(backing) {
64 buffer_size_(buffer_size) {}
65
66 bool Init() {
67 if (shared_memory_.CreateAndMapAnonymous(buffer_size_)) {
68 data_ = reinterpret_cast<char*>(shared_memory_.memory());
69 DCHECK(data_);
70 ok_ = true;
71 }
72 return ok_;
73 } 73 }
74
75 base::SharedMemory* shared_memory() { return &shared_memory_; }
76 bool ok() { return ok_; }
77 int buffer_size() { return buffer_size_; }
78
79 private: 74 private:
80 ~SharedIOBuffer() { 75 ~DependentIOBuffer() {}
81 DCHECK(g_spare_read_buffer != this); 76 scoped_refptr<ResourceBuffer> backing_;
82 data_ = NULL;
83 }
84
85 base::SharedMemory shared_memory_;
86 bool ok_;
87 int buffer_size_;
88 }; 77 };
89 78
90 AsyncResourceHandler::AsyncResourceHandler( 79 AsyncResourceHandler::AsyncResourceHandler(
91 ResourceMessageFilter* filter, 80 ResourceMessageFilter* filter,
92 int routing_id, 81 int routing_id,
93 net::URLRequest* request, 82 net::URLRequest* request,
94 ResourceDispatcherHostImpl* rdh) 83 ResourceDispatcherHostImpl* rdh)
95 : filter_(filter), 84 : filter_(filter),
96 routing_id_(routing_id), 85 routing_id_(routing_id),
97 request_(request), 86 request_(request),
98 rdh_(rdh), 87 rdh_(rdh),
99 next_buffer_size_(kInitialReadBufSize),
100 pending_data_count_(0), 88 pending_data_count_(0),
89 allocation_size_(0),
101 did_defer_(false), 90 did_defer_(false),
102 sent_received_response_msg_(false) { 91 sent_received_response_msg_(false),
92 sent_first_data_msg_(false) {
103 // Set a back-pointer from ResourceRequestInfoImpl to |this|, so that the 93 // Set a back-pointer from ResourceRequestInfoImpl to |this|, so that the
104 // ResourceDispatcherHostImpl can send us IPC messages. 94 // ResourceDispatcherHostImpl can send us IPC messages.
105 // TODO(darin): Implement an IPC message filter instead? 95 // TODO(darin): Implement an IPC message filter instead?
106 ResourceRequestInfoImpl::ForRequest(request_)->set_async_handler(this); 96 ResourceRequestInfoImpl::ForRequest(request_)->set_async_handler(this);
97
98 InitializeResourceBufferConstants();
107 } 99 }
108 100
109 AsyncResourceHandler::~AsyncResourceHandler() { 101 AsyncResourceHandler::~AsyncResourceHandler() {
110 // Cleanup back-pointer stored on the request info. 102 // Cleanup back-pointer stored on the request info.
111 ResourceRequestInfoImpl::ForRequest(request_)->set_async_handler(NULL); 103 ResourceRequestInfoImpl::ForRequest(request_)->set_async_handler(NULL);
112 } 104 }
113 105
114 void AsyncResourceHandler::OnFollowRedirect( 106 void AsyncResourceHandler::OnFollowRedirect(
115 bool has_new_first_party_for_cookies, 107 bool has_new_first_party_for_cookies,
116 const GURL& new_first_party_for_cookies) { 108 const GURL& new_first_party_for_cookies) {
117 if (!request_->status().is_success()) { 109 if (!request_->status().is_success()) {
118 DVLOG(1) << "OnFollowRedirect for invalid request"; 110 DVLOG(1) << "OnFollowRedirect for invalid request";
119 return; 111 return;
120 } 112 }
121 113
122 if (has_new_first_party_for_cookies) 114 if (has_new_first_party_for_cookies)
123 request_->set_first_party_for_cookies(new_first_party_for_cookies); 115 request_->set_first_party_for_cookies(new_first_party_for_cookies);
124 116
125 ResumeIfDeferred(); 117 ResumeIfDeferred();
126 } 118 }
127 119
128 void AsyncResourceHandler::OnDataReceivedACK() { 120 void AsyncResourceHandler::OnDataReceivedACK() {
129 if (pending_data_count_-- == kMaxPendingDataMessages) 121 --pending_data_count_;
122
123 buffer_->RecycleLeastRecentlyAllocated();
124 if (buffer_->CanAllocate())
130 ResumeIfDeferred(); 125 ResumeIfDeferred();
131 } 126 }
132 127
133 bool AsyncResourceHandler::OnUploadProgress(int request_id, 128 bool AsyncResourceHandler::OnUploadProgress(int request_id,
134 uint64 position, 129 uint64 position,
135 uint64 size) { 130 uint64 size) {
136 return filter_->Send(new ResourceMsg_UploadProgress(routing_id_, request_id, 131 return filter_->Send(new ResourceMsg_UploadProgress(routing_id_, request_id,
137 position, size)); 132 position, size));
138 } 133 }
139 134
(...skipping 65 matching lines...) Expand 10 before | Expand all | Expand 10 after
205 bool AsyncResourceHandler::OnWillStart(int request_id, 200 bool AsyncResourceHandler::OnWillStart(int request_id,
206 const GURL& url, 201 const GURL& url,
207 bool* defer) { 202 bool* defer) {
208 return true; 203 return true;
209 } 204 }
210 205
211 bool AsyncResourceHandler::OnWillRead(int request_id, net::IOBuffer** buf, 206 bool AsyncResourceHandler::OnWillRead(int request_id, net::IOBuffer** buf,
212 int* buf_size, int min_size) { 207 int* buf_size, int min_size) {
213 DCHECK_EQ(-1, min_size); 208 DCHECK_EQ(-1, min_size);
214 209
215 if (g_spare_read_buffer) { 210 if (!EnsureResourceBufferIsInitialized())
216 DCHECK(!read_buffer_); 211 return false;
217 read_buffer_.swap(&g_spare_read_buffer);
218 DCHECK(read_buffer_->data());
219 212
220 *buf = read_buffer_.get(); 213 DCHECK(buffer_->CanAllocate());
221 *buf_size = read_buffer_->buffer_size(); 214 char* memory = buffer_->Allocate(&allocation_size_);
222 } else { 215 CHECK(memory);
223 read_buffer_ = new SharedIOBuffer(next_buffer_size_);
224 if (!read_buffer_->Init()) {
225 DLOG(ERROR) << "Couldn't allocate shared io buffer";
226 read_buffer_ = NULL;
227 return false;
228 }
229 DCHECK(read_buffer_->data());
230 *buf = read_buffer_.get();
231 *buf_size = next_buffer_size_;
232 216
233 UMA_HISTOGRAM_CUSTOM_COUNTS( 217 *buf = new DependentIOBuffer(buffer_, memory);
234 "Net.AsyncResourceHandler_SharedIOBuffer_Alloc", 218 *buf_size = allocation_size_;
235 next_buffer_size_, kInitialReadBufSize, kMaxReadBufSize, 16);
236 }
237 219
220 UMA_HISTOGRAM_CUSTOM_COUNTS(
221 "Net.AsyncResourceHandler_SharedIOBuffer_Alloc",
222 *buf_size, 0, kMaxAllocationSize, 100);
238 return true; 223 return true;
239 } 224 }
240 225
241 bool AsyncResourceHandler::OnReadCompleted(int request_id, int bytes_read, 226 bool AsyncResourceHandler::OnReadCompleted(int request_id, int bytes_read,
242 bool* defer) { 227 bool* defer) {
243 if (!bytes_read) 228 if (!bytes_read)
244 return true; 229 return true;
245 DCHECK(read_buffer_.get()); 230
231 buffer_->ShrinkLastAllocation(bytes_read);
246 232
247 UMA_HISTOGRAM_CUSTOM_COUNTS( 233 UMA_HISTOGRAM_CUSTOM_COUNTS(
248 "Net.AsyncResourceHandler_SharedIOBuffer_Used", 234 "Net.AsyncResourceHandler_SharedIOBuffer_Used",
249 bytes_read, 0, kMaxReadBufSize, 100); 235 bytes_read, 0, kMaxAllocationSize, 100);
jar (doing other things) 2013/03/27 16:55:22 FYI: It is to late now.... but best practice when
250 UMA_HISTOGRAM_PERCENTAGE( 236 UMA_HISTOGRAM_PERCENTAGE(
251 "Net.AsyncResourceHandler_SharedIOBuffer_UsedPercentage", 237 "Net.AsyncResourceHandler_SharedIOBuffer_UsedPercentage",
252 CalcUsedPercentage(bytes_read, read_buffer_->buffer_size())); 238 CalcUsedPercentage(bytes_read, allocation_size_));
253 239
254 if (read_buffer_->buffer_size() == bytes_read) { 240 if (!sent_first_data_msg_) {
255 // The network layer has saturated our buffer. Next time, we should give it 241 base::SharedMemoryHandle handle;
256 // a bigger buffer for it to fill, to minimize the number of round trips we 242 int size;
257 // do with the renderer process. 243 if (!buffer_->ShareToProcess(filter_->peer_handle(), &handle, &size))
258 next_buffer_size_ = std::min(next_buffer_size_ * 2, kMaxReadBufSize); 244 return false;
245 filter_->Send(
246 new ResourceMsg_SetDataBuffer(routing_id_, request_id, handle, size));
247 sent_first_data_msg_ = true;
259 } 248 }
260 249
261 WillSendData(defer); 250 int data_offset = buffer_->GetLastAllocationOffset();
262
263 base::SharedMemoryHandle handle;
264 if (!read_buffer_->shared_memory()->GiveToProcess(
265 filter_->peer_handle(), &handle)) {
266 // We wrongfully incremented the pending data count. Fake an ACK message
267 // to fix this. We can't move this call above the WillSendData because
268 // it's killing our read_buffer_, and we don't want that when we pause
269 // the request.
270 OnDataReceivedACK();
271
272 // We just unmapped the memory.
273 read_buffer_ = NULL;
274 return false;
275 }
276 // We just unmapped the memory.
277 read_buffer_ = NULL;
278
279 int encoded_data_length = 251 int encoded_data_length =
280 DevToolsNetLogObserver::GetAndResetEncodedDataLength(request_); 252 DevToolsNetLogObserver::GetAndResetEncodedDataLength(request_);
281 filter_->Send(new ResourceMsg_DataReceived( 253
282 routing_id_, request_id, handle, bytes_read, encoded_data_length)); 254 filter_->Send(
255 new ResourceMsg_DataReceived(routing_id_, request_id, data_offset,
256 bytes_read, encoded_data_length));
257 ++pending_data_count_;
258 UMA_HISTOGRAM_CUSTOM_COUNTS(
259 "Net.AsyncResourceHandler_PendingDataCount",
260 pending_data_count_, 0, 100, 100);
261
262 if (!buffer_->CanAllocate()) {
263 UMA_HISTOGRAM_CUSTOM_COUNTS(
264 "Net.AsyncResourceHandler_PendingDataCount_WhenFull",
265 pending_data_count_, 0, 100, 100);
266 *defer = did_defer_ = true;
267 }
283 268
284 return true; 269 return true;
285 } 270 }
286 271
287 void AsyncResourceHandler::OnDataDownloaded( 272 void AsyncResourceHandler::OnDataDownloaded(
288 int request_id, int bytes_downloaded) { 273 int request_id, int bytes_downloaded) {
289 filter_->Send(new ResourceMsg_DataDownloaded( 274 filter_->Send(new ResourceMsg_DataDownloaded(
290 routing_id_, request_id, bytes_downloaded)); 275 routing_id_, request_id, bytes_downloaded));
291 } 276 }
292 277
(...skipping 36 matching lines...) Expand 10 before | Expand all | Expand 10 after
329 error_code == net::OK) { 314 error_code == net::OK) {
330 error_code = net::ERR_FAILED; 315 error_code = net::ERR_FAILED;
331 } 316 }
332 317
333 filter_->Send(new ResourceMsg_RequestComplete(routing_id_, 318 filter_->Send(new ResourceMsg_RequestComplete(routing_id_,
334 request_id, 319 request_id,
335 error_code, 320 error_code,
336 was_ignored_by_handler, 321 was_ignored_by_handler,
337 security_info, 322 security_info,
338 completion_time)); 323 completion_time));
339
340 // If we still have a read buffer, then see about caching it for later...
341 // Note that we have to make sure the buffer is not still being used, so we
342 // have to perform an explicit check on the status code.
343 if (g_spare_read_buffer ||
344 net::URLRequestStatus::SUCCESS != status.status()) {
345 read_buffer_ = NULL;
346 } else if (read_buffer_.get()) {
347 DCHECK(read_buffer_->data());
348 read_buffer_.swap(&g_spare_read_buffer);
349 }
350 return true; 324 return true;
351 } 325 }
352 326
353 // static 327 bool AsyncResourceHandler::EnsureResourceBufferIsInitialized() {
354 void AsyncResourceHandler::GlobalCleanup() { 328 if (buffer_ && buffer_->IsInitialized())
355 if (g_spare_read_buffer) { 329 return true;
356 // Avoid the CHECK in SharedIOBuffer::~SharedIOBuffer().
357 SharedIOBuffer* tmp = g_spare_read_buffer;
358 g_spare_read_buffer = NULL;
359 tmp->Release();
360 }
361 }
362 330
363 void AsyncResourceHandler::WillSendData(bool* defer) { 331 buffer_ = new ResourceBuffer();
364 if (++pending_data_count_ >= kMaxPendingDataMessages) { 332 return buffer_->Initialize(kBufferSize,
365 // We reached the max number of data messages that can be sent to 333 kMinAllocationSize,
366 // the renderer for a given request. Pause the request and wait for 334 kMaxAllocationSize);
367 // the renderer to start processing them before resuming it.
368 *defer = did_defer_ = true;
369 }
370
371 UMA_HISTOGRAM_CUSTOM_COUNTS(
372 "Net.AsyncResourceHandler_PendingDataCount",
373 pending_data_count_, 0, kMaxPendingDataMessages, kMaxPendingDataMessages);
374 } 335 }
375 336
376 void AsyncResourceHandler::ResumeIfDeferred() { 337 void AsyncResourceHandler::ResumeIfDeferred() {
377 if (did_defer_) { 338 if (did_defer_) {
378 did_defer_ = false; 339 did_defer_ = false;
379 controller()->Resume(); 340 controller()->Resume();
380 } 341 }
381 } 342 }
382 343
383 } // namespace content 344 } // namespace content
OLDNEW
« no previous file with comments | « content/browser/renderer_host/async_resource_handler.h ('k') | content/browser/renderer_host/resource_buffer.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698