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

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

Powered by Google App Engine
This is Rietveld 408576698