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

Side by Side Diff: net/http/http_cache_writers.cc

Issue 2886483002: Adds a new class HttpCache::Writers for multiple cache transactions reading from the network. (Closed)
Patch Set: Feedback addressed Created 3 years, 6 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
OLDNEW
(Empty)
1 // Copyright (c) 2017 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "net/http/http_cache_writers.h"
6
7 #include <algorithm>
8 #include <utility>
9 #include "net/disk_cache/disk_cache.h"
10 #include "net/http/http_cache_transaction.h"
11
12 namespace net {
13
14 HttpCache::Writers::Writers(HttpCache* cache, ActiveEntry* entry)
15 : cache_(cache), entry_(entry), weak_factory_(this) {}
16
17 HttpCache::Writers::~Writers() {}
18
19 int HttpCache::Writers::Read(scoped_refptr<IOBuffer> buf,
20 int buf_len,
21 const CompletionCallback& callback,
22 Transaction* transaction) {
23 DCHECK(buf);
24 DCHECK_GT(buf_len, 0);
25 DCHECK(!callback.is_null());
26 DCHECK(transaction);
27
28 if (next_state_ == State::FAIL_READ) {
29 DCHECK(entry_->doomed);
30 return error_code_;
31 }
32
33 // If another transaction invoked a Read which is currently ongoing, then
34 // this transaction waits for the read to complete and gets its buffer filled
35 // with the data returned from that read.
36 if (next_state_ != State::NONE) {
37 WaitingForRead waiting_transaction(transaction, buf, buf_len, callback);
38 waiting_for_read_.push_back(waiting_transaction);
39 return ERR_IO_PENDING;
40 }
41
42 DCHECK_EQ(next_state_, State::NONE);
43 DCHECK(callback_.is_null());
44 DCHECK_EQ(nullptr, active_transaction_);
45 active_transaction_ = transaction;
Randy Smith (Not in Mondays) 2017/06/08 17:10:01 Worth a DCHECK that transaction is in all_writers_
shivanisha 2017/06/12 18:51:14 done
46
47 read_buf_ = std::move(buf);
48 io_buf_len_ = buf_len;
49 next_state_ = State::NETWORK_READ;
50
51 int rv = DoLoop(OK);
52 if (rv == ERR_IO_PENDING) {
Randy Smith (Not in Mondays) 2017/06/08 17:10:01 nit, suggestion: No need for curly braces.
shivanisha 2017/06/12 18:51:14 removed
53 callback_ = callback;
54 }
55
56 return rv;
57 }
58
59 bool HttpCache::Writers::StopCaching(Transaction* transaction) {
60 // If this is the only transaction in Writers, then stopping will be
61 // successful. If not, then we will not stop caching since there are
62 // other consumers waiting to read from the cache.
63 if (all_writers_.size() == 1) {
64 DCHECK(all_writers_.count(transaction));
65 network_read_only_ = true;
66 return true;
67 }
68 return false;
69 }
70
71 void HttpCache::Writers::AddTransaction(
72 Transaction* transaction,
73 std::unique_ptr<HttpTransaction> network_transaction,
74 bool is_exclusive) {
75 DCHECK(transaction);
76 DCHECK(CanAddWriters());
77
78 std::pair<TransactionSet::iterator, bool> return_val =
79 all_writers_.insert(transaction);
80 DCHECK_EQ(return_val.second, true);
81
82 if (is_exclusive) {
83 DCHECK_EQ(all_writers_.size(), (size_t)1);
jkarlin 2017/06/12 18:30:32 DCHECK_EQ(1u, all_writers_.size()); DCHECK's cras
shivanisha 2017/06/14 02:33:16 done
84 is_exclusive_ = true;
85 }
86
87 if (network_transaction) {
88 DCHECK(!network_transaction_);
89 network_transaction_ = std::move(network_transaction);
90 }
91
92 DCHECK(network_transaction_);
jkarlin 2017/06/12 18:30:32 I'd prefer we moved this to the top of the functio
shivanisha 2017/06/14 02:33:16 done
93
94 priority_ = std::max(transaction->priority(), priority_);
95 network_transaction_->SetPriority(priority_);
96 }
97
98 void HttpCache::Writers::RemoveTransaction(Transaction* transaction) {
99 if (!transaction)
jkarlin 2017/06/12 18:30:32 Can we instead DCHECK(transaction)?
shivanisha 2017/06/14 02:33:17 Explicitly handling nullptr for the following scen
100 return;
101
102 // The transaction should be part of all_writers.
103 auto it = all_writers_.find(transaction);
104 DCHECK(it != all_writers_.end());
105 all_writers_.erase(transaction);
106 if (all_writers_.empty() && next_state_ == State::NONE)
107 ResetStateForEmptyWriters();
108 else
109 PriorityChanged();
jkarlin 2017/06/12 18:30:32 Perhaps rename to UpdatePriority since the priorit
shivanisha 2017/06/14 02:33:17 done
110
111 if (active_transaction_ == transaction) {
112 active_transaction_ = nullptr;
113 callback_.Reset();
114 return;
115 }
116
117 auto waiting_it = waiting_for_read_.begin();
118 for (; waiting_it != waiting_for_read_.end(); waiting_it++) {
119 if (transaction == waiting_it->transaction) {
120 waiting_for_read_.erase(waiting_it);
jkarlin 2017/06/12 18:30:32 Hmm, is the iterator still safe to increment after
shivanisha 2017/06/14 02:33:17 we do not increment the iterator after erase due t
121 // If a waiting transaction existed, there should have been an
122 // active_transaction_.
123 DCHECK(active_transaction_);
124 return;
125 }
126 }
127 }
128
129 void HttpCache::Writers::PriorityChanged() {
130 // Get the current highest priority.
131 RequestPriority current_highest = MINIMUM_PRIORITY;
132 for (auto* transaction : all_writers_)
133 current_highest = std::max(transaction->priority(), current_highest);
134
135 if (priority_ != current_highest) {
136 network_transaction_->SetPriority(current_highest);
137 priority_ = current_highest;
138 }
139 }
140
141 bool HttpCache::Writers::ContainsOnlyIdleWriters() const {
142 return waiting_for_read_.empty() && !active_transaction_;
143 }
144
145 void HttpCache::Writers::MoveIdleWritersToReaders() {
146 // Should be invoked after |waiting_for_read_| transactions and
jkarlin 2017/06/12 18:30:32 extra space between transactions and and
shivanisha 2017/06/14 02:33:17 done
147 // |active_transaction_| are processed so that all_writers_ only contains idle
148 // writers.
149 DCHECK(ContainsOnlyIdleWriters());
150 for (auto* idle_writer : all_writers_) {
151 entry_->readers.insert(idle_writer);
jkarlin 2017/06/12 18:30:32 I think at this point we should treat ActiveEntry
shivanisha 2017/06/14 02:33:17 done
152 }
153 all_writers_.clear();
154 ResetStateForEmptyWriters();
155 }
156
157 bool HttpCache::Writers::CanAddWriters() {
158 if (all_writers_.empty())
159 return true;
160
161 return !is_exclusive_ && !network_read_only_;
162 }
163
164 void HttpCache::Writers::ProcessFailure(Transaction* transaction, int error) {
165 DCHECK(!transaction || transaction == active_transaction_);
166
167 // Notify waiting_for_read_ of the failure. Tasks will be posted for all the
168 // transactions.
169 ProcessWaitingForReadTransactions(error);
170
171 // Idle readers should fail when Read is invoked on them.
172 SetIdleWritersFailState(error);
173 DCHECK(all_writers_.empty());
174 ResetStateForEmptyWriters();
175 }
176
177 void HttpCache::Writers::TruncateEntry() {
178 // TODO(shivanisha) On integration, see if the entry really needs to be
179 // truncated on the lines of Transaction::AddTruncatedFlag and then proceed.
180 DCHECK_EQ(next_state_, State::NONE);
181 next_state_ = State::CACHE_WRITE_TRUNCATED_RESPONSE;
182 DoLoop(OK);
183 }
184
185 LoadState HttpCache::Writers::GetWriterLoadState() {
186 DCHECK(network_transaction_);
187 return network_transaction_->GetLoadState();
188 }
189
190 HttpCache::Writers::WaitingForRead::WaitingForRead(
191 Transaction* cache_transaction,
192 scoped_refptr<IOBuffer> buf,
193 int len,
194 const CompletionCallback& consumer_callback)
195 : transaction(cache_transaction),
196 read_buf(std::move(buf)),
197 read_buf_len(len),
198 write_len(0),
199 callback(consumer_callback) {
200 DCHECK(cache_transaction);
201 DCHECK(buf);
202 DCHECK_GT(len, 0);
203 DCHECK(!consumer_callback.is_null());
204 }
205
206 HttpCache::Writers::WaitingForRead::~WaitingForRead() {}
207 HttpCache::Writers::WaitingForRead::WaitingForRead(const WaitingForRead&) =
208 default;
209
210 int HttpCache::Writers::DoLoop(int result) {
211 DCHECK(next_state_ != State::NONE);
212 int rv = result;
213 do {
214 State state = next_state_;
215 next_state_ = State::NONE;
216 switch (state) {
217 case State::NETWORK_READ:
218 DCHECK_EQ(OK, rv);
219 rv = DoNetworkRead();
220 break;
221 case State::NETWORK_READ_COMPLETE:
222 rv = DoNetworkReadComplete(rv);
223 break;
224 case State::CACHE_WRITE_DATA:
225 rv = DoCacheWriteData(rv);
226 break;
227 case State::CACHE_WRITE_DATA_COMPLETE:
228 rv = DoCacheWriteDataComplete(rv);
229 break;
230 case State::CACHE_WRITE_TRUNCATED_RESPONSE:
231 rv = DoCacheWriteTruncatedResponse();
232 break;
233 case State::CACHE_WRITE_TRUNCATED_RESPONSE_COMPLETE:
234 rv = DoCacheWriteTruncatedResponseComplete(rv);
235 break;
236 default:
237 NOTREACHED() << "bad state";
238 rv = ERR_FAILED;
239 break;
240 }
241 } while (next_state_ != State::NONE && rv != ERR_IO_PENDING);
242
243 if (rv != ERR_IO_PENDING && !callback_.is_null()) {
244 read_buf_ = NULL; // Release the buffer before invoking the callback.
245 base::ResetAndReturn(&callback_).Run(rv);
246 }
247 return rv;
248 }
249
250 int HttpCache::Writers::DoNetworkRead() {
251 next_state_ = State::NETWORK_READ_COMPLETE;
252 CompletionCallback io_callback =
253 base::Bind(&HttpCache::Writers::OnIOComplete, weak_factory_.GetWeakPtr());
254 return network_transaction_->Read(read_buf_.get(), io_buf_len_, io_callback);
255 }
256
257 int HttpCache::Writers::DoNetworkReadComplete(int result) {
258 if (result < 0) {
259 OnNetworkReadFailure(result);
260 return result;
261 }
262
263 next_state_ = State::CACHE_WRITE_DATA;
264 return result;
265 }
266
267 void HttpCache::Writers::OnNetworkReadFailure(int result) {
268 cache_->DoneWithEntry(entry_, active_transaction_, true);
269 // TODO(shivanisha): Send |result| while invoking ProcessFailure while
270 // integrating this class with HttpCache.
271 }
272
273 int HttpCache::Writers::DoCacheWriteData(int num_bytes) {
274 next_state_ = State::CACHE_WRITE_DATA_COMPLETE;
275 write_len_ = num_bytes;
276 if (!num_bytes || network_read_only_)
277 return num_bytes;
278
279 int current_size = entry_->disk_entry->GetDataSize(kResponseContentIndex);
Randy Smith (Not in Mondays) 2017/06/08 17:10:01 Why is this accurate as the offset to which to wri
shivanisha 2017/06/08 17:55:56 WriteToEntry => partial->CacheWrite which does not
Randy Smith (Not in Mondays) 2017/06/13 17:45:05 Ah, right. I'd afraid I'd call that a confusing i
shivanisha 2017/06/14 02:33:16 Moved to calling site.
280 CompletionCallback io_callback =
281 base::Bind(&HttpCache::Writers::OnIOComplete, weak_factory_.GetWeakPtr());
282 return WriteToEntry(current_size, read_buf_.get(), num_bytes, io_callback);
283 }
284
285 int HttpCache::Writers::WriteToEntry(int offset,
286 IOBuffer* data,
287 int data_len,
288 const CompletionCallback& callback) {
289 int rv = 0;
290
291 PartialData* partial = nullptr;
292 // The active transaction must be alive if this is a partial request, as
293 // partial requests are exclusive and hence will always be the active
294 // transaction.
295 // Todo(shivanisha): When partial requests support parallel writing, this
296 // assumption will not be true.
297 if (active_transaction_)
298 partial = active_transaction_->partial();
299
300 if (!partial || !data_len) {
301 rv = entry_->disk_entry->WriteData(kResponseContentIndex, offset, data,
302 data_len, callback, true);
303 } else {
304 rv = partial->CacheWrite(entry_->disk_entry, data, data_len, callback);
305 }
306 return rv;
307 }
308
309 int HttpCache::Writers::DoCacheWriteDataComplete(int result) {
310 if (result != write_len_) {
311 OnCacheWriteFailure();
312
313 // |active_transaction_| can continue reading from the network.
314 result = write_len_;
315 } else {
316 OnDataReceived(result);
317 }
318 return result;
319 }
320
321 int HttpCache::Writers::DoCacheWriteTruncatedResponse() {
322 next_state_ = State::CACHE_WRITE_TRUNCATED_RESPONSE_COMPLETE;
323 const HttpResponseInfo* response = network_transaction_->GetResponseInfo();
324 scoped_refptr<PickledIOBuffer> data(new PickledIOBuffer());
325 response->Persist(data->pickle(), true /* skip_transient_headers*/, true);
326 data->Done();
327 io_buf_len_ = data->pickle()->size();
328 CompletionCallback io_callback =
329 base::Bind(&HttpCache::Writers::OnIOComplete, weak_factory_.GetWeakPtr());
330 return entry_->disk_entry->WriteData(kResponseInfoIndex, 0, data.get(),
331 io_buf_len_, io_callback, true);
332 }
333
334 int HttpCache::Writers::DoCacheWriteTruncatedResponseComplete(int result) {
335 if (result != io_buf_len_) {
336 DLOG(ERROR) << "failed to write response info to cache";
337 cache_->DoneWritingToEntry(entry_, false);
338 }
339 truncated_ = true;
340 return OK;
341 }
342
343 void HttpCache::Writers::OnDataReceived(int result) {
344 if (result == 0) {
345 // Check if the response is actually completed or if not, attempt to mark
346 // the entry as truncated.
347 int current_size = entry_->disk_entry->GetDataSize(kResponseContentIndex);
348 const HttpResponseInfo* response_info =
349 network_transaction_->GetResponseInfo();
350 int64_t content_length = response_info->headers->GetContentLength();
351 if (content_length >= 0 && content_length > current_size) {
352 OnNetworkReadFailure(result);
353 return;
354 }
355 // TODO(shivanisha) Invoke cache_->DoneWritingToEntry() with success after
356 // integration with HttpCache layer.
357 }
358
359 // Notify waiting_for_read_. Tasks will be posted for all the
360 // transactions.
361 ProcessWaitingForReadTransactions(write_len_);
362
363 active_transaction_ = nullptr;
364
365 if (all_writers_.empty())
366 ResetStateForEmptyWriters();
367 }
368
369 void HttpCache::Writers::OnCacheWriteFailure() {
370 network_read_only_ = true;
371
372 // Call the cache_ function here even if |active_transaction_| is alive
373 // because it wouldn't know if this was an error case, since it gets a
374 // positive result back.
375 // TODO(shivanisha) : Invoke ProcessFailure here or through DoneWritingToEntry
376 // on integration.
377 cache_->DoneWritingToEntry(entry_, false);
378 }
379
380 void HttpCache::Writers::ProcessWaitingForReadTransactions(int result) {
381 for (auto& waiting : waiting_for_read_) {
382 Transaction* transaction = waiting.transaction;
383 int callback_result = result;
384
385 if (result >= 0) { // success
386 // Save the data in the waiting transaction's read buffer.
387 waiting.write_len = std::min(waiting.read_buf_len, result);
388 memcpy(waiting.read_buf->data(), read_buf_->data(), waiting.write_len);
389 callback_result = waiting.write_len;
390 }
391
392 // If its response completion or failure, this transaction needs to be
393 // removed.
394 if (result <= 0)
395 all_writers_.erase(transaction);
396
397 // Post task to notify transaction.
398 base::ThreadTaskRunnerHandle::Get()->PostTask(
399 FROM_HERE, base::Bind(waiting.callback, callback_result));
400 }
401
402 waiting_for_read_.clear();
403 }
404
405 void HttpCache::Writers::SetIdleWritersFailState(int result) {
406 // Since this is only for idle transactions, all waiting_for_read_ and
407 // active_transaction_ should be empty.
408 DCHECK(ContainsOnlyIdleWriters());
409 next_state_ = State::FAIL_READ;
410 error_code_ = result;
411 }
412
413 void HttpCache::Writers::ResetStateForEmptyWriters() {
414 DCHECK(all_writers_.empty());
415 network_read_only_ = false;
416 network_transaction_.reset();
417 }
418
419 void HttpCache::Writers::OnIOComplete(int result) {
420 DoLoop(result);
421 }
422
423 } // namespace net
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698