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

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 tests and support abort/delete oprations 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 base::RefCounted<ServiceWorkerDiskCacheMigrator::Task> {
kinuko 2015/05/27 05:45:14 nit: It's not obvious why this needs to be ref-cou
nhiroki 2015/05/27 10:05:27 I thought the owner of the disk_cache::Entry* shou
20 public:
21 explicit Task(const base::WeakPtr<ServiceWorkerDiskCacheMigrator>& owner);
22
23 void Run(int task_id,
24 ServiceWorkerDiskCache* src,
25 ServiceWorkerDiskCache* dest);
26 void Abort();
27
28 private:
29 friend class base::RefCounted<ServiceWorkerDiskCacheMigrator::Task>;
30 friend class ServiceWorkerDiskCacheMigrator;
kinuko 2015/05/27 05:45:14 nit: having this Migrator class friend seems to im
nhiroki 2015/05/27 10:05:27 These 'friend' are no longer necessary. Removed.
31
32 ~Task();
33
34 void ReadResponseInfo();
35 void OnReadResponseInfo(
36 const scoped_refptr<HttpResponseInfoIOBuffer>& info_buffer,
37 int result);
38 void OnWriteResponseInfo(
39 const scoped_refptr<HttpResponseInfoIOBuffer>& info_buffer,
40 int result);
41 void WriteResponseMetadata(
42 const scoped_refptr<HttpResponseInfoIOBuffer>& info_buffer);
43 void OnWriteResponseMetadata(int result);
44 void ReadResponseData();
45 void OnReadResponseData(const scoped_refptr<net::IOBuffer>& buffer,
46 int result);
47 void OnWriteResponseData(int result);
48 void DeleteResponse();
49 void OnDeleteResponse(int result);
50 void Finish(ServiceWorkerStatusCode status);
51
52 IDMap<Task>::KeyType task_id_;
53 int64 resource_id_ = kInvalidServiceWorkerResourceId;
54 bool is_aborted_ = false;
55
56 base::WeakPtr<ServiceWorkerDiskCacheMigrator> owner_;
57 ServiceWorkerDiskCache* src_ = nullptr;
58 disk_cache::Entry* entry_ = nullptr;
59
60 scoped_ptr<ServiceWorkerResponseReader> reader_;
61 scoped_ptr<ServiceWorkerResponseWriter> writer_;
62 scoped_ptr<ServiceWorkerResponseMetadataWriter> metadata_writer_;
63
64 DISALLOW_COPY_AND_ASSIGN(Task);
65 };
66
67 ServiceWorkerDiskCacheMigrator::Task::Task(
68 const base::WeakPtr<ServiceWorkerDiskCacheMigrator>& owner)
69 : owner_(owner) {
70 }
71
72 ServiceWorkerDiskCacheMigrator::Task::~Task() {
73 if (entry_)
74 entry_->Close();
75 }
76
77 void ServiceWorkerDiskCacheMigrator::Task::Run(IDMap<Task>::KeyType task_id,
78 ServiceWorkerDiskCache* src,
79 ServiceWorkerDiskCache* dest) {
80 task_id_ = task_id;
81 src_ = src;
82
83 DCHECK(entry_);
84 if (!base::StringToInt64(entry_->GetKey(), &resource_id_)) {
85 LOG(ERROR) << "Failed to read the resource id";
86 Finish(SERVICE_WORKER_ERROR_FAILED);
87 return;
88 }
89 DCHECK_NE(kInvalidServiceWorkerResourceId, resource_id_);
90
91 reader_.reset(new ServiceWorkerResponseReader(resource_id_, src));
92 writer_.reset(new ServiceWorkerResponseWriter(resource_id_, dest));
93 metadata_writer_.reset(
94 new ServiceWorkerResponseMetadataWriter(resource_id_, dest));
95
96 ReadResponseInfo();
97 }
98
99 void ServiceWorkerDiskCacheMigrator::Task::Abort() {
100 is_aborted_ = true;
101 }
102
103 void ServiceWorkerDiskCacheMigrator::Task::ReadResponseInfo() {
104 scoped_refptr<HttpResponseInfoIOBuffer> info_buffer(
105 new HttpResponseInfoIOBuffer);
106 reader_->ReadInfo(info_buffer.get(),
107 base::Bind(&Task::OnReadResponseInfo, this, info_buffer));
108 }
109
110 void ServiceWorkerDiskCacheMigrator::Task::OnReadResponseInfo(
111 const scoped_refptr<HttpResponseInfoIOBuffer>& info_buffer,
112 int result) {
113 if (is_aborted_)
114 return;
115 if (result < 0) {
116 LOG(ERROR) << "Failed to read the response info";
117 Finish(SERVICE_WORKER_ERROR_FAILED);
118 return;
119 }
120 writer_->WriteInfo(info_buffer.get(),
121 base::Bind(&Task::OnWriteResponseInfo, this, info_buffer));
122 }
123
124 void ServiceWorkerDiskCacheMigrator::Task::OnWriteResponseInfo(
125 const scoped_refptr<HttpResponseInfoIOBuffer>& buffer,
126 int result) {
127 if (is_aborted_)
128 return;
129 if (result < 0) {
130 LOG(ERROR) << "Failed to write the response info";
131 Finish(SERVICE_WORKER_ERROR_FAILED);
132 return;
133 }
134
135 const net::HttpResponseInfo* http_info = buffer->http_info.get();
136 if (http_info->metadata) {
137 WriteResponseMetadata(buffer);
138 return;
139 }
140 ReadResponseData();
141 }
142
143 void ServiceWorkerDiskCacheMigrator::Task::WriteResponseMetadata(
144 const scoped_refptr<HttpResponseInfoIOBuffer>& info_buffer) {
145 const net::HttpResponseInfo* http_info = info_buffer->http_info.get();
146 const int data_size = http_info->metadata->size();
147
148 scoped_refptr<net::IOBuffer> buffer = new net::IOBuffer(data_size);
149 if (data_size)
150 memmove(buffer->data(), http_info->metadata->data(), data_size);
151 metadata_writer_->WriteMetadata(
152 buffer.get(), data_size,
153 base::Bind(&Task::OnWriteResponseMetadata, this));
154 }
155
156 void ServiceWorkerDiskCacheMigrator::Task::OnWriteResponseMetadata(int result) {
157 if (is_aborted_)
158 return;
159 if (result < 0) {
160 LOG(ERROR) << "Failed to write the response metadata";
161 Finish(SERVICE_WORKER_ERROR_FAILED);
162 return;
163 }
164 ReadResponseData();
165 }
166
167 void ServiceWorkerDiskCacheMigrator::Task::ReadResponseData() {
168 scoped_refptr<net::IOBuffer> buffer =
169 new net::IOBuffer(entry_->GetDataSize(0));
170 reader_->ReadData(buffer.get(), entry_->GetDataSize(0),
171 base::Bind(&Task::OnReadResponseData, this, buffer));
172 }
173
174 void ServiceWorkerDiskCacheMigrator::Task::OnReadResponseData(
175 const scoped_refptr<net::IOBuffer>& buffer,
176 int result) {
177 if (is_aborted_)
178 return;
179 if (result < 0) {
180 LOG(ERROR) << "Failed to read the response data";
181 Finish(SERVICE_WORKER_ERROR_FAILED);
182 return;
183 }
184 writer_->WriteData(buffer.get(), result,
185 base::Bind(&Task::OnWriteResponseData, this));
186 }
187
188 void ServiceWorkerDiskCacheMigrator::Task::OnWriteResponseData(int result) {
189 if (is_aborted_)
190 return;
191 if (result < 0) {
192 LOG(ERROR) << "Failed to write the response data";
193 Finish(SERVICE_WORKER_ERROR_FAILED);
194 return;
195 }
196 DeleteResponse();
197 }
198
199 void ServiceWorkerDiskCacheMigrator::Task::DeleteResponse() {
200 // Delete the response from the src diskcache so that we can avoid migrating
201 // it twice when remaining tasks are cancelled due to the storage shutdown and
202 // a retry happens.
kinuko 2015/05/27 05:45:15 Do we really want to delete entry one by one while
nhiroki 2015/05/27 10:05:27 Right, half-migrated resources or migrated-but-not
203 int result =
204 src_->DoomEntry(resource_id_, base::Bind(&Task::OnDeleteResponse, this));
205 if (result == net::ERR_IO_PENDING)
206 return;
207 OnDeleteResponse(result);
208 }
209
210 void ServiceWorkerDiskCacheMigrator::Task::OnDeleteResponse(int result) {
211 if (is_aborted_)
212 return;
213 if (result < 0) {
214 LOG(ERROR) << "Failed to delete the response";
215 // Ignore an error and try to continue migrating. A leftover response will
216 // be deleted with the diskcache directory when all tasks are completed.
217 }
218 Finish(SERVICE_WORKER_OK);
219 }
220
221 void ServiceWorkerDiskCacheMigrator::Task::Finish(
222 ServiceWorkerStatusCode status) {
223 if (owner_)
224 owner_->OnResourceMigrated(task_id_, status);
225 }
226
227 ServiceWorkerDiskCacheMigrator::ServiceWorkerDiskCacheMigrator(
228 ServiceWorkerDiskCache* src,
229 ServiceWorkerDiskCache* dest,
230 const StatusCallback& callback)
231 : src_(src), dest_(dest), callback_(callback), weak_factory_(this) {
232 DCHECK(!src_->is_disabled());
233 DCHECK(!dest_->is_disabled());
234 }
235
236 ServiceWorkerDiskCacheMigrator::~ServiceWorkerDiskCacheMigrator() {
237 }
238
239 void ServiceWorkerDiskCacheMigrator::Start() {
240 iterator_ = src_->disk_cache()->CreateIterator();
241 ContinueMigratingResources();
242 }
243
244 void ServiceWorkerDiskCacheMigrator::ContinueMigratingResources() {
245 scoped_refptr<Task> next_task(new Task(weak_factory_.GetWeakPtr()));
kinuko 2015/05/27 05:45:15 Instead of doing this here and initializing necess
nhiroki 2015/05/27 10:05:27 Done.
246
247 int result = iterator_->OpenNextEntry(
248 &next_task->entry_,
249 base::Bind(&ServiceWorkerDiskCacheMigrator::OnOpenNextEntry,
250 weak_factory_.GetWeakPtr(), next_task));
251 if (result == net::ERR_IO_PENDING)
252 return;
253 OnOpenNextEntry(next_task, result);
254 }
255
256 void ServiceWorkerDiskCacheMigrator::OnOpenNextEntry(
257 const scoped_refptr<Task>& next_task,
258 int result) {
259 if (result == net::ERR_FAILED) {
260 // ERR_FAILED means the iterator reaches the end of the enumeration.
261 if (inflight_tasks_.IsEmpty())
262 callback_.Run(SERVICE_WORKER_OK);
263 return;
264 }
265
266 if (result != net::OK) {
267 LOG(ERROR) << "Failed to open the next entry";
268 callback_.Run(SERVICE_WORKER_ERROR_FAILED);
269 return;
270 }
271
272 IDMap<Task>::KeyType task_id = inflight_tasks_.Add(next_task.get());
273 next_task->Run(task_id, src_, dest_);
274
275 ContinueMigratingResources();
276 }
277
278 void ServiceWorkerDiskCacheMigrator::OnResourceMigrated(
279 IDMap<Task>::KeyType task_id,
280 ServiceWorkerStatusCode status) {
281 DCHECK(inflight_tasks_.Lookup(task_id));
282 inflight_tasks_.Remove(task_id);
283
284 if (status != SERVICE_WORKER_OK) {
285 AbortAllTasks();
286 callback_.Run(status);
287 return;
288 }
289
290 if (inflight_tasks_.IsEmpty()) {
291 callback_.Run(SERVICE_WORKER_OK);
292 return;
293 }
294
295 // |callback_| will be invoked when all inflight tasks are completed.
296 }
297
298 void ServiceWorkerDiskCacheMigrator::AbortAllTasks() {
299 for (IDMap<Task>::iterator it(&inflight_tasks_); !it.IsAtEnd();
300 it.Advance()) {
301 Task* task = it.GetCurrentValue();
302 DCHECK(task);
303 task->Abort();
304 }
305 inflight_tasks_.Clear();
306 }
307
308 } // namespace content
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698