Chromium Code Reviews| OLD | NEW |
|---|---|
| (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 | |
| OLD | NEW |