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

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: incorporate michaeln@'s comments 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 disk_cache::ScopedEntryPtr entry,
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 disk_cache::ScopedEntryPtr entry_;
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 disk_cache::ScopedEntryPtr entry,
94 ServiceWorkerDiskCache* src,
95 ServiceWorkerDiskCache* dest,
96 const base::WeakPtr<ServiceWorkerDiskCacheMigrator>& owner)
97 : task_id_(task_id),
98 resource_id_(resource_id),
99 entry_(entry.Pass()),
100 owner_(owner),
101 weak_factory_(this) {
102 DCHECK(entry_);
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 =
184 new net::IOBuffer(entry_->GetDataSize(0));
michaeln 2015/06/03 02:13:07 ah, i was wondering why u kept the entry_ ptr arou
nhiroki 2015/06/03 03:58:12 I see. We only have to pass the datasize... update
185 reader_->ReadData(buffer.get(), entry_->GetDataSize(0),
186 base::Bind(&Task::OnReadResponseData,
187 weak_factory_.GetWeakPtr(), buffer));
188 }
189
190 void ServiceWorkerDiskCacheMigrator::Task::OnReadResponseData(
191 const scoped_refptr<net::IOBuffer>& buffer,
192 int result) {
193 if (result < 0) {
194 LOG(ERROR) << "Failed to read the response data";
195 Finish(SERVICE_WORKER_ERROR_FAILED);
196 return;
197 }
198 writer_->WriteData(
199 buffer.get(), result,
200 base::Bind(&Task::OnWriteResponseData, weak_factory_.GetWeakPtr()));
201 }
202
203 void ServiceWorkerDiskCacheMigrator::Task::OnWriteResponseData(int result) {
204 if (result < 0) {
205 LOG(ERROR) << "Failed to write the response data";
206 Finish(SERVICE_WORKER_ERROR_FAILED);
207 return;
208 }
209 Finish(SERVICE_WORKER_OK);
210 }
211
212 void ServiceWorkerDiskCacheMigrator::Task::Finish(
213 ServiceWorkerStatusCode status) {
214 DCHECK(owner_);
215 owner_->OnResourceMigrated(task_id_, status);
216 }
217
218 ServiceWorkerDiskCacheMigrator::ServiceWorkerDiskCacheMigrator(
219 ServiceWorkerDiskCache* src,
220 ServiceWorkerDiskCache* dest)
221 : src_(src), dest_(dest), weak_factory_(this) {
222 DCHECK(!src_->is_disabled());
223 DCHECK(!dest_->is_disabled());
224 }
225
226 ServiceWorkerDiskCacheMigrator::~ServiceWorkerDiskCacheMigrator() {
227 }
228
229 void ServiceWorkerDiskCacheMigrator::Start(const StatusCallback& callback) {
230 callback_ = callback;
231 iterator_ = src_->disk_cache()->CreateIterator();
232 ContinueMigratingResources();
233 }
234
235 void ServiceWorkerDiskCacheMigrator::ContinueMigratingResources() {
236 scoped_ptr<WrappedEntry> wrapped_entry(new WrappedEntry);
237 disk_cache::Entry** entry_ptr = wrapped_entry->GetPtr();
238
239 net::CompletionCallback callback = base::Bind(
240 &ServiceWorkerDiskCacheMigrator::OnOpenNextEntry,
241 weak_factory_.GetWeakPtr(), base::Passed(wrapped_entry.Pass()));
242 int result = iterator_->OpenNextEntry(entry_ptr, callback);
243 if (result == net::ERR_IO_PENDING)
244 return;
245 callback.Run(result);
246 }
247
248 void ServiceWorkerDiskCacheMigrator::OnOpenNextEntry(
249 scoped_ptr<WrappedEntry> wrapped_entry,
250 int result) {
251 if (result == net::ERR_FAILED) {
252 // ERR_FAILED means the iterator reaches the end of the enumeration.
253 if (inflight_tasks_.IsEmpty())
254 Complete(SERVICE_WORKER_OK);
255 return;
256 }
257
258 if (result != net::OK) {
259 LOG(ERROR) << "Failed to open the next entry";
260 inflight_tasks_.Clear();
261 Complete(SERVICE_WORKER_ERROR_FAILED);
262 return;
263 }
264
265 disk_cache::ScopedEntryPtr scoped_entry(wrapped_entry->Unwrap());
266 DCHECK(scoped_entry);
267
268 int64 resource_id = kInvalidServiceWorkerResourceId;
269 if (!base::StringToInt64(scoped_entry->GetKey(), &resource_id)) {
270 LOG(ERROR) << "Failed to read the resource id";
271 inflight_tasks_.Clear();
272 Complete(SERVICE_WORKER_ERROR_FAILED);
273 return;
274 }
275
276 DCHECK(!pending_task_);
277 InflightTaskMap::KeyType task_id = next_task_id_++;
278 pending_task_.reset(new Task(task_id, resource_id, scoped_entry.Pass(), src_,
279 dest_, weak_factory_.GetWeakPtr()));
280 if (inflight_tasks_.size() < max_number_of_inflight_tasks_) {
281 RunPendingTask();
282 ContinueMigratingResources();
283 return;
284 }
285 // |pending_task_| will run when an inflight task is completed.
286 }
287
288 void ServiceWorkerDiskCacheMigrator::RunPendingTask() {
289 DCHECK(pending_task_);
290 DCHECK_GT(max_number_of_inflight_tasks_, inflight_tasks_.size());
291 InflightTaskMap::KeyType task_id = pending_task_->task_id();
292 pending_task_->Run();
293 inflight_tasks_.AddWithID(pending_task_.release(), task_id);
294 }
295
296 void ServiceWorkerDiskCacheMigrator::OnResourceMigrated(
297 InflightTaskMap::KeyType task_id,
298 ServiceWorkerStatusCode status) {
299 DCHECK(inflight_tasks_.Lookup(task_id));
300 inflight_tasks_.Remove(task_id);
301
302 if (status != SERVICE_WORKER_OK) {
303 inflight_tasks_.Clear();
304 Complete(status);
305 return;
306 }
307
308 if (pending_task_) {
309 RunPendingTask();
310 ContinueMigratingResources();
311 return;
312 }
313
314 if (inflight_tasks_.IsEmpty()) {
315 Complete(SERVICE_WORKER_OK);
michaeln 2015/06/03 02:13:07 Is there a race here? Is it possible for task->Run
nhiroki 2015/06/03 03:58:12 Good point. The race could happen depending on the
316 return;
317 }
318 }
319
320 void ServiceWorkerDiskCacheMigrator::Complete(ServiceWorkerStatusCode status) {
321 DCHECK(inflight_tasks_.IsEmpty());
322 // TODO(nhiroki): Add UMA for the result of migration.
323 callback_.Run(status);
324 }
325
326 } // namespace content
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698