Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright 2013 The Chromium Authors. All rights reserved. | 1 // Copyright 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 "content/browser/dom_storage/dom_storage_namespace.h" | 5 #include "content/browser/dom_storage/dom_storage_namespace.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/sys_info.h" | |
| 10 #include "content/browser/dom_storage/dom_storage_area.h" | 13 #include "content/browser/dom_storage/dom_storage_area.h" |
| 11 #include "content/browser/dom_storage/dom_storage_task_runner.h" | 14 #include "content/browser/dom_storage/dom_storage_task_runner.h" |
| 12 #include "content/browser/dom_storage/session_storage_database.h" | 15 #include "content/browser/dom_storage/session_storage_database.h" |
| 13 #include "content/common/dom_storage/dom_storage_types.h" | 16 #include "content/common/dom_storage/dom_storage_types.h" |
| 14 | 17 |
| 15 namespace content { | 18 namespace content { |
| 16 | 19 |
| 17 DOMStorageNamespace::DOMStorageNamespace( | 20 DOMStorageNamespace::DOMStorageNamespace( |
| 18 const base::FilePath& directory, | 21 const base::FilePath& directory, |
| 19 DOMStorageTaskRunner* task_runner) | 22 DOMStorageTaskRunner* task_runner) |
| 20 : namespace_id_(kLocalStorageNamespaceId), | 23 : namespace_id_(kLocalStorageNamespaceId), |
| 21 directory_(directory), | 24 directory_(directory), |
| 22 task_runner_(task_runner) { | 25 task_runner_(task_runner) { |
| 23 } | 26 } |
| 24 | 27 |
| 25 DOMStorageNamespace::DOMStorageNamespace( | 28 DOMStorageNamespace::DOMStorageNamespace( |
| 26 int64_t namespace_id, | 29 int64_t namespace_id, |
| 27 const std::string& persistent_namespace_id, | 30 const std::string& persistent_namespace_id, |
| 28 SessionStorageDatabase* session_storage_database, | 31 SessionStorageDatabase* session_storage_database, |
| 29 DOMStorageTaskRunner* task_runner) | 32 DOMStorageTaskRunner* task_runner) |
| 30 : namespace_id_(namespace_id), | 33 : namespace_id_(namespace_id), |
| 31 persistent_namespace_id_(persistent_namespace_id), | 34 persistent_namespace_id_(persistent_namespace_id), |
| 32 task_runner_(task_runner), | 35 task_runner_(task_runner), |
| 33 session_storage_database_(session_storage_database) { | 36 session_storage_database_(session_storage_database), |
| 37 is_low_end_device_(base::SysInfo::IsLowEndDevice()) { | |
| 34 DCHECK_NE(kLocalStorageNamespaceId, namespace_id); | 38 DCHECK_NE(kLocalStorageNamespaceId, namespace_id); |
| 35 } | 39 } |
| 36 | 40 |
| 37 DOMStorageNamespace::~DOMStorageNamespace() { | 41 DOMStorageNamespace::~DOMStorageNamespace() { |
| 38 } | 42 } |
| 39 | 43 |
| 40 DOMStorageArea* DOMStorageNamespace::OpenStorageArea(const GURL& origin) { | 44 DOMStorageArea* DOMStorageNamespace::OpenStorageArea(const GURL& origin) { |
| 41 if (AreaHolder* holder = GetAreaHolder(origin)) { | 45 if (AreaHolder* holder = GetAreaHolder(origin)) { |
| 42 ++(holder->open_count_); | 46 ++(holder->open_count_); |
| 43 return holder->area_.get(); | 47 return holder->area_.get(); |
| (...skipping 69 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 113 | 117 |
| 114 void DOMStorageNamespace::DeleteSessionStorageOrigin(const GURL& origin) { | 118 void DOMStorageNamespace::DeleteSessionStorageOrigin(const GURL& origin) { |
| 115 DOMStorageArea* area = OpenStorageArea(origin); | 119 DOMStorageArea* area = OpenStorageArea(origin); |
| 116 area->FastClear(); | 120 area->FastClear(); |
| 117 CloseStorageArea(area); | 121 CloseStorageArea(area); |
| 118 } | 122 } |
| 119 | 123 |
| 120 void DOMStorageNamespace::PurgeMemory(PurgeOption option) { | 124 void DOMStorageNamespace::PurgeMemory(PurgeOption option) { |
| 121 if (directory_.empty()) | 125 if (directory_.empty()) |
| 122 return; // We can't purge w/o backing on disk. | 126 return; // We can't purge w/o backing on disk. |
| 127 | |
| 128 option = std::max(option, FindPurgeLevel()); | |
| 129 if (option == PURGE_UNSPECIFIED) | |
| 130 return; | |
| 131 | |
| 123 AreaMap::iterator it = areas_.begin(); | 132 AreaMap::iterator it = areas_.begin(); |
| 124 while (it != areas_.end()) { | 133 while (it != areas_.end()) { |
| 125 const AreaHolder& holder = it->second; | 134 const AreaHolder& holder = it->second; |
| 126 | 135 |
| 127 // We can't purge if there are changes pending. | 136 // We can't purge if there are changes pending. |
| 128 if (holder.area_->HasUncommittedChanges()) { | 137 if (holder.area_->HasUncommittedChanges()) { |
| 129 if (holder.open_count_ == 0) { | 138 if (holder.open_count_ == 0) { |
| 130 // Schedule an immediate commit so the next time we're asked to purge, | 139 // Schedule an immediate commit so the next time we're asked to purge, |
| 131 // we can drop it from memory. | 140 // we can drop it from memory. |
| 132 holder.area_->ScheduleImmediateCommit(); | 141 holder.area_->ScheduleImmediateCommit(); |
| 133 } | 142 } |
| 134 ++it; | 143 |
| 144 if (option == PURGE_AGGRESSIVE) | |
| 145 holder.area_->TrimDatabase(); | |
| 146 it++; | |
| 135 continue; | 147 continue; |
| 136 } | 148 } |
| 137 | 149 |
| 138 // If not in use, we can shut it down and remove | 150 // If not in use, we can shut it down and remove |
| 139 // it from our collection entirely. | 151 // it from our collection entirely. |
| 140 if (holder.open_count_ == 0) { | 152 if (holder.open_count_ == 0) { |
| 141 holder.area_->Shutdown(); | 153 holder.area_->Shutdown(); |
| 142 areas_.erase(it++); | 154 areas_.erase(it++); |
| 143 continue; | 155 continue; |
| 144 } | 156 } |
| 145 | 157 |
| 146 if (option == PURGE_AGGRESSIVE) { | 158 if (option == PURGE_AGGRESSIVE) { |
| 147 // If aggressive is true, we clear caches and such | 159 // If aggressive is true, we clear caches and such |
| 148 // for opened areas. | 160 // for opened areas. |
| 149 holder.area_->PurgeMemory(); | 161 holder.area_->PurgeMemory(); |
| 150 } | 162 } |
| 151 | 163 it++; |
| 152 ++it; | |
| 153 } | 164 } |
| 154 } | 165 } |
| 155 | 166 |
| 167 // This function works based on the cache sizes without including the database | |
| 168 // size since it can be expensive trying to estimate the sqlite usage for all | |
| 169 // databases. | |
| 170 DOMStorageNamespace::PurgeOption DOMStorageNamespace::FindPurgeLevel() { | |
| 171 // Maximum in-memory cache usage of all databases. | |
| 172 size_t in_memory_areas_size_budget = 20 * 1024 * 1024; | |
| 173 // Maximum in-memory cache usage of databases that are open. | |
| 174 size_t inactive_in_memory_area_size_budget = 20 * 1024 * 1024; | |
| 175 // Number of areas over which the open databases can be purged. | |
| 176 size_t active_in_memory_areas_unpurged = 100; | |
| 177 #if defined(OS_ANDROID) | |
|
michaeln
2016/05/09 22:12:17
This might be more complicated then it needs to be
ssid
2016/05/10 02:04:16
I agree this is very complicated. I was expecting
| |
| 178 if (is_low_end_device_) { | |
| 179 // For low-end devices, when the cache size reaches 5Mib, only 10 open | |
| 180 // databases are allowed before being purged. | |
| 181 active_in_memory_areas_unpurged = 10; | |
| 182 in_memory_areas_size_budget = 5 * 1024 * 1024; | |
| 183 // Clear most of the inactive database on low-end device. | |
| 184 inactive_in_memory_area_size_budget = 100 * 1024; | |
| 185 } else { | |
| 186 // Set a low cache budget for inactive database cache on android. | |
| 187 inactive_in_memory_area_size_budget = 1024 * 1024; | |
|
ssid
2016/05/06 03:28:51
I chose these values based on what sizes I observe
michaeln
2016/05/09 22:12:17
we don't have stats for the number of 'area in use
| |
| 188 } | |
| 189 #endif | |
| 190 | |
| 191 size_t total_cache_size = 0; | |
| 192 size_t area_count = 0; | |
| 193 size_t active_areas_cache_size = 0; | |
| 194 unsigned active_areas_count = 0; | |
| 195 for (AreaMap::const_iterator it = areas_.begin(); it != areas_.end(); ++it) { | |
| 196 if (it->second.area_->IsLoadedInMemory()) { | |
| 197 size_t cache_size = it->second.area_->map_usage_in_bytes(); | |
|
ssid
2016/05/06 03:28:52
Do you think just the cache size is a good estimat
michaeln
2016/05/09 22:12:17
Sure, this is what we have to work with.
| |
| 198 total_cache_size += cache_size; | |
| 199 ++area_count; | |
| 200 if (it->second.open_count_ > 0) { | |
| 201 active_areas_cache_size += cache_size; | |
| 202 ++active_areas_count; | |
| 203 } | |
| 204 } | |
| 205 } | |
| 206 | |
| 207 if (active_areas_cache_size > in_memory_areas_size_budget && | |
| 208 active_areas_count > active_in_memory_areas_unpurged) { | |
| 209 return PURGE_AGGRESSIVE; | |
| 210 } | |
| 211 | |
| 212 size_t purgeable_cache_size = total_cache_size - active_areas_cache_size; | |
| 213 if (purgeable_cache_size) { | |
| 214 if (purgeable_cache_size > inactive_in_memory_area_size_budget || | |
| 215 total_cache_size > in_memory_areas_size_budget) { | |
| 216 return PURGE_UNOPENED; | |
| 217 } | |
| 218 } | |
| 219 return PURGE_UNSPECIFIED; | |
| 220 } | |
| 221 | |
| 156 void DOMStorageNamespace::Shutdown() { | 222 void DOMStorageNamespace::Shutdown() { |
| 157 AreaMap::const_iterator it = areas_.begin(); | 223 AreaMap::const_iterator it = areas_.begin(); |
| 158 for (; it != areas_.end(); ++it) | 224 for (; it != areas_.end(); ++it) |
| 159 it->second.area_->Shutdown(); | 225 it->second.area_->Shutdown(); |
| 160 } | 226 } |
| 161 | 227 |
| 162 void DOMStorageNamespace::Flush() { | 228 void DOMStorageNamespace::Flush() { |
| 163 for (auto& entry : areas_) { | 229 for (auto& entry : areas_) { |
| 164 if (!entry.second.area_->HasUncommittedChanges()) | 230 if (!entry.second.area_->HasUncommittedChanges()) |
| 165 continue; | 231 continue; |
| 166 entry.second.area_->ScheduleImmediateCommit(); | 232 entry.second.area_->ScheduleImmediateCommit(); |
| 167 } | 233 } |
| 168 } | 234 } |
| 169 | 235 |
| 170 unsigned int DOMStorageNamespace::CountInMemoryAreas() const { | |
| 171 unsigned int area_count = 0; | |
| 172 for (AreaMap::const_iterator it = areas_.begin(); it != areas_.end(); ++it) { | |
| 173 if (it->second.area_->IsLoadedInMemory()) | |
| 174 ++area_count; | |
| 175 } | |
| 176 return area_count; | |
| 177 } | |
| 178 | |
| 179 void DOMStorageNamespace::OnMemoryDump( | 236 void DOMStorageNamespace::OnMemoryDump( |
| 180 base::trace_event::ProcessMemoryDump* pmd) { | 237 base::trace_event::ProcessMemoryDump* pmd) { |
| 181 DCHECK(task_runner_->IsRunningOnPrimarySequence()); | 238 DCHECK(task_runner_->IsRunningOnPrimarySequence()); |
| 182 for (const auto& it : areas_) | 239 for (const auto& it : areas_) |
| 183 it.second.area_->OnMemoryDump(pmd); | 240 it.second.area_->OnMemoryDump(pmd); |
| 184 } | 241 } |
| 185 | 242 |
| 186 DOMStorageNamespace::AreaHolder* | 243 DOMStorageNamespace::AreaHolder* |
| 187 DOMStorageNamespace::GetAreaHolder(const GURL& origin) { | 244 DOMStorageNamespace::GetAreaHolder(const GURL& origin) { |
| 188 AreaMap::iterator found = areas_.find(origin); | 245 AreaMap::iterator found = areas_.find(origin); |
| (...skipping 12 matching lines...) Expand all Loading... | |
| 201 DOMStorageArea* area, int count) | 258 DOMStorageArea* area, int count) |
| 202 : area_(area), open_count_(count) { | 259 : area_(area), open_count_(count) { |
| 203 } | 260 } |
| 204 | 261 |
| 205 DOMStorageNamespace::AreaHolder::AreaHolder(const AreaHolder& other) = default; | 262 DOMStorageNamespace::AreaHolder::AreaHolder(const AreaHolder& other) = default; |
| 206 | 263 |
| 207 DOMStorageNamespace::AreaHolder::~AreaHolder() { | 264 DOMStorageNamespace::AreaHolder::~AreaHolder() { |
| 208 } | 265 } |
| 209 | 266 |
| 210 } // namespace content | 267 } // namespace content |
| OLD | NEW |