OLD | NEW |
1 // Copyright (c) 2013 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2013 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "storage/browser/blob/blob_storage_context.h" | 5 #include "storage/browser/blob/blob_storage_context.h" |
6 | 6 |
7 #include <stddef.h> | 7 #include <stddef.h> |
8 #include <stdint.h> | 8 #include <stdint.h> |
9 #include <algorithm> | 9 #include <algorithm> |
10 #include <limits> | 10 #include <limits> |
11 #include <utility> | 11 #include <utility> |
12 | 12 |
13 #include "base/bind.h" | 13 #include "base/bind.h" |
| 14 #include "base/callback.h" |
14 #include "base/location.h" | 15 #include "base/location.h" |
15 #include "base/logging.h" | 16 #include "base/logging.h" |
16 #include "base/memory/scoped_ptr.h" | 17 #include "base/memory/scoped_ptr.h" |
| 18 #include "base/message_loop/message_loop.h" |
17 #include "base/metrics/histogram.h" | 19 #include "base/metrics/histogram.h" |
18 #include "base/stl_util.h" | |
19 #include "base/thread_task_runner_handle.h" | 20 #include "base/thread_task_runner_handle.h" |
20 #include "base/trace_event/trace_event.h" | 21 #include "base/trace_event/trace_event.h" |
21 #include "storage/browser/blob/blob_data_builder.h" | 22 #include "storage/browser/blob/blob_data_builder.h" |
22 #include "storage/browser/blob/shareable_file_reference.h" | 23 #include "storage/browser/blob/blob_data_handle.h" |
| 24 #include "storage/browser/blob/blob_data_item.h" |
| 25 #include "storage/browser/blob/blob_data_snapshot.h" |
| 26 #include "storage/browser/blob/shareable_blob_data_item.h" |
23 #include "url/gurl.h" | 27 #include "url/gurl.h" |
24 | 28 |
25 namespace storage { | 29 namespace storage { |
| 30 using BlobRegistryEntry = BlobStorageRegistry::Entry; |
| 31 using BlobState = BlobStorageRegistry::BlobState; |
26 | 32 |
27 namespace { | 33 BlobStorageContext::BlobStorageContext() : memory_usage_(0) {} |
28 | |
29 // We can't use GURL directly for these hash fragment manipulations | |
30 // since it doesn't have specific knowlege of the BlobURL format. GURL | |
31 // treats BlobURLs as if they were PathURLs which don't support hash | |
32 // fragments. | |
33 | |
34 bool BlobUrlHasRef(const GURL& url) { | |
35 return url.spec().find('#') != std::string::npos; | |
36 } | |
37 | |
38 GURL ClearBlobUrlRef(const GURL& url) { | |
39 size_t hash_pos = url.spec().find('#'); | |
40 if (hash_pos == std::string::npos) | |
41 return url; | |
42 return GURL(url.spec().substr(0, hash_pos)); | |
43 } | |
44 | |
45 // TODO(michaeln): use base::SysInfo::AmountOfPhysicalMemoryMB() in some | |
46 // way to come up with a better limit. | |
47 static const int64_t kMaxMemoryUsage = 500 * 1024 * 1024; // Half a gig. | |
48 | |
49 } // namespace | |
50 | |
51 BlobStorageContext::BlobMapEntry::BlobMapEntry() : refcount(0), flags(0) { | |
52 } | |
53 | |
54 BlobStorageContext::BlobMapEntry::BlobMapEntry(int refcount, | |
55 InternalBlobData::Builder* data) | |
56 : refcount(refcount), flags(0), data_builder(data) { | |
57 } | |
58 | |
59 BlobStorageContext::BlobMapEntry::~BlobMapEntry() { | |
60 } | |
61 | |
62 bool BlobStorageContext::BlobMapEntry::IsBeingBuilt() { | |
63 return !!data_builder; | |
64 } | |
65 | |
66 BlobStorageContext::BlobStorageContext() : memory_usage_(0) { | |
67 } | |
68 | 34 |
69 BlobStorageContext::~BlobStorageContext() { | 35 BlobStorageContext::~BlobStorageContext() { |
70 STLDeleteContainerPairSecondPointers(blob_map_.begin(), blob_map_.end()); | |
71 } | 36 } |
72 | 37 |
73 scoped_ptr<BlobDataHandle> BlobStorageContext::GetBlobDataFromUUID( | 38 scoped_ptr<BlobDataHandle> BlobStorageContext::GetBlobDataFromUUID( |
74 const std::string& uuid) { | 39 const std::string& uuid) { |
75 scoped_ptr<BlobDataHandle> result; | 40 BlobRegistryEntry* entry = registry_.GetEntry(uuid); |
76 BlobMap::iterator found = blob_map_.find(uuid); | 41 if (!entry) { |
77 if (found == blob_map_.end()) | 42 return nullptr; |
78 return result; | 43 } |
79 auto* entry = found->second; | 44 return make_scoped_ptr( |
80 if (entry->flags & EXCEEDED_MEMORY) | 45 new BlobDataHandle(uuid, entry->content_type, entry->content_disposition, |
81 return result; | 46 this, base::ThreadTaskRunnerHandle::Get().get())); |
82 DCHECK(!entry->IsBeingBuilt()); | |
83 result.reset(new BlobDataHandle(uuid, entry->data->content_type(), | |
84 entry->data->content_disposition(), this, | |
85 base::ThreadTaskRunnerHandle::Get().get())); | |
86 return result; | |
87 } | 47 } |
88 | 48 |
89 scoped_ptr<BlobDataHandle> BlobStorageContext::GetBlobDataFromPublicURL( | 49 scoped_ptr<BlobDataHandle> BlobStorageContext::GetBlobDataFromPublicURL( |
90 const GURL& url) { | 50 const GURL& url) { |
91 BlobURLMap::iterator found = | 51 std::string uuid; |
92 public_blob_urls_.find(BlobUrlHasRef(url) ? ClearBlobUrlRef(url) : url); | 52 BlobRegistryEntry* entry = registry_.GetEntryFromURL(url, &uuid); |
93 if (found == public_blob_urls_.end()) | 53 if (!entry) { |
94 return scoped_ptr<BlobDataHandle>(); | 54 return nullptr; |
95 return GetBlobDataFromUUID(found->second); | 55 } |
| 56 return make_scoped_ptr( |
| 57 new BlobDataHandle(uuid, entry->content_type, entry->content_disposition, |
| 58 this, base::ThreadTaskRunnerHandle::Get().get())); |
96 } | 59 } |
97 | 60 |
98 scoped_ptr<BlobDataHandle> BlobStorageContext::AddFinishedBlob( | 61 scoped_ptr<BlobDataHandle> BlobStorageContext::AddFinishedBlob( |
99 const BlobDataBuilder& external_builder) { | 62 const BlobDataBuilder& external_builder) { |
100 TRACE_EVENT0("Blob", "Context::AddFinishedBlob"); | 63 TRACE_EVENT0("Blob", "Context::AddFinishedBlob"); |
101 StartBuildingBlob(external_builder.uuid_); | 64 CreatePendingBlob(external_builder.uuid(), external_builder.content_type_, |
102 BlobMap::iterator found = blob_map_.find(external_builder.uuid_); | 65 external_builder.content_disposition_); |
103 DCHECK(found != blob_map_.end()); | 66 CompletePendingBlob(external_builder); |
104 BlobMapEntry* entry = found->second; | |
105 InternalBlobData::Builder* target_blob_builder = entry->data_builder.get(); | |
106 DCHECK(target_blob_builder); | |
107 | |
108 target_blob_builder->set_content_disposition( | |
109 external_builder.content_disposition_); | |
110 for (const auto& blob_item : external_builder.items_) { | |
111 if (!AppendAllocatedBlobItem(external_builder.uuid_, blob_item, | |
112 target_blob_builder)) { | |
113 BlobEntryExceededMemory(entry); | |
114 break; | |
115 } | |
116 } | |
117 | |
118 FinishBuildingBlob(external_builder.uuid_, external_builder.content_type_); | |
119 scoped_ptr<BlobDataHandle> handle = | 67 scoped_ptr<BlobDataHandle> handle = |
120 GetBlobDataFromUUID(external_builder.uuid_); | 68 GetBlobDataFromUUID(external_builder.uuid_); |
121 DecrementBlobRefCount(external_builder.uuid_); | 69 DecrementBlobRefCount(external_builder.uuid_); |
122 return handle; | 70 return handle; |
123 } | 71 } |
124 | 72 |
125 scoped_ptr<BlobDataHandle> BlobStorageContext::AddFinishedBlob( | 73 scoped_ptr<BlobDataHandle> BlobStorageContext::AddFinishedBlob( |
126 const BlobDataBuilder* builder) { | 74 const BlobDataBuilder* builder) { |
127 DCHECK(builder); | 75 DCHECK(builder); |
128 return AddFinishedBlob(*builder); | 76 return AddFinishedBlob(*builder); |
129 } | 77 } |
130 | 78 |
131 bool BlobStorageContext::RegisterPublicBlobURL(const GURL& blob_url, | 79 bool BlobStorageContext::RegisterPublicBlobURL(const GURL& blob_url, |
132 const std::string& uuid) { | 80 const std::string& uuid) { |
133 DCHECK(!BlobUrlHasRef(blob_url)); | 81 if (!registry_.CreateUrlMapping(blob_url, uuid)) { |
134 DCHECK(IsInUse(uuid)); | |
135 DCHECK(!IsUrlRegistered(blob_url)); | |
136 if (!IsInUse(uuid) || IsUrlRegistered(blob_url)) | |
137 return false; | 82 return false; |
| 83 } |
138 IncrementBlobRefCount(uuid); | 84 IncrementBlobRefCount(uuid); |
139 public_blob_urls_[blob_url] = uuid; | |
140 return true; | 85 return true; |
141 } | 86 } |
142 | 87 |
143 void BlobStorageContext::RevokePublicBlobURL(const GURL& blob_url) { | 88 void BlobStorageContext::RevokePublicBlobURL(const GURL& blob_url) { |
144 DCHECK(!BlobUrlHasRef(blob_url)); | 89 std::string uuid; |
145 if (!IsUrlRegistered(blob_url)) | 90 if (!registry_.DeleteURLMapping(blob_url, &uuid)) { |
146 return; | 91 return; |
147 DecrementBlobRefCount(public_blob_urls_[blob_url]); | 92 } |
148 public_blob_urls_.erase(blob_url); | 93 DecrementBlobRefCount(uuid); |
| 94 } |
| 95 |
| 96 void BlobStorageContext::CreatePendingBlob( |
| 97 const std::string& uuid, |
| 98 const std::string& content_type, |
| 99 const std::string& content_disposition) { |
| 100 DCHECK(!registry_.GetEntry(uuid) && !uuid.empty()); |
| 101 registry_.CreateEntry(uuid, content_type, content_disposition); |
| 102 } |
| 103 |
| 104 void BlobStorageContext::CompletePendingBlob( |
| 105 const BlobDataBuilder& external_builder) { |
| 106 BlobRegistryEntry* entry = registry_.GetEntry(external_builder.uuid()); |
| 107 DCHECK(entry); |
| 108 DCHECK(!entry->data.get()) << "Blob already constructed: " |
| 109 << external_builder.uuid(); |
| 110 // We want to handle storing our broken blob as well. |
| 111 switch (entry->state) { |
| 112 case BlobState::PENDING: { |
| 113 entry->data_builder.reset(new InternalBlobData::Builder()); |
| 114 InternalBlobData::Builder* internal_data_builder = |
| 115 entry->data_builder.get(); |
| 116 |
| 117 bool broken = false; |
| 118 for (const auto& blob_item : external_builder.items_) { |
| 119 IPCBlobCreationCancelCode error_code; |
| 120 if (!AppendAllocatedBlobItem(external_builder.uuid_, blob_item, |
| 121 internal_data_builder, &error_code)) { |
| 122 broken = true; |
| 123 memory_usage_ -= entry->data_builder->GetNonsharedMemoryUsage(); |
| 124 entry->state = BlobState::BROKEN; |
| 125 entry->broken_reason = error_code; |
| 126 entry->data_builder.reset(new InternalBlobData::Builder()); |
| 127 break; |
| 128 } |
| 129 } |
| 130 entry->data = entry->data_builder->Build(); |
| 131 entry->data_builder.reset(); |
| 132 entry->state = broken ? BlobState::BROKEN : BlobState::COMPLETE; |
| 133 break; |
| 134 } |
| 135 case BlobState::BROKEN: { |
| 136 InternalBlobData::Builder builder; |
| 137 entry->data = builder.Build(); |
| 138 break; |
| 139 } |
| 140 case BlobState::COMPLETE: |
| 141 DCHECK(false) << "Blob already constructed: " << external_builder.uuid(); |
| 142 return; |
| 143 } |
| 144 |
| 145 UMA_HISTOGRAM_COUNTS("Storage.Blob.ItemCount", entry->data->items().size()); |
| 146 UMA_HISTOGRAM_BOOLEAN("Storage.Blob.Broken", |
| 147 entry->state == BlobState::BROKEN); |
| 148 if (entry->state == BlobState::BROKEN) { |
| 149 UMA_HISTOGRAM_ENUMERATION( |
| 150 "Storage.Blob.BrokenReason", static_cast<int>(entry->broken_reason), |
| 151 (static_cast<int>(IPCBlobCreationCancelCode::LAST) + 1)); |
| 152 } |
| 153 size_t total_memory = 0, nonshared_memory = 0; |
| 154 entry->data->GetMemoryUsage(&total_memory, &nonshared_memory); |
| 155 UMA_HISTOGRAM_COUNTS("Storage.Blob.TotalSize", total_memory / 1024); |
| 156 UMA_HISTOGRAM_COUNTS("Storage.Blob.TotalUnsharedSize", |
| 157 nonshared_memory / 1024); |
| 158 TRACE_COUNTER1("Blob", "MemoryStoreUsageBytes", memory_usage_); |
| 159 |
| 160 auto runner = base::ThreadTaskRunnerHandle::Get(); |
| 161 for (const auto& callback : entry->build_completion_callbacks) { |
| 162 runner->PostTask(FROM_HERE, |
| 163 base::Bind(callback, entry->state == BlobState::COMPLETE)); |
| 164 } |
| 165 entry->build_completion_callbacks.clear(); |
| 166 } |
| 167 |
| 168 void BlobStorageContext::CancelPendingBlob(const std::string& uuid, |
| 169 IPCBlobCreationCancelCode reason) { |
| 170 BlobRegistryEntry* entry = registry_.GetEntry(uuid); |
| 171 DCHECK(entry && entry->state == BlobState::PENDING); |
| 172 entry->state = BlobState::BROKEN; |
| 173 entry->broken_reason = reason; |
| 174 CompletePendingBlob(BlobDataBuilder(uuid)); |
| 175 } |
| 176 |
| 177 void BlobStorageContext::IncrementBlobRefCount(const std::string& uuid) { |
| 178 BlobRegistryEntry* entry = registry_.GetEntry(uuid); |
| 179 DCHECK(entry); |
| 180 ++(entry->refcount); |
| 181 } |
| 182 |
| 183 void BlobStorageContext::DecrementBlobRefCount(const std::string& uuid) { |
| 184 BlobRegistryEntry* entry = registry_.GetEntry(uuid); |
| 185 DCHECK(entry); |
| 186 DCHECK_GT(entry->refcount, 0u); |
| 187 if (--(entry->refcount) == 0) { |
| 188 size_t memory_freeing = 0; |
| 189 if (entry->state == BlobState::COMPLETE) { |
| 190 memory_freeing = entry->data->GetUnsharedMemoryUsage(); |
| 191 entry->data->RemoveBlobFromShareableItems(uuid); |
| 192 } |
| 193 DCHECK_LE(memory_freeing, memory_usage_); |
| 194 memory_usage_ -= memory_freeing; |
| 195 registry_.DeleteEntry(uuid); |
| 196 } |
149 } | 197 } |
150 | 198 |
151 scoped_ptr<BlobDataSnapshot> BlobStorageContext::CreateSnapshot( | 199 scoped_ptr<BlobDataSnapshot> BlobStorageContext::CreateSnapshot( |
152 const std::string& uuid) { | 200 const std::string& uuid) { |
153 scoped_ptr<BlobDataSnapshot> result; | 201 scoped_ptr<BlobDataSnapshot> result; |
154 auto found = blob_map_.find(uuid); | 202 BlobRegistryEntry* entry = registry_.GetEntry(uuid); |
155 DCHECK(found != blob_map_.end()) | 203 if (entry->state != BlobState::COMPLETE) { |
156 << "Blob " << uuid << " should be in map, as the handle is still around"; | 204 return result; |
157 BlobMapEntry* entry = found->second; | 205 } |
158 DCHECK(!entry->IsBeingBuilt()); | 206 |
159 const InternalBlobData& data = *entry->data; | 207 const InternalBlobData& data = *entry->data; |
160 | |
161 scoped_ptr<BlobDataSnapshot> snapshot(new BlobDataSnapshot( | 208 scoped_ptr<BlobDataSnapshot> snapshot(new BlobDataSnapshot( |
162 uuid, data.content_type(), data.content_disposition())); | 209 uuid, entry->content_type, entry->content_disposition)); |
163 snapshot->items_.reserve(data.items().size()); | 210 snapshot->items_.reserve(data.items().size()); |
164 for (const auto& shareable_item : data.items()) { | 211 for (const auto& shareable_item : data.items()) { |
165 snapshot->items_.push_back(shareable_item->item()); | 212 snapshot->items_.push_back(shareable_item->item()); |
166 } | 213 } |
167 return snapshot; | 214 return snapshot; |
168 } | 215 } |
169 | 216 |
170 void BlobStorageContext::StartBuildingBlob(const std::string& uuid) { | 217 bool BlobStorageContext::IsBroken(const std::string& uuid) const { |
171 DCHECK(!IsInUse(uuid) && !uuid.empty()); | 218 const BlobRegistryEntry* entry = registry_.GetEntry(uuid); |
172 blob_map_[uuid] = new BlobMapEntry(1, new InternalBlobData::Builder()); | 219 if (!entry) { |
| 220 return true; |
| 221 } |
| 222 return entry->state == BlobState::BROKEN; |
173 } | 223 } |
174 | 224 |
175 void BlobStorageContext::AppendBlobDataItem( | 225 bool BlobStorageContext::IsBeingBuilt(const std::string& uuid) const { |
176 const std::string& uuid, | 226 const BlobRegistryEntry* entry = registry_.GetEntry(uuid); |
177 const storage::DataElement& ipc_data_element) { | 227 if (!entry) { |
178 TRACE_EVENT0("Blob", "Context::AppendBlobDataItem"); | 228 return false; |
179 DCHECK(IsBeingBuilt(uuid)); | |
180 BlobMap::iterator found = blob_map_.find(uuid); | |
181 if (found == blob_map_.end()) | |
182 return; | |
183 BlobMapEntry* entry = found->second; | |
184 if (entry->flags & EXCEEDED_MEMORY) | |
185 return; | |
186 InternalBlobData::Builder* target_blob_builder = entry->data_builder.get(); | |
187 DCHECK(target_blob_builder); | |
188 | |
189 if (ipc_data_element.type() == DataElement::TYPE_BYTES && | |
190 memory_usage_ + ipc_data_element.length() > kMaxMemoryUsage) { | |
191 BlobEntryExceededMemory(entry); | |
192 return; | |
193 } | 229 } |
194 if (!AppendAllocatedBlobItem(uuid, AllocateBlobItem(uuid, ipc_data_element), | 230 return entry->state == BlobState::PENDING; |
195 target_blob_builder)) { | |
196 BlobEntryExceededMemory(entry); | |
197 } | |
198 } | 231 } |
199 | 232 |
200 void BlobStorageContext::FinishBuildingBlob(const std::string& uuid, | 233 void BlobStorageContext::RunOnConstructionComplete( |
201 const std::string& content_type) { | 234 const std::string& uuid, |
202 DCHECK(IsBeingBuilt(uuid)); | 235 const base::Callback<void(bool)>& done) { |
203 BlobMap::iterator found = blob_map_.find(uuid); | 236 BlobRegistryEntry* entry = registry_.GetEntry(uuid); |
204 if (found == blob_map_.end()) | 237 DCHECK(entry); |
205 return; | 238 switch (entry->state) { |
206 BlobMapEntry* entry = found->second; | 239 case BlobState::COMPLETE: |
207 entry->data_builder->set_content_type(content_type); | 240 done.Run(true); |
208 entry->data = entry->data_builder->Build(); | 241 return; |
209 entry->data_builder.reset(); | 242 case BlobState::BROKEN: |
210 UMA_HISTOGRAM_COUNTS("Storage.Blob.ItemCount", entry->data->items().size()); | 243 done.Run(false); |
211 UMA_HISTOGRAM_BOOLEAN("Storage.Blob.ExceededMemory", | 244 return; |
212 (entry->flags & EXCEEDED_MEMORY) == EXCEEDED_MEMORY); | 245 case BlobState::PENDING: |
213 size_t total_memory = 0, nonshared_memory = 0; | 246 entry->build_completion_callbacks.push_back(done); |
214 entry->data->GetMemoryUsage(&total_memory, &nonshared_memory); | 247 return; |
215 UMA_HISTOGRAM_COUNTS("Storage.Blob.TotalSize", total_memory / 1024); | |
216 UMA_HISTOGRAM_COUNTS("Storage.Blob.TotalUnsharedSize", | |
217 nonshared_memory / 1024); | |
218 TRACE_COUNTER1("Blob", "MemoryStoreUsageBytes", memory_usage_); | |
219 } | |
220 | |
221 void BlobStorageContext::CancelBuildingBlob(const std::string& uuid) { | |
222 DCHECK(IsBeingBuilt(uuid)); | |
223 DecrementBlobRefCount(uuid); | |
224 } | |
225 | |
226 void BlobStorageContext::IncrementBlobRefCount(const std::string& uuid) { | |
227 BlobMap::iterator found = blob_map_.find(uuid); | |
228 if (found == blob_map_.end()) { | |
229 DCHECK(false); | |
230 return; | |
231 } | 248 } |
232 ++(found->second->refcount); | 249 NOTREACHED(); |
233 } | |
234 | |
235 void BlobStorageContext::DecrementBlobRefCount(const std::string& uuid) { | |
236 BlobMap::iterator found = blob_map_.find(uuid); | |
237 if (found == blob_map_.end()) | |
238 return; | |
239 auto* entry = found->second; | |
240 if (--(entry->refcount) == 0) { | |
241 size_t memory_freeing = 0; | |
242 if (entry->IsBeingBuilt()) { | |
243 memory_freeing = entry->data_builder->GetNonsharedMemoryUsage(); | |
244 entry->data_builder->RemoveBlobFromShareableItems(uuid); | |
245 } else { | |
246 memory_freeing = entry->data->GetUnsharedMemoryUsage(); | |
247 entry->data->RemoveBlobFromShareableItems(uuid); | |
248 } | |
249 DCHECK_LE(memory_freeing, memory_usage_); | |
250 memory_usage_ -= memory_freeing; | |
251 delete entry; | |
252 blob_map_.erase(found); | |
253 } | |
254 } | |
255 | |
256 void BlobStorageContext::BlobEntryExceededMemory(BlobMapEntry* entry) { | |
257 // If we're using too much memory, drop this blob's data. | |
258 // TODO(michaeln): Blob memory storage does not yet spill over to disk, | |
259 // as a stop gap, we'll prevent memory usage over a max amount. | |
260 memory_usage_ -= entry->data_builder->GetNonsharedMemoryUsage(); | |
261 entry->flags |= EXCEEDED_MEMORY; | |
262 entry->data_builder.reset(new InternalBlobData::Builder()); | |
263 } | |
264 | |
265 scoped_refptr<BlobDataItem> BlobStorageContext::AllocateBlobItem( | |
266 const std::string& uuid, | |
267 const DataElement& ipc_data) { | |
268 scoped_refptr<BlobDataItem> blob_item; | |
269 | |
270 uint64_t length = ipc_data.length(); | |
271 scoped_ptr<DataElement> element(new DataElement()); | |
272 switch (ipc_data.type()) { | |
273 case DataElement::TYPE_BYTES: | |
274 DCHECK(!ipc_data.offset()); | |
275 element->SetToBytes(ipc_data.bytes(), length); | |
276 blob_item = new BlobDataItem(std::move(element)); | |
277 break; | |
278 case DataElement::TYPE_FILE: | |
279 element->SetToFilePathRange(ipc_data.path(), ipc_data.offset(), length, | |
280 ipc_data.expected_modification_time()); | |
281 blob_item = new BlobDataItem( | |
282 std::move(element), ShareableFileReference::Get(ipc_data.path())); | |
283 break; | |
284 case DataElement::TYPE_FILE_FILESYSTEM: | |
285 element->SetToFileSystemUrlRange(ipc_data.filesystem_url(), | |
286 ipc_data.offset(), length, | |
287 ipc_data.expected_modification_time()); | |
288 blob_item = new BlobDataItem(std::move(element)); | |
289 break; | |
290 case DataElement::TYPE_BLOB: | |
291 // This is a temporary item that will be deconstructed later. | |
292 element->SetToBlobRange(ipc_data.blob_uuid(), ipc_data.offset(), | |
293 ipc_data.length()); | |
294 blob_item = new BlobDataItem(std::move(element)); | |
295 break; | |
296 case DataElement::TYPE_DISK_CACHE_ENTRY: // This type can't be sent by IPC. | |
297 NOTREACHED(); | |
298 break; | |
299 default: | |
300 NOTREACHED(); | |
301 break; | |
302 } | |
303 | |
304 return blob_item; | |
305 } | 250 } |
306 | 251 |
307 bool BlobStorageContext::AppendAllocatedBlobItem( | 252 bool BlobStorageContext::AppendAllocatedBlobItem( |
308 const std::string& target_blob_uuid, | 253 const std::string& target_blob_uuid, |
309 scoped_refptr<BlobDataItem> blob_item, | 254 scoped_refptr<BlobDataItem> blob_item, |
310 InternalBlobData::Builder* target_blob_builder) { | 255 InternalBlobData::Builder* target_blob_builder, |
311 bool exceeded_memory = false; | 256 IPCBlobCreationCancelCode* error_code) { |
| 257 DCHECK(error_code); |
| 258 *error_code = IPCBlobCreationCancelCode::UNKNOWN; |
| 259 bool error = false; |
312 | 260 |
313 // The blob data is stored in the canonical way which only contains a | 261 // The blob data is stored in the canonical way which only contains a |
314 // list of Data, File, and FileSystem items. Aggregated TYPE_BLOB items | 262 // list of Data, File, and FileSystem items. Aggregated TYPE_BLOB items |
315 // are expanded into the primitive constituent types and reused if possible. | 263 // are expanded into the primitive constituent types and reused if possible. |
316 // 1) The Data item is denoted by the raw data and length. | 264 // 1) The Data item is denoted by the raw data and length. |
317 // 2) The File item is denoted by the file path, the range and the expected | 265 // 2) The File item is denoted by the file path, the range and the expected |
318 // modification time. | 266 // modification time. |
319 // 3) The FileSystem File item is denoted by the FileSystem URL, the range | 267 // 3) The FileSystem File item is denoted by the FileSystem URL, the range |
320 // and the expected modification time. | 268 // and the expected modification time. |
321 // 4) The Blob item is denoted by the source blob and an offset and size. | 269 // 4) The Blob item is denoted by the source blob and an offset and size. |
322 // Internal items that are fully used by the new blob (not cut by the | 270 // Internal items that are fully used by the new blob (not cut by the |
323 // offset or size) are shared between the blobs. Otherwise, the relevant | 271 // offset or size) are shared between the blobs. Otherwise, the relevant |
324 // portion of the item is copied. | 272 // portion of the item is copied. |
325 | 273 |
| 274 DCHECK(blob_item->data_element_ptr()); |
326 const DataElement& data_element = blob_item->data_element(); | 275 const DataElement& data_element = blob_item->data_element(); |
327 uint64_t length = data_element.length(); | 276 uint64_t length = data_element.length(); |
328 uint64_t offset = data_element.offset(); | 277 uint64_t offset = data_element.offset(); |
329 UMA_HISTOGRAM_COUNTS("Storage.Blob.StorageSizeBeforeAppend", | 278 UMA_HISTOGRAM_COUNTS("Storage.Blob.StorageSizeBeforeAppend", |
330 memory_usage_ / 1024); | 279 memory_usage_ / 1024); |
331 switch (data_element.type()) { | 280 switch (data_element.type()) { |
332 case DataElement::TYPE_BYTES: | 281 case DataElement::TYPE_BYTES: |
333 UMA_HISTOGRAM_COUNTS("Storage.BlobItemSize.Bytes", length / 1024); | 282 UMA_HISTOGRAM_COUNTS("Storage.BlobItemSize.Bytes", length / 1024); |
334 DCHECK(!offset); | 283 DCHECK(!offset); |
335 if (memory_usage_ + length > kMaxMemoryUsage) { | 284 if (memory_usage_ + length > kBlobStorageMaxMemoryUsage) { |
336 exceeded_memory = true; | 285 error = true; |
| 286 *error_code = IPCBlobCreationCancelCode::OUT_OF_MEMORY; |
337 break; | 287 break; |
338 } | 288 } |
339 memory_usage_ += length; | 289 memory_usage_ += length; |
340 target_blob_builder->AppendSharedBlobItem( | 290 target_blob_builder->AppendSharedBlobItem( |
341 new ShareableBlobDataItem(target_blob_uuid, blob_item)); | 291 new ShareableBlobDataItem(target_blob_uuid, blob_item)); |
342 break; | 292 break; |
343 case DataElement::TYPE_FILE: { | 293 case DataElement::TYPE_FILE: { |
344 bool full_file = (length == std::numeric_limits<uint64_t>::max()); | 294 bool full_file = (length == std::numeric_limits<uint64_t>::max()); |
345 UMA_HISTOGRAM_BOOLEAN("Storage.BlobItemSize.File.Unknown", full_file); | 295 UMA_HISTOGRAM_BOOLEAN("Storage.BlobItemSize.File.Unknown", full_file); |
346 if (!full_file) { | 296 if (!full_file) { |
(...skipping 15 matching lines...) Expand all Loading... |
362 target_blob_builder->AppendSharedBlobItem( | 312 target_blob_builder->AppendSharedBlobItem( |
363 new ShareableBlobDataItem(target_blob_uuid, blob_item)); | 313 new ShareableBlobDataItem(target_blob_uuid, blob_item)); |
364 break; | 314 break; |
365 } | 315 } |
366 case DataElement::TYPE_BLOB: { | 316 case DataElement::TYPE_BLOB: { |
367 UMA_HISTOGRAM_COUNTS("Storage.BlobItemSize.Blob", | 317 UMA_HISTOGRAM_COUNTS("Storage.BlobItemSize.Blob", |
368 (length - offset) / 1024); | 318 (length - offset) / 1024); |
369 // We grab the handle to ensure it stays around while we copy it. | 319 // We grab the handle to ensure it stays around while we copy it. |
370 scoped_ptr<BlobDataHandle> src = | 320 scoped_ptr<BlobDataHandle> src = |
371 GetBlobDataFromUUID(data_element.blob_uuid()); | 321 GetBlobDataFromUUID(data_element.blob_uuid()); |
372 if (src) { | 322 if (!src || src->IsBroken() || src->IsBeingBuilt()) { |
373 BlobMapEntry* other_entry = | 323 error = true; |
374 blob_map_.find(data_element.blob_uuid())->second; | 324 *error_code = IPCBlobCreationCancelCode::REFERENCED_BLOB_BROKEN; |
375 DCHECK(other_entry->data); | 325 break; |
376 exceeded_memory = !AppendBlob(target_blob_uuid, *other_entry->data, | 326 } |
377 offset, length, target_blob_builder); | 327 BlobRegistryEntry* other_entry = |
| 328 registry_.GetEntry(data_element.blob_uuid()); |
| 329 DCHECK(other_entry->data); |
| 330 if (!AppendBlob(target_blob_uuid, *other_entry->data, offset, length, |
| 331 target_blob_builder)) { |
| 332 error = true; |
| 333 *error_code = IPCBlobCreationCancelCode::OUT_OF_MEMORY; |
378 } | 334 } |
379 break; | 335 break; |
380 } | 336 } |
381 case DataElement::TYPE_DISK_CACHE_ENTRY: { | 337 case DataElement::TYPE_DISK_CACHE_ENTRY: { |
382 UMA_HISTOGRAM_COUNTS("Storage.BlobItemSize.CacheEntry", | 338 UMA_HISTOGRAM_COUNTS("Storage.BlobItemSize.CacheEntry", |
383 (length - offset) / 1024); | 339 (length - offset) / 1024); |
384 target_blob_builder->AppendSharedBlobItem( | 340 target_blob_builder->AppendSharedBlobItem( |
385 new ShareableBlobDataItem(target_blob_uuid, blob_item)); | 341 new ShareableBlobDataItem(target_blob_uuid, blob_item)); |
386 break; | 342 break; |
387 } | 343 } |
388 default: | 344 case DataElement::TYPE_BYTES_DESCRIPTION: |
| 345 case DataElement::TYPE_UNKNOWN: |
389 NOTREACHED(); | 346 NOTREACHED(); |
390 break; | 347 break; |
391 } | 348 } |
392 UMA_HISTOGRAM_COUNTS("Storage.Blob.StorageSizeAfterAppend", | 349 UMA_HISTOGRAM_COUNTS("Storage.Blob.StorageSizeAfterAppend", |
393 memory_usage_ / 1024); | 350 memory_usage_ / 1024); |
394 | 351 return !error; |
395 return !exceeded_memory; | |
396 } | 352 } |
397 | 353 |
398 bool BlobStorageContext::AppendBlob( | 354 bool BlobStorageContext::AppendBlob( |
399 const std::string& target_blob_uuid, | 355 const std::string& target_blob_uuid, |
400 const InternalBlobData& blob, | 356 const InternalBlobData& blob, |
401 uint64_t offset, | 357 uint64_t offset, |
402 uint64_t length, | 358 uint64_t length, |
403 InternalBlobData::Builder* target_blob_builder) { | 359 InternalBlobData::Builder* target_blob_builder) { |
404 DCHECK(length > 0); | 360 DCHECK_GT(length, 0ull); |
405 | 361 |
406 const std::vector<scoped_refptr<ShareableBlobDataItem>>& items = blob.items(); | 362 const std::vector<scoped_refptr<ShareableBlobDataItem>>& items = blob.items(); |
407 auto iter = items.begin(); | 363 auto iter = items.begin(); |
408 if (offset) { | 364 if (offset) { |
409 for (; iter != items.end(); ++iter) { | 365 for (; iter != items.end(); ++iter) { |
410 const BlobDataItem& item = *(iter->get()->item()); | 366 const BlobDataItem& item = *(iter->get()->item()); |
411 if (offset >= item.length()) | 367 if (offset >= item.length()) |
412 offset -= item.length(); | 368 offset -= item.length(); |
413 else | 369 else |
414 break; | 370 break; |
(...skipping 16 matching lines...) Expand all Loading... |
431 length -= new_length; | 387 length -= new_length; |
432 continue; | 388 continue; |
433 } | 389 } |
434 | 390 |
435 // We need to do copying of the items when we have a different offset or | 391 // We need to do copying of the items when we have a different offset or |
436 // length | 392 // length |
437 switch (item.type()) { | 393 switch (item.type()) { |
438 case DataElement::TYPE_BYTES: { | 394 case DataElement::TYPE_BYTES: { |
439 UMA_HISTOGRAM_COUNTS("Storage.BlobItemSize.BlobSlice.Bytes", | 395 UMA_HISTOGRAM_COUNTS("Storage.BlobItemSize.BlobSlice.Bytes", |
440 new_length / 1024); | 396 new_length / 1024); |
441 if (memory_usage_ + new_length > kMaxMemoryUsage) { | 397 if (memory_usage_ + new_length > kBlobStorageMaxMemoryUsage) { |
442 return false; | 398 return false; |
443 } | 399 } |
444 DCHECK(!item.offset()); | 400 DCHECK(!item.offset()); |
445 scoped_ptr<DataElement> element(new DataElement()); | 401 scoped_ptr<DataElement> element(new DataElement()); |
446 element->SetToBytes(item.bytes() + offset, | 402 element->SetToBytes(item.bytes() + offset, |
447 static_cast<int64_t>(new_length)); | 403 static_cast<int64_t>(new_length)); |
448 memory_usage_ += new_length; | 404 memory_usage_ += new_length; |
449 target_blob_builder->AppendSharedBlobItem(new ShareableBlobDataItem( | 405 target_blob_builder->AppendSharedBlobItem(new ShareableBlobDataItem( |
450 target_blob_uuid, new BlobDataItem(std::move(element)))); | 406 target_blob_uuid, new BlobDataItem(std::move(element)))); |
451 } break; | 407 } break; |
(...skipping 23 matching lines...) Expand all Loading... |
475 case DataElement::TYPE_DISK_CACHE_ENTRY: { | 431 case DataElement::TYPE_DISK_CACHE_ENTRY: { |
476 scoped_ptr<DataElement> element(new DataElement()); | 432 scoped_ptr<DataElement> element(new DataElement()); |
477 element->SetToDiskCacheEntryRange(item.offset() + offset, | 433 element->SetToDiskCacheEntryRange(item.offset() + offset, |
478 new_length); | 434 new_length); |
479 target_blob_builder->AppendSharedBlobItem(new ShareableBlobDataItem( | 435 target_blob_builder->AppendSharedBlobItem(new ShareableBlobDataItem( |
480 target_blob_uuid, | 436 target_blob_uuid, |
481 new BlobDataItem(std::move(element), item.data_handle_, | 437 new BlobDataItem(std::move(element), item.data_handle_, |
482 item.disk_cache_entry(), | 438 item.disk_cache_entry(), |
483 item.disk_cache_stream_index()))); | 439 item.disk_cache_stream_index()))); |
484 } break; | 440 } break; |
485 default: | 441 case DataElement::TYPE_BYTES_DESCRIPTION: |
| 442 case DataElement::TYPE_BLOB: |
| 443 case DataElement::TYPE_UNKNOWN: |
486 CHECK(false) << "Illegal blob item type: " << item.type(); | 444 CHECK(false) << "Illegal blob item type: " << item.type(); |
487 } | 445 } |
488 length -= new_length; | 446 length -= new_length; |
489 offset = 0; | 447 offset = 0; |
490 } | 448 } |
491 return true; | 449 return true; |
492 } | 450 } |
493 | 451 |
494 bool BlobStorageContext::IsInUse(const std::string& uuid) { | |
495 return blob_map_.find(uuid) != blob_map_.end(); | |
496 } | |
497 | |
498 bool BlobStorageContext::IsBeingBuilt(const std::string& uuid) { | |
499 BlobMap::iterator found = blob_map_.find(uuid); | |
500 if (found == blob_map_.end()) | |
501 return false; | |
502 return found->second->IsBeingBuilt(); | |
503 } | |
504 | |
505 bool BlobStorageContext::IsUrlRegistered(const GURL& blob_url) { | |
506 return public_blob_urls_.find(blob_url) != public_blob_urls_.end(); | |
507 } | |
508 | |
509 } // namespace storage | 452 } // namespace storage |
OLD | NEW |