Chromium Code Reviews| 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 <algorithm> | |
| 8 | |
| 7 #include "base/bind.h" | 9 #include "base/bind.h" |
| 8 #include "base/location.h" | 10 #include "base/location.h" |
| 9 #include "base/logging.h" | 11 #include "base/logging.h" |
| 12 #include "base/memory/scoped_ptr.h" | |
| 10 #include "base/message_loop/message_loop_proxy.h" | 13 #include "base/message_loop/message_loop_proxy.h" |
| 11 #include "base/metrics/histogram.h" | 14 #include "base/metrics/histogram.h" |
| 12 #include "base/stl_util.h" | 15 #include "base/stl_util.h" |
| 13 #include "storage/browser/blob/blob_data_builder.h" | 16 #include "storage/browser/blob/blob_data_builder.h" |
| 17 #include "storage/browser/blob/blob_data_handle.h" | |
| 14 #include "url/gurl.h" | 18 #include "url/gurl.h" |
| 15 | 19 |
| 16 namespace storage { | 20 namespace storage { |
| 17 | 21 |
| 18 namespace { | 22 namespace { |
| 19 | 23 |
| 20 // We can't use GURL directly for these hash fragment manipulations | 24 // We can't use GURL directly for these hash fragment manipulations |
| 21 // since it doesn't have specific knowlege of the BlobURL format. GURL | 25 // since it doesn't have specific knowlege of the BlobURL format. GURL |
| 22 // treats BlobURLs as if they were PathURLs which don't support hash | 26 // treats BlobURLs as if they were PathURLs which don't support hash |
| 23 // fragments. | 27 // fragments. |
| 24 | 28 |
| 25 bool BlobUrlHasRef(const GURL& url) { | 29 bool BlobUrlHasRef(const GURL& url) { |
| 26 return url.spec().find('#') != std::string::npos; | 30 return url.spec().find('#') != std::string::npos; |
| 27 } | 31 } |
| 28 | 32 |
| 29 GURL ClearBlobUrlRef(const GURL& url) { | 33 GURL ClearBlobUrlRef(const GURL& url) { |
| 30 size_t hash_pos = url.spec().find('#'); | 34 size_t hash_pos = url.spec().find('#'); |
| 31 if (hash_pos == std::string::npos) | 35 if (hash_pos == std::string::npos) |
| 32 return url; | 36 return url; |
| 33 return GURL(url.spec().substr(0, hash_pos)); | 37 return GURL(url.spec().substr(0, hash_pos)); |
| 34 } | 38 } |
| 35 | 39 |
| 36 // TODO(michaeln): use base::SysInfo::AmountOfPhysicalMemoryMB() in some | 40 // TODO(michaeln): use base::SysInfo::AmountOfPhysicalMemoryMB() in some |
| 37 // way to come up with a better limit. | 41 // way to come up with a better limit. |
| 38 static const int64 kMaxMemoryUsage = 500 * 1024 * 1024; // Half a gig. | 42 static const int64 kMaxMemoryUsage = 500 * 1024 * 1024; // Half a gig. |
| 39 | 43 |
| 40 } // namespace | 44 } // namespace |
| 41 | 45 |
| 42 BlobStorageContext::BlobMapEntry::BlobMapEntry() | 46 BlobStorageContext::BlobMapEntry::BlobMapEntry() : refcount(0), flags(0) { |
| 43 : refcount(0), flags(0) { | |
| 44 } | 47 } |
| 45 | 48 |
| 46 BlobStorageContext::BlobMapEntry::BlobMapEntry(int refcount, | 49 BlobStorageContext::BlobMapEntry::BlobMapEntry(int refcount, |
| 47 BlobDataBuilder* data) | 50 InternalBlobData::Builder* data) |
| 48 : refcount(refcount), flags(0), data_builder(data) { | 51 : refcount(refcount), flags(0), data_builder(data) { |
| 49 } | 52 } |
| 50 | 53 |
| 51 BlobStorageContext::BlobMapEntry::~BlobMapEntry() { | 54 BlobStorageContext::BlobMapEntry::~BlobMapEntry() { |
| 52 } | 55 } |
| 53 | 56 |
| 54 bool BlobStorageContext::BlobMapEntry::IsBeingBuilt() { | 57 bool BlobStorageContext::BlobMapEntry::IsBeingBuilt() { |
| 55 return data_builder; | 58 return data_builder; |
| 56 } | 59 } |
| 57 | 60 |
| 58 BlobStorageContext::BlobStorageContext() | 61 BlobStorageContext::BlobStorageContext() : memory_usage_(0) { |
| 59 : memory_usage_(0) { | |
| 60 } | 62 } |
| 61 | 63 |
| 62 BlobStorageContext::~BlobStorageContext() { | 64 BlobStorageContext::~BlobStorageContext() { |
| 63 STLDeleteContainerPairSecondPointers(blob_map_.begin(), blob_map_.end()); | 65 STLDeleteContainerPairSecondPointers(blob_map_.begin(), blob_map_.end()); |
| 64 } | 66 } |
| 65 | 67 |
| 66 scoped_ptr<BlobDataHandle> BlobStorageContext::GetBlobDataFromUUID( | 68 scoped_ptr<BlobDataHandle> BlobStorageContext::GetBlobDataFromUUID( |
| 67 const std::string& uuid) { | 69 const std::string& uuid) { |
| 68 scoped_ptr<BlobDataHandle> result; | 70 scoped_ptr<BlobDataHandle> result; |
| 69 BlobMap::iterator found = blob_map_.find(uuid); | 71 BlobMap::iterator found = blob_map_.find(uuid); |
| 70 if (found == blob_map_.end()) | 72 if (found == blob_map_.end()) |
| 71 return result.Pass(); | 73 return result.Pass(); |
| 72 auto* entry = found->second; | 74 auto* entry = found->second; |
| 73 if (entry->flags & EXCEEDED_MEMORY) | 75 if (entry->flags & EXCEEDED_MEMORY) |
| 74 return result.Pass(); | 76 return result.Pass(); |
| 75 DCHECK(!entry->IsBeingBuilt()); | 77 DCHECK(!entry->IsBeingBuilt()); |
| 76 result.reset( | 78 result.reset( |
| 77 new BlobDataHandle(uuid, this, base::MessageLoopProxy::current().get())); | 79 new BlobDataHandle(uuid, this, base::MessageLoopProxy::current().get())); |
| 78 return result.Pass(); | 80 return result.Pass(); |
| 79 } | 81 } |
| 80 | 82 |
| 81 scoped_ptr<BlobDataHandle> BlobStorageContext::GetBlobDataFromPublicURL( | 83 scoped_ptr<BlobDataHandle> BlobStorageContext::GetBlobDataFromPublicURL( |
| 82 const GURL& url) { | 84 const GURL& url) { |
| 83 BlobURLMap::iterator found = public_blob_urls_.find( | 85 BlobURLMap::iterator found = |
| 84 BlobUrlHasRef(url) ? ClearBlobUrlRef(url) : url); | 86 public_blob_urls_.find(BlobUrlHasRef(url) ? ClearBlobUrlRef(url) : url); |
| 85 if (found == public_blob_urls_.end()) | 87 if (found == public_blob_urls_.end()) |
| 86 return scoped_ptr<BlobDataHandle>(); | 88 return scoped_ptr<BlobDataHandle>(); |
| 87 return GetBlobDataFromUUID(found->second); | 89 return GetBlobDataFromUUID(found->second); |
| 88 } | 90 } |
| 89 | 91 |
| 90 scoped_ptr<BlobDataHandle> BlobStorageContext::AddFinishedBlob( | 92 scoped_ptr<BlobDataHandle> BlobStorageContext::AddFinishedBlob( |
| 91 const BlobDataBuilder& data) { | 93 BlobDataBuilder* builder) { |
| 92 StartBuildingBlob(data.uuid_); | 94 StartBuildingBlob(builder->uuid_); |
| 93 for (const auto& blob_item : data.items_) | 95 BlobMap::iterator found = blob_map_.find(builder->uuid_); |
| 94 AppendBlobDataItem(data.uuid_, *(blob_item->item_)); | 96 DCHECK(found != blob_map_.end()); |
| 95 FinishBuildingBlob(data.uuid_, data.content_type_); | 97 BlobMapEntry* entry = found->second; |
| 96 scoped_ptr<BlobDataHandle> handle = GetBlobDataFromUUID(data.uuid_); | 98 InternalBlobData::Builder* target_blob_builder = entry->data_builder.get(); |
| 97 DecrementBlobRefCount(data.uuid_); | 99 DCHECK(target_blob_builder); |
| 100 | |
| 101 for (const auto& blob_item : builder->items_) { | |
| 102 if (entry->flags & EXCEEDED_MEMORY) | |
|
michaeln
2015/02/05 22:58:47
not sure this test is needed since StartBuildingBl
dmurph
2015/02/06 01:32:30
True, removed.
| |
| 103 break; | |
| 104 if (!AppendAllocatedBlobItem(builder->uuid_, target_blob_builder, | |
| 105 blob_item)) { | |
| 106 BlobEntryExceededMemory(entry); | |
| 107 break; | |
| 108 } | |
| 109 } | |
| 110 | |
| 111 FinishBuildingBlob(builder->uuid_, builder->content_type_); | |
| 112 scoped_ptr<BlobDataHandle> handle = GetBlobDataFromUUID(builder->uuid_); | |
| 113 DecrementBlobRefCount(builder->uuid_); | |
| 98 return handle.Pass(); | 114 return handle.Pass(); |
| 99 } | 115 } |
| 100 | 116 |
| 101 bool BlobStorageContext::RegisterPublicBlobURL( | 117 bool BlobStorageContext::RegisterPublicBlobURL(const GURL& blob_url, |
| 102 const GURL& blob_url, const std::string& uuid) { | 118 const std::string& uuid) { |
| 103 DCHECK(!BlobUrlHasRef(blob_url)); | 119 DCHECK(!BlobUrlHasRef(blob_url)); |
| 104 DCHECK(IsInUse(uuid)); | 120 DCHECK(IsInUse(uuid)); |
| 105 DCHECK(!IsUrlRegistered(blob_url)); | 121 DCHECK(!IsUrlRegistered(blob_url)); |
| 106 if (!IsInUse(uuid) || IsUrlRegistered(blob_url)) | 122 if (!IsInUse(uuid) || IsUrlRegistered(blob_url)) |
| 107 return false; | 123 return false; |
| 108 IncrementBlobRefCount(uuid); | 124 IncrementBlobRefCount(uuid); |
| 109 public_blob_urls_[blob_url] = uuid; | 125 public_blob_urls_[blob_url] = uuid; |
| 110 return true; | 126 return true; |
| 111 } | 127 } |
| 112 | 128 |
| 113 void BlobStorageContext::RevokePublicBlobURL(const GURL& blob_url) { | 129 void BlobStorageContext::RevokePublicBlobURL(const GURL& blob_url) { |
| 114 DCHECK(!BlobUrlHasRef(blob_url)); | 130 DCHECK(!BlobUrlHasRef(blob_url)); |
| 115 if (!IsUrlRegistered(blob_url)) | 131 if (!IsUrlRegistered(blob_url)) |
| 116 return; | 132 return; |
| 117 DecrementBlobRefCount(public_blob_urls_[blob_url]); | 133 DecrementBlobRefCount(public_blob_urls_[blob_url]); |
| 118 public_blob_urls_.erase(blob_url); | 134 public_blob_urls_.erase(blob_url); |
| 119 } | 135 } |
| 120 | 136 |
| 121 scoped_ptr<BlobDataSnapshot> BlobStorageContext::CreateSnapshot( | 137 scoped_ptr<BlobDataSnapshot> BlobStorageContext::CreateSnapshot( |
| 122 const std::string& uuid) { | 138 const std::string& uuid) { |
| 123 scoped_ptr<BlobDataSnapshot> result; | 139 scoped_ptr<BlobDataSnapshot> result; |
| 124 auto found = blob_map_.find(uuid); | 140 auto found = blob_map_.find(uuid); |
| 125 DCHECK(found != blob_map_.end()) | 141 DCHECK(found != blob_map_.end()) |
| 126 << "Blob should be in map, as the handle is still around"; | 142 << "Blob " << uuid << " should be in map, as the handle is still around"; |
| 127 BlobMapEntry* entry = found->second; | 143 BlobMapEntry* entry = found->second; |
| 128 DCHECK(!entry->IsBeingBuilt()); | 144 DCHECK(!entry->IsBeingBuilt()); |
| 129 result.reset(new BlobDataSnapshot(*entry->data)); | 145 const InternalBlobData& data = *entry->data; |
| 130 return result.Pass(); | 146 return CreateSnapshot(uuid, data); |
| 131 } | 147 } |
| 132 | 148 |
| 133 void BlobStorageContext::StartBuildingBlob(const std::string& uuid) { | 149 void BlobStorageContext::StartBuildingBlob(const std::string& uuid) { |
| 134 DCHECK(!IsInUse(uuid) && !uuid.empty()); | 150 DCHECK(!IsInUse(uuid) && !uuid.empty()); |
| 135 blob_map_[uuid] = new BlobMapEntry(1, new BlobDataBuilder(uuid)); | 151 blob_map_[uuid] = new BlobMapEntry(1, new InternalBlobData::Builder()); |
| 136 } | 152 } |
| 137 | 153 |
| 138 void BlobStorageContext::AppendBlobDataItem(const std::string& uuid, | 154 void BlobStorageContext::AppendBlobDataItem( |
| 139 const DataElement& item) { | 155 const std::string& uuid, |
| 156 const storage::DataElement& data_element) { | |
| 140 DCHECK(IsBeingBuilt(uuid)); | 157 DCHECK(IsBeingBuilt(uuid)); |
| 141 BlobMap::iterator found = blob_map_.find(uuid); | 158 BlobMap::iterator found = blob_map_.find(uuid); |
| 142 if (found == blob_map_.end()) | 159 if (found == blob_map_.end()) |
| 143 return; | 160 return; |
| 144 BlobMapEntry* entry = found->second; | 161 BlobMapEntry* entry = found->second; |
| 145 if (entry->flags & EXCEEDED_MEMORY) | 162 if (entry->flags & EXCEEDED_MEMORY) |
| 146 return; | 163 return; |
| 147 BlobDataBuilder* target_blob_data = entry->data_builder.get(); | 164 InternalBlobData::Builder* target_blob_builder = entry->data_builder.get(); |
| 148 DCHECK(target_blob_data); | 165 DCHECK(target_blob_builder); |
| 149 | 166 |
| 150 bool exceeded_memory = false; | 167 if (!CanFitDataElement(data_element)) { |
|
michaeln
2015/02/05 22:58:47
it might make sense to hoist the one line body of
dmurph
2015/02/06 01:32:30
Done.
| |
| 151 | 168 BlobEntryExceededMemory(entry); |
| 152 // The blob data is stored in the canonical way which only contains a | 169 return; |
| 153 // list of Data, File, and FileSystem items. Aggregated TYPE_BLOB items | |
| 154 // are expanded into the primitive constituent types. | |
| 155 // 1) The Data item is denoted by the raw data and length. | |
| 156 // 2) The File item is denoted by the file path, the range and the expected | |
| 157 // modification time. | |
| 158 // 3) The FileSystem File item is denoted by the FileSystem URL, the range | |
| 159 // and the expected modification time. | |
| 160 // 4) The Blob items are expanded. | |
| 161 // TODO(michaeln): Would be nice to avoid copying Data items when expanding. | |
| 162 | |
| 163 uint64 length = item.length(); | |
| 164 DCHECK_GT(length, 0u); | |
| 165 UMA_HISTOGRAM_COUNTS("Storage.Blob.StorageSizeBeforeAppend", | |
| 166 memory_usage_ / 1024); | |
| 167 switch (item.type()) { | |
| 168 case DataElement::TYPE_BYTES: | |
| 169 UMA_HISTOGRAM_COUNTS("Storage.BlobItemSize.Bytes", length / 1024); | |
| 170 DCHECK(!item.offset()); | |
| 171 exceeded_memory = !AppendBytesItem(target_blob_data, item.bytes(), | |
| 172 static_cast<int64>(length)); | |
| 173 break; | |
| 174 case DataElement::TYPE_FILE: | |
| 175 UMA_HISTOGRAM_COUNTS("Storage.BlobItemSize.File", length / 1024); | |
| 176 AppendFileItem(target_blob_data, item.path(), item.offset(), | |
| 177 item.length(), item.expected_modification_time()); | |
| 178 break; | |
| 179 case DataElement::TYPE_FILE_FILESYSTEM: | |
| 180 UMA_HISTOGRAM_COUNTS("Storage.BlobItemSize.FileSystem", length / 1024); | |
| 181 AppendFileSystemFileItem(target_blob_data, item.filesystem_url(), | |
| 182 item.offset(), item.length(), | |
| 183 item.expected_modification_time()); | |
| 184 break; | |
| 185 case DataElement::TYPE_BLOB: { | |
| 186 UMA_HISTOGRAM_COUNTS("Storage.BlobItemSize.Blob", length / 1024); | |
| 187 // We grab the handle to ensure it stays around while we copy it. | |
| 188 scoped_ptr<BlobDataHandle> src = GetBlobDataFromUUID(item.blob_uuid()); | |
| 189 if (src) { | |
| 190 BlobMapEntry* entry = blob_map_.find(item.blob_uuid())->second; | |
| 191 DCHECK(entry->data); | |
| 192 exceeded_memory = !ExpandStorageItems(target_blob_data, *entry->data, | |
| 193 item.offset(), item.length()); | |
| 194 } | |
| 195 break; | |
| 196 } | |
| 197 default: | |
| 198 NOTREACHED(); | |
| 199 break; | |
| 200 } | 170 } |
| 201 UMA_HISTOGRAM_COUNTS("Storage.Blob.StorageSizeAfterAppend", | 171 scoped_refptr<BlobDataItem> data_item = |
| 202 memory_usage_ / 1024); | 172 TransformDataElement(uuid, data_element); |
|
michaeln
2015/02/05 22:58:47
maybe rename this method to AllocateBlobItem(id, e
dmurph
2015/02/06 01:32:30
Done.
| |
| 203 | 173 if (!AppendAllocatedBlobItem(uuid, target_blob_builder, data_item)) { |
| 204 // If we're using too much memory, drop this blob's data. | 174 BlobEntryExceededMemory(entry); |
| 205 // TODO(michaeln): Blob memory storage does not yet spill over to disk, | |
| 206 // as a stop gap, we'll prevent memory usage over a max amount. | |
| 207 if (exceeded_memory) { | |
| 208 memory_usage_ -= target_blob_data->GetMemoryUsage(); | |
| 209 entry->flags |= EXCEEDED_MEMORY; | |
| 210 entry->data_builder.reset(new BlobDataBuilder(uuid)); | |
| 211 return; | |
| 212 } | 175 } |
| 213 } | 176 } |
| 214 | 177 |
| 215 void BlobStorageContext::FinishBuildingBlob( | 178 void BlobStorageContext::FinishBuildingBlob(const std::string& uuid, |
| 216 const std::string& uuid, const std::string& content_type) { | 179 const std::string& content_type) { |
| 217 DCHECK(IsBeingBuilt(uuid)); | 180 DCHECK(IsBeingBuilt(uuid)); |
| 218 BlobMap::iterator found = blob_map_.find(uuid); | 181 BlobMap::iterator found = blob_map_.find(uuid); |
| 219 if (found == blob_map_.end()) | 182 if (found == blob_map_.end()) |
| 220 return; | 183 return; |
| 221 BlobMapEntry* entry = found->second; | 184 BlobMapEntry* entry = found->second; |
| 222 entry->data_builder->set_content_type(content_type); | 185 entry->data_builder->set_content_type(content_type); |
| 223 entry->data = entry->data_builder->BuildSnapshot().Pass(); | 186 scoped_ptr<InternalBlobData::Builder> builder(entry->data_builder.release()); |
| 224 entry->data_builder.reset(); | 187 entry->data.reset(new InternalBlobData(builder.Pass())); |
| 225 UMA_HISTOGRAM_COUNTS("Storage.Blob.ItemCount", entry->data->items().size()); | 188 UMA_HISTOGRAM_COUNTS("Storage.Blob.ItemCount", entry->data->items().size()); |
| 226 UMA_HISTOGRAM_BOOLEAN("Storage.Blob.ExceededMemory", | 189 UMA_HISTOGRAM_BOOLEAN("Storage.Blob.ExceededMemory", |
| 227 (entry->flags & EXCEEDED_MEMORY) == EXCEEDED_MEMORY); | 190 (entry->flags & EXCEEDED_MEMORY) == EXCEEDED_MEMORY); |
| 228 } | 191 } |
| 229 | 192 |
| 230 void BlobStorageContext::CancelBuildingBlob(const std::string& uuid) { | 193 void BlobStorageContext::CancelBuildingBlob(const std::string& uuid) { |
| 231 DCHECK(IsBeingBuilt(uuid)); | 194 DCHECK(IsBeingBuilt(uuid)); |
| 232 DecrementBlobRefCount(uuid); | 195 DecrementBlobRefCount(uuid); |
| 233 } | 196 } |
| 234 | 197 |
| 235 void BlobStorageContext::IncrementBlobRefCount(const std::string& uuid) { | 198 void BlobStorageContext::IncrementBlobRefCount(const std::string& uuid) { |
| 236 BlobMap::iterator found = blob_map_.find(uuid); | 199 BlobMap::iterator found = blob_map_.find(uuid); |
| 237 if (found == blob_map_.end()) { | 200 if (found == blob_map_.end()) { |
| 238 DCHECK(false); | 201 DCHECK(false); |
| 239 return; | 202 return; |
| 240 } | 203 } |
| 241 ++(found->second->refcount); | 204 ++(found->second->refcount); |
| 242 } | 205 } |
| 243 | 206 |
| 244 void BlobStorageContext::DecrementBlobRefCount(const std::string& uuid) { | 207 void BlobStorageContext::DecrementBlobRefCount(const std::string& uuid) { |
| 245 BlobMap::iterator found = blob_map_.find(uuid); | 208 BlobMap::iterator found = blob_map_.find(uuid); |
| 246 if (found == blob_map_.end()) | 209 if (found == blob_map_.end()) |
| 247 return; | 210 return; |
| 248 auto* entry = found->second; | 211 auto* entry = found->second; |
| 249 if (--(entry->refcount) == 0) { | 212 if (--(entry->refcount) == 0) { |
| 213 size_t memory_freeing = 0; | |
| 250 if (entry->IsBeingBuilt()) { | 214 if (entry->IsBeingBuilt()) { |
| 251 memory_usage_ -= entry->data_builder->GetMemoryUsage(); | 215 memory_freeing = entry->data_builder->GetNonsharedMemoryUsage(); |
| 216 entry->data_builder->RemoveBlobFromShareableItems(uuid); | |
| 252 } else { | 217 } else { |
| 253 memory_usage_ -= entry->data->GetMemoryUsage(); | 218 memory_freeing = entry->data->GetNonsharedMemoryUsage(); |
| 219 entry->data->RemoveBlobFromShareableItems(uuid); | |
| 254 } | 220 } |
| 221 DCHECK_LE(memory_freeing, memory_usage_); | |
| 222 memory_usage_ -= memory_freeing; | |
| 255 delete entry; | 223 delete entry; |
| 256 blob_map_.erase(found); | 224 blob_map_.erase(found); |
| 257 } | 225 } |
| 258 } | 226 } |
| 259 | 227 |
| 260 bool BlobStorageContext::ExpandStorageItems( | 228 bool BlobStorageContext::CanFitDataElement(const DataElement& item) { |
| 261 BlobDataBuilder* target_blob_data, | 229 return item.type() == DataElement::TYPE_BYTES && |
| 262 const BlobDataSnapshot& src_blob_data, | 230 memory_usage_ + item.length() <= kMaxMemoryUsage; |
| 263 uint64 offset, | 231 } |
| 264 uint64 length) { | |
| 265 DCHECK(target_blob_data && length != static_cast<uint64>(-1)); | |
| 266 | 232 |
| 267 const std::vector<scoped_refptr<BlobDataItem>>& items = src_blob_data.items(); | 233 void BlobStorageContext::BlobEntryExceededMemory(BlobMapEntry* entry) { |
| 234 // If we're using too much memory, drop this blob's data. | |
| 235 // TODO(michaeln): Blob memory storage does not yet spill over to disk, | |
| 236 // as a stop gap, we'll prevent memory usage over a max amount. | |
| 237 memory_usage_ -= entry->data_builder->GetNonsharedMemoryUsage(); | |
| 238 entry->flags |= EXCEEDED_MEMORY; | |
| 239 entry->data_builder.reset(new InternalBlobData::Builder()); | |
| 240 } | |
| 241 | |
| 242 scoped_refptr<BlobDataItem> BlobStorageContext::TransformDataElement( | |
| 243 const std::string& uuid, | |
| 244 const DataElement& item) { | |
| 245 scoped_refptr<BlobDataItem> blob_item; | |
| 246 | |
| 247 size_t length = item.length(); | |
| 248 DCHECK_GT(length, 0u); | |
| 249 scoped_ptr<DataElement> element(new DataElement()); | |
| 250 switch (item.type()) { | |
| 251 case DataElement::TYPE_BYTES: | |
| 252 DCHECK(!item.offset()); | |
| 253 element->SetToBytes(item.bytes(), length); | |
| 254 blob_item = new BlobDataItem(element.Pass()); | |
| 255 break; | |
| 256 case DataElement::TYPE_FILE: | |
| 257 element->SetToFilePathRange(item.path(), item.offset(), length, | |
| 258 item.expected_modification_time()); | |
| 259 blob_item = new BlobDataItem(element.Pass(), | |
| 260 ShareableFileReference::Get(item.path())); | |
| 261 break; | |
| 262 case DataElement::TYPE_FILE_FILESYSTEM: | |
| 263 element->SetToFileSystemUrlRange(item.filesystem_url(), item.offset(), | |
| 264 length, | |
| 265 item.expected_modification_time()); | |
| 266 blob_item = new BlobDataItem(element.Pass()); | |
| 267 break; | |
| 268 case DataElement::TYPE_BLOB: | |
| 269 // This is a temporary item that will be deconstructed later. | |
| 270 element->SetToBlobRange(item.blob_uuid(), item.offset(), item.length()); | |
| 271 blob_item = new BlobDataItem(element.Pass()); | |
| 272 break; | |
| 273 default: | |
| 274 NOTREACHED(); | |
| 275 break; | |
| 276 } | |
| 277 | |
| 278 return blob_item; | |
| 279 } | |
| 280 | |
| 281 bool BlobStorageContext::AppendAllocatedBlobItem( | |
| 282 const std::string& target_blob_uuid, | |
| 283 InternalBlobData::Builder* target_blob_builder, | |
| 284 scoped_refptr<BlobDataItem> blob_item) { | |
| 285 bool exceeded_memory = false; | |
| 286 | |
| 287 // The blob data is stored in the canonical way which only contains a | |
| 288 // list of Data, File, and FileSystem items. Aggregated TYPE_BLOB items | |
| 289 // are expanded into the primitive constituent types. | |
| 290 // 1) The Data item is denoted by the raw data and length. | |
| 291 // 2) The File item is denoted by the file path, the range and the expected | |
| 292 // modification time. | |
| 293 // 3) The FileSystem File item is denoted by the FileSystem URL, the range | |
| 294 // and the expected modification time. | |
| 295 // 4) The Blob items are expanded. | |
| 296 | |
| 297 const DataElement& data_element = blob_item->data_element(); | |
| 298 size_t length = data_element.length(); | |
| 299 size_t offset = data_element.offset(); | |
|
michaeln
2015/02/05 22:58:47
these can be len/offsets into files so int64 as th
dmurph
2015/02/06 01:32:30
Done
| |
| 300 DCHECK_GT(length, 0u); | |
| 301 UMA_HISTOGRAM_COUNTS("Storage.Blob.StorageSizeBeforeAppend", | |
| 302 memory_usage_ / 1024); | |
| 303 switch (data_element.type()) { | |
| 304 case DataElement::TYPE_BYTES: | |
| 305 UMA_HISTOGRAM_COUNTS("Storage.BlobItemSize.Bytes", length / 1024); | |
| 306 DCHECK(!offset); | |
| 307 if (memory_usage_ + length > kMaxMemoryUsage) { | |
| 308 exceeded_memory = true; | |
| 309 break; | |
| 310 } | |
| 311 memory_usage_ += length; | |
| 312 target_blob_builder->AppendSharedBlobItem( | |
| 313 new ShareableBlobDataItem(target_blob_uuid, blob_item)); | |
| 314 break; | |
| 315 case DataElement::TYPE_FILE: | |
| 316 UMA_HISTOGRAM_COUNTS("Storage.BlobItemSize.File", | |
| 317 (length - offset) / 1024); | |
| 318 target_blob_builder->AppendSharedBlobItem( | |
| 319 new ShareableBlobDataItem(target_blob_uuid, blob_item)); | |
| 320 break; | |
| 321 case DataElement::TYPE_FILE_FILESYSTEM: | |
| 322 UMA_HISTOGRAM_COUNTS("Storage.BlobItemSize.FileSystem", | |
| 323 (length - offset) / 1024); | |
| 324 target_blob_builder->AppendSharedBlobItem( | |
| 325 new ShareableBlobDataItem(target_blob_uuid, blob_item)); | |
| 326 break; | |
| 327 case DataElement::TYPE_BLOB: { | |
| 328 UMA_HISTOGRAM_COUNTS("Storage.BlobItemSize.Blob", | |
| 329 (length - offset) / 1024); | |
| 330 // We grab the handle to ensure it stays around while we copy it. | |
| 331 scoped_ptr<BlobDataHandle> src = | |
| 332 GetBlobDataFromUUID(data_element.blob_uuid()); | |
| 333 if (src) { | |
| 334 BlobMapEntry* other_entry = | |
| 335 blob_map_.find(data_element.blob_uuid())->second; | |
| 336 DCHECK(other_entry->data); | |
| 337 exceeded_memory = !AppendBlob(target_blob_uuid, target_blob_builder, | |
| 338 *other_entry->data, offset, length); | |
| 339 } | |
| 340 break; | |
| 341 } | |
| 342 default: | |
| 343 NOTREACHED(); | |
| 344 break; | |
| 345 } | |
| 346 UMA_HISTOGRAM_COUNTS("Storage.Blob.StorageSizeAfterAppend", | |
| 347 memory_usage_ / 1024); | |
| 348 | |
| 349 return !exceeded_memory; | |
| 350 } | |
| 351 | |
| 352 bool BlobStorageContext::AppendBlob( | |
| 353 const std::string& target_blob_uuid, | |
| 354 InternalBlobData::Builder* target_blob_builder, | |
| 355 const InternalBlobData& blob, | |
| 356 size_t offset, | |
| 357 size_t length) { | |
| 358 DCHECK(length > 0); | |
| 359 | |
| 360 const std::vector<scoped_refptr<ShareableBlobDataItem>>& items = blob.items(); | |
| 268 auto iter = items.begin(); | 361 auto iter = items.begin(); |
| 269 if (offset) { | 362 if (offset) { |
| 270 for (; iter != items.end(); ++iter) { | 363 for (; iter != items.end(); ++iter) { |
| 271 const BlobDataItem& item = *(iter->get()); | 364 const BlobDataItem& item = *(iter->get()->item()); |
| 272 if (offset >= item.length()) | 365 if (offset >= item.length()) |
| 273 offset -= item.length(); | 366 offset -= item.length(); |
| 274 else | 367 else |
| 275 break; | 368 break; |
| 276 } | 369 } |
| 277 } | 370 } |
| 278 | 371 |
| 279 for (; iter != items.end() && length > 0; ++iter) { | 372 for (; iter != items.end() && length > 0; ++iter) { |
| 280 const BlobDataItem& item = *(iter->get()); | 373 scoped_refptr<ShareableBlobDataItem> shareable_item = iter->get(); |
| 281 uint64 current_length = item.length() - offset; | 374 const BlobDataItem& item = *(shareable_item->item()); |
| 282 uint64 new_length = current_length > length ? length : current_length; | 375 size_t item_length = item.length(); |
| 283 if (iter->get()->type() == DataElement::TYPE_BYTES) { | 376 DCHECK_GT(item_length, offset); |
| 284 if (!AppendBytesItem( | 377 size_t current_length = item.length() - offset; |
| 285 target_blob_data, | 378 size_t new_length = current_length > length ? length : current_length; |
| 286 item.bytes() + static_cast<size_t>(item.offset() + offset), | 379 |
| 287 static_cast<int64>(new_length))) { | 380 bool reusing_blob_item = offset == 0 && new_length == item.length(); |
| 288 return false; // exceeded memory | 381 UMA_HISTOGRAM_BOOLEAN("Storage.Blob.ReusedItem", reusing_blob_item); |
| 289 } | 382 if (reusing_blob_item) { |
|
michaeln
2015/02/05 20:02:09
woohooo!
dmurph
2015/02/06 01:32:30
:)
| |
| 290 } else if (item.type() == DataElement::TYPE_FILE) { | 383 shareable_item->referencing_blobs().insert(target_blob_uuid); |
| 291 AppendFileItem(target_blob_data, item.path(), item.offset() + offset, | 384 target_blob_builder->AppendSharedBlobItem(shareable_item); |
| 292 new_length, item.expected_modification_time()); | 385 length -= new_length; |
| 293 } else { | 386 continue; |
| 294 DCHECK(item.type() == DataElement::TYPE_FILE_FILESYSTEM); | 387 } |
| 295 AppendFileSystemFileItem(target_blob_data, item.filesystem_url(), | 388 |
| 296 item.offset() + offset, new_length, | 389 // We need to do copying of the items when we have a different offset or |
| 297 item.expected_modification_time()); | 390 // length |
| 391 switch (item.type()) { | |
| 392 case DataElement::TYPE_BYTES: { | |
| 393 UMA_HISTOGRAM_COUNTS("Storage.BlobItemSize.Blob.Bytes", | |
| 394 new_length / 1024); | |
| 395 if (memory_usage_ + new_length > kMaxMemoryUsage) { | |
| 396 return false; | |
| 397 } | |
| 398 DCHECK(!item.offset()); | |
| 399 scoped_ptr<DataElement> element(new DataElement()); | |
| 400 element->SetToBytes(item.bytes() + offset, | |
| 401 static_cast<int64>(new_length)); | |
| 402 memory_usage_ += new_length; | |
| 403 target_blob_builder->AppendSharedBlobItem(new ShareableBlobDataItem( | |
| 404 target_blob_uuid, new BlobDataItem(element.Pass()))); | |
| 405 } break; | |
| 406 case DataElement::TYPE_FILE: { | |
| 407 UMA_HISTOGRAM_COUNTS("Storage.BlobItemSize.Blob.File", | |
| 408 new_length / 1024); | |
| 409 scoped_ptr<DataElement> element(new DataElement()); | |
| 410 element->SetToFilePathRange(item.path(), item.offset() + offset, | |
| 411 new_length, | |
| 412 item.expected_modification_time()); | |
| 413 target_blob_builder->AppendSharedBlobItem(new ShareableBlobDataItem( | |
| 414 target_blob_uuid, | |
| 415 new BlobDataItem(element.Pass(), item.file_handle_))); | |
| 416 } break; | |
| 417 case DataElement::TYPE_FILE_FILESYSTEM: { | |
| 418 UMA_HISTOGRAM_COUNTS("Storage.BlobItemSize.Blob.FileSystem", | |
| 419 new_length / 1024); | |
| 420 scoped_ptr<DataElement> element(new DataElement()); | |
| 421 element->SetToFileSystemUrlRange(item.filesystem_url(), | |
| 422 item.offset() + offset, new_length, | |
| 423 item.expected_modification_time()); | |
| 424 target_blob_builder->AppendSharedBlobItem(new ShareableBlobDataItem( | |
| 425 target_blob_uuid, new BlobDataItem(element.Pass()))); | |
| 426 } break; | |
| 427 default: | |
| 428 CHECK(false) << "Illegal blob item type: " << item.type(); | |
| 298 } | 429 } |
| 299 length -= new_length; | 430 length -= new_length; |
| 300 offset = 0; | 431 offset = 0; |
| 301 } | 432 } |
| 302 return true; | 433 return true; |
| 303 } | 434 } |
| 304 | 435 |
| 305 bool BlobStorageContext::AppendBytesItem(BlobDataBuilder* target_blob_data, | |
| 306 const char* bytes, | |
| 307 int64 length) { | |
| 308 if (length < 0) { | |
| 309 DCHECK(false); | |
| 310 return false; | |
| 311 } | |
| 312 if (memory_usage_ + length > kMaxMemoryUsage) { | |
| 313 return false; | |
| 314 } | |
| 315 target_blob_data->AppendData(bytes, static_cast<size_t>(length)); | |
| 316 memory_usage_ += length; | |
| 317 return true; | |
| 318 } | |
| 319 | |
| 320 void BlobStorageContext::AppendFileItem( | |
| 321 BlobDataBuilder* target_blob_data, | |
| 322 const base::FilePath& file_path, | |
| 323 uint64 offset, | |
| 324 uint64 length, | |
| 325 const base::Time& expected_modification_time) { | |
| 326 // It may be a temporary file that should be deleted when no longer needed. | |
| 327 scoped_refptr<ShareableFileReference> shareable_file = | |
| 328 ShareableFileReference::Get(file_path); | |
| 329 | |
| 330 target_blob_data->AppendFile(file_path, offset, length, | |
| 331 expected_modification_time, shareable_file); | |
| 332 } | |
| 333 | |
| 334 void BlobStorageContext::AppendFileSystemFileItem( | |
| 335 BlobDataBuilder* target_blob_data, | |
| 336 const GURL& filesystem_url, | |
| 337 uint64 offset, | |
| 338 uint64 length, | |
| 339 const base::Time& expected_modification_time) { | |
| 340 target_blob_data->AppendFileSystemFile(filesystem_url, offset, length, | |
| 341 expected_modification_time); | |
| 342 } | |
| 343 | |
| 344 bool BlobStorageContext::IsInUse(const std::string& uuid) { | 436 bool BlobStorageContext::IsInUse(const std::string& uuid) { |
| 345 return blob_map_.find(uuid) != blob_map_.end(); | 437 return blob_map_.find(uuid) != blob_map_.end(); |
| 346 } | 438 } |
| 347 | 439 |
| 348 bool BlobStorageContext::IsBeingBuilt(const std::string& uuid) { | 440 bool BlobStorageContext::IsBeingBuilt(const std::string& uuid) { |
| 349 BlobMap::iterator found = blob_map_.find(uuid); | 441 BlobMap::iterator found = blob_map_.find(uuid); |
| 350 if (found == blob_map_.end()) | 442 if (found == blob_map_.end()) |
| 351 return false; | 443 return false; |
| 352 return found->second->IsBeingBuilt(); | 444 return found->second->IsBeingBuilt(); |
| 353 } | 445 } |
| 354 | 446 |
| 355 bool BlobStorageContext::IsUrlRegistered(const GURL& blob_url) { | 447 bool BlobStorageContext::IsUrlRegistered(const GURL& blob_url) { |
| 356 return public_blob_urls_.find(blob_url) != public_blob_urls_.end(); | 448 return public_blob_urls_.find(blob_url) != public_blob_urls_.end(); |
| 357 } | 449 } |
| 358 | 450 |
| 451 scoped_ptr<BlobDataSnapshot> BlobStorageContext::CreateSnapshot( | |
| 452 const std::string& uuid, | |
| 453 const InternalBlobData& data) { | |
| 454 scoped_ptr<BlobDataSnapshot> snapshot(new BlobDataSnapshot( | |
| 455 uuid, data.content_type(), data.content_disposition())); | |
| 456 snapshot->items_.resize(data.items_.size()); | |
| 457 std::transform(data.items_.begin(), data.items_.end(), | |
|
michaeln
2015/02/05 20:02:09
might be easier to read as a for (const auto& shar
dmurph
2015/02/06 01:32:30
Fine! :)
| |
| 458 snapshot->items_.begin(), | |
| 459 [](const scoped_refptr<ShareableBlobDataItem>& shareableItem) { | |
|
michaeln
2015/02/05 20:02:09
shareableItem s/b shareable_item
dmurph
2015/02/06 01:32:30
Done.
| |
| 460 return shareableItem->item(); | |
| 461 }); | |
| 462 return snapshot; | |
| 463 } | |
| 464 | |
| 359 } // namespace storage | 465 } // namespace storage |
| OLD | NEW |