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

Side by Side Diff: content/browser/service_worker/service_worker_disk_cache_migrator.cc

Issue 1155063002: ServiceWorker: Introduce ServiceWorkerDiskCacheMigrator (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: fix race and clean up Created 5 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 2015 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 "content/browser/service_worker/service_worker_disk_cache_migrator.h"
6
7 #include "base/memory/ref_counted.h"
8 #include "base/strings/string_number_conversions.h"
9 #include "content/common/service_worker/service_worker_types.h"
10 #include "net/base/io_buffer.h"
11 #include "net/base/net_errors.h"
12 #include "net/disk_cache/disk_cache.h"
13
14 namespace content {
15
16 // A task to move a cached resource from the src DiskCache to the dest
17 // DiskCache. This is owned by ServiceWorkerDiskCacheMigrator.
18 class ServiceWorkerDiskCacheMigrator::Task {
19 public:
20 Task(InflightTaskMap::KeyType task_id,
21 int64 resource_id,
22 int32 data_size,
23 ServiceWorkerDiskCache* src,
24 ServiceWorkerDiskCache* dest,
25 const base::WeakPtr<ServiceWorkerDiskCacheMigrator>& owner);
26 ~Task();
27
28 void Run();
29
30 InflightTaskMap::KeyType task_id() const { return task_id_; }
31
32 private:
33 void ReadResponseInfo();
34 void OnReadResponseInfo(
35 const scoped_refptr<HttpResponseInfoIOBuffer>& info_buffer,
36 int result);
37 void OnWriteResponseInfo(
38 const scoped_refptr<HttpResponseInfoIOBuffer>& info_buffer,
39 int result);
40 void WriteResponseMetadata(
41 const scoped_refptr<HttpResponseInfoIOBuffer>& info_buffer);
42 void OnWriteResponseMetadata(
43 const scoped_refptr<HttpResponseInfoIOBuffer>& info_buffer,
44 int result);
45 void ReadResponseData();
46 void OnReadResponseData(const scoped_refptr<net::IOBuffer>& buffer,
47 int result);
48 void OnWriteResponseData(int result);
49 void Finish(ServiceWorkerStatusCode status);
50
51 InflightTaskMap::KeyType task_id_;
52 int64 resource_id_;
53 int32 data_size_;
54 base::WeakPtr<ServiceWorkerDiskCacheMigrator> owner_;
55
56 scoped_ptr<ServiceWorkerResponseReader> reader_;
57 scoped_ptr<ServiceWorkerResponseWriter> writer_;
58 scoped_ptr<ServiceWorkerResponseMetadataWriter> metadata_writer_;
59
60 base::WeakPtrFactory<Task> weak_factory_;
61
62 DISALLOW_COPY_AND_ASSIGN(Task);
63 };
64
65 // A wrapper class for disk_cache::Entry. This is used for holding an open entry
66 // and ensuring that the entry gets closed on the dtor.
67 class ServiceWorkerDiskCacheMigrator::WrappedEntry {
68 public:
69 WrappedEntry() {}
70
71 ~WrappedEntry() {
72 if (entry_)
73 entry_->Close();
74 }
75
76 disk_cache::Entry* Unwrap() {
77 disk_cache::Entry* entry = entry_;
78 entry_ = nullptr;
79 return entry;
80 }
81
82 disk_cache::Entry** GetPtr() { return &entry_; }
83
84 private:
85 disk_cache::Entry* entry_ = nullptr;
86
87 DISALLOW_COPY_AND_ASSIGN(WrappedEntry);
88 };
89
90 ServiceWorkerDiskCacheMigrator::Task::Task(
91 InflightTaskMap::KeyType task_id,
92 int64 resource_id,
93 int32 data_size,
94 ServiceWorkerDiskCache* src,
95 ServiceWorkerDiskCache* dest,
96 const base::WeakPtr<ServiceWorkerDiskCacheMigrator>& owner)
97 : task_id_(task_id),
98 resource_id_(resource_id),
99 data_size_(data_size),
100 owner_(owner),
101 weak_factory_(this) {
102 DCHECK_LT(0, data_size_);
103 reader_.reset(new ServiceWorkerResponseReader(resource_id, src));
104 writer_.reset(new ServiceWorkerResponseWriter(resource_id, dest));
105 metadata_writer_.reset(
106 new ServiceWorkerResponseMetadataWriter(resource_id, dest));
107 }
108
109 ServiceWorkerDiskCacheMigrator::Task::~Task() {
110 }
111
112 void ServiceWorkerDiskCacheMigrator::Task::Run() {
113 ReadResponseInfo();
114 }
115
116 void ServiceWorkerDiskCacheMigrator::Task::ReadResponseInfo() {
117 scoped_refptr<HttpResponseInfoIOBuffer> info_buffer(
118 new HttpResponseInfoIOBuffer);
119 reader_->ReadInfo(info_buffer.get(),
120 base::Bind(&Task::OnReadResponseInfo,
121 weak_factory_.GetWeakPtr(), info_buffer));
122 }
123
124 void ServiceWorkerDiskCacheMigrator::Task::OnReadResponseInfo(
125 const scoped_refptr<HttpResponseInfoIOBuffer>& info_buffer,
126 int result) {
127 if (result < 0) {
128 LOG(ERROR) << "Failed to read the response info";
129 Finish(SERVICE_WORKER_ERROR_FAILED);
130 return;
131 }
132 writer_->WriteInfo(info_buffer.get(),
133 base::Bind(&Task::OnWriteResponseInfo,
134 weak_factory_.GetWeakPtr(), info_buffer));
135 }
136
137 void ServiceWorkerDiskCacheMigrator::Task::OnWriteResponseInfo(
138 const scoped_refptr<HttpResponseInfoIOBuffer>& buffer,
139 int result) {
140 if (result < 0) {
141 LOG(ERROR) << "Failed to write the response info";
142 Finish(SERVICE_WORKER_ERROR_FAILED);
143 return;
144 }
145
146 const net::HttpResponseInfo* http_info = buffer->http_info.get();
147 if (http_info->metadata && http_info->metadata->size()) {
148 WriteResponseMetadata(buffer);
149 return;
150 }
151 ReadResponseData();
152 }
153
154 void ServiceWorkerDiskCacheMigrator::Task::WriteResponseMetadata(
155 const scoped_refptr<HttpResponseInfoIOBuffer>& info_buffer) {
156 const net::HttpResponseInfo* http_info = info_buffer->http_info.get();
157 DCHECK(http_info->metadata);
158 DCHECK(http_info->metadata->size());
159
160 // |wrapped_buffer| does not own the given metadata buffer, so a callback
161 // for WriteMetadata keeps |info_buffer| which is the real owner of the
162 // metadata buffer.
163 scoped_refptr<net::WrappedIOBuffer> wrapped_buffer =
164 new net::WrappedIOBuffer(http_info->metadata->data());
165 metadata_writer_->WriteMetadata(
166 wrapped_buffer.get(), http_info->metadata->size(),
167 base::Bind(&Task::OnWriteResponseMetadata, weak_factory_.GetWeakPtr(),
168 info_buffer));
169 }
170
171 void ServiceWorkerDiskCacheMigrator::Task::OnWriteResponseMetadata(
172 const scoped_refptr<HttpResponseInfoIOBuffer>& protect,
173 int result) {
174 if (result < 0) {
175 LOG(ERROR) << "Failed to write the response metadata";
176 Finish(SERVICE_WORKER_ERROR_FAILED);
177 return;
178 }
179 ReadResponseData();
180 }
181
182 void ServiceWorkerDiskCacheMigrator::Task::ReadResponseData() {
183 scoped_refptr<net::IOBuffer> buffer = new net::IOBuffer(data_size_);
184 reader_->ReadData(buffer.get(), data_size_,
185 base::Bind(&Task::OnReadResponseData,
186 weak_factory_.GetWeakPtr(), buffer));
187 }
188
189 void ServiceWorkerDiskCacheMigrator::Task::OnReadResponseData(
190 const scoped_refptr<net::IOBuffer>& buffer,
191 int result) {
192 if (result < 0) {
193 LOG(ERROR) << "Failed to read the response data";
194 Finish(SERVICE_WORKER_ERROR_FAILED);
195 return;
196 }
197 writer_->WriteData(
198 buffer.get(), result,
199 base::Bind(&Task::OnWriteResponseData, weak_factory_.GetWeakPtr()));
200 }
201
202 void ServiceWorkerDiskCacheMigrator::Task::OnWriteResponseData(int result) {
203 if (result < 0) {
204 LOG(ERROR) << "Failed to write the response data";
205 Finish(SERVICE_WORKER_ERROR_FAILED);
206 return;
207 }
208 Finish(SERVICE_WORKER_OK);
209 }
210
211 void ServiceWorkerDiskCacheMigrator::Task::Finish(
212 ServiceWorkerStatusCode status) {
213 DCHECK(owner_);
214 owner_->OnEntryMigrated(task_id_, status);
215 }
216
217 ServiceWorkerDiskCacheMigrator::ServiceWorkerDiskCacheMigrator(
218 ServiceWorkerDiskCache* src,
219 ServiceWorkerDiskCache* dest)
220 : src_(src), dest_(dest), weak_factory_(this) {
221 DCHECK(!src_->is_disabled());
222 DCHECK(!dest_->is_disabled());
223 }
224
225 ServiceWorkerDiskCacheMigrator::~ServiceWorkerDiskCacheMigrator() {
226 }
227
228 void ServiceWorkerDiskCacheMigrator::Start(const StatusCallback& callback) {
229 callback_ = callback;
230 iterator_ = src_->disk_cache()->CreateIterator();
231 OpenNextEntry();
232 }
233
234 void ServiceWorkerDiskCacheMigrator::OpenNextEntry() {
235 DCHECK(!pending_task_);
236 DCHECK(!is_iterating_);
237 is_iterating_ = true;
238
239 scoped_ptr<WrappedEntry> wrapped_entry(new WrappedEntry);
240 disk_cache::Entry** entry_ptr = wrapped_entry->GetPtr();
241
242 net::CompletionCallback callback = base::Bind(
243 &ServiceWorkerDiskCacheMigrator::OnNextEntryOpened,
244 weak_factory_.GetWeakPtr(), base::Passed(wrapped_entry.Pass()));
245 int result = iterator_->OpenNextEntry(entry_ptr, callback);
246 if (result == net::ERR_IO_PENDING)
247 return;
248 callback.Run(result);
249 }
250
251 void ServiceWorkerDiskCacheMigrator::OnNextEntryOpened(
252 scoped_ptr<WrappedEntry> wrapped_entry,
253 int result) {
254 DCHECK(!pending_task_);
255 is_iterating_ = false;
256
257 if (result == net::ERR_FAILED) {
258 // ERR_FAILED means the iterator reaches the end of the enumeration.
falken 2015/06/03 06:45:33 nit: reached
nhiroki 2015/06/03 08:51:22 Done.
259 if (inflight_tasks_.IsEmpty())
260 Complete(SERVICE_WORKER_OK);
261 return;
262 }
263
264 if (result != net::OK) {
265 LOG(ERROR) << "Failed to open the next entry";
266 inflight_tasks_.Clear();
267 Complete(SERVICE_WORKER_ERROR_FAILED);
268 return;
269 }
270
271 disk_cache::ScopedEntryPtr scoped_entry(wrapped_entry->Unwrap());
272 DCHECK(scoped_entry);
273
274 int64 resource_id = kInvalidServiceWorkerResourceId;
275 if (!base::StringToInt64(scoped_entry->GetKey(), &resource_id)) {
276 LOG(ERROR) << "Failed to read the resource id";
277 inflight_tasks_.Clear();
278 Complete(SERVICE_WORKER_ERROR_FAILED);
279 return;
280 }
281
282 InflightTaskMap::KeyType task_id = next_task_id_++;
283 pending_task_.reset(new Task(task_id, resource_id,
284 scoped_entry->GetDataSize(0), src_, dest_,
falken 2015/06/03 06:45:33 What's the 0 from?
nhiroki 2015/06/03 08:51:22 This is the data type and '0' means "ResponseInfo"
285 weak_factory_.GetWeakPtr()));
286 if (inflight_tasks_.size() < max_number_of_inflight_tasks_) {
287 RunPendingTask();
288 OpenNextEntry();
289 return;
290 }
291 // |pending_task_| will run when an inflight task is completed.
292 }
293
294 void ServiceWorkerDiskCacheMigrator::RunPendingTask() {
295 DCHECK(pending_task_);
296 DCHECK_GT(max_number_of_inflight_tasks_, inflight_tasks_.size());
297 InflightTaskMap::KeyType task_id = pending_task_->task_id();
298 pending_task_->Run();
299 inflight_tasks_.AddWithID(pending_task_.release(), task_id);
300 }
301
302 void ServiceWorkerDiskCacheMigrator::OnEntryMigrated(
303 InflightTaskMap::KeyType task_id,
304 ServiceWorkerStatusCode status) {
305 DCHECK(inflight_tasks_.Lookup(task_id));
306 inflight_tasks_.Remove(task_id);
307
308 if (status != SERVICE_WORKER_OK) {
309 inflight_tasks_.Clear();
310 Complete(status);
311 return;
312 }
313
314 if (pending_task_) {
315 RunPendingTask();
316 OpenNextEntry();
317 return;
318 }
319
320 if (is_iterating_)
321 return;
322
323 if (inflight_tasks_.IsEmpty()) {
324 Complete(SERVICE_WORKER_OK);
325 return;
falken 2015/06/03 06:45:33 nit: dont' need this return
nhiroki 2015/06/03 08:51:22 Done.
326 }
327 }
328
329 void ServiceWorkerDiskCacheMigrator::Complete(ServiceWorkerStatusCode status) {
330 DCHECK(inflight_tasks_.IsEmpty());
331 // TODO(nhiroki): Add UMA for the result of migration.
332 callback_.Run(status);
333 }
334
335 } // namespace content
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698