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

Side by Side Diff: cc/resources/resource_pool.cc

Issue 1293873005: Expire resources in ResourcePool after non-use (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@re-use
Patch Set: Created 5 years, 4 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
« cc/resources/resource_pool.h ('K') | « cc/resources/resource_pool.h ('k') | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 // Copyright 2012 The Chromium Authors. All rights reserved. 1 // Copyright 2012 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 "cc/resources/resource_pool.h" 5 #include "cc/resources/resource_pool.h"
6 6
7 #include <algorithm> 7 #include <algorithm>
8 8
9 #include "base/format_macros.h" 9 #include "base/format_macros.h"
10 #include "base/strings/stringprintf.h" 10 #include "base/strings/stringprintf.h"
11 #include "base/thread_task_runner_handle.h" 11 #include "base/thread_task_runner_handle.h"
12 #include "base/trace_event/memory_dump_manager.h" 12 #include "base/trace_event/memory_dump_manager.h"
13 #include "cc/resources/resource_provider.h" 13 #include "cc/resources/resource_provider.h"
14 #include "cc/resources/resource_util.h" 14 #include "cc/resources/resource_util.h"
15 #include "cc/resources/scoped_resource.h" 15 #include "cc/resources/scoped_resource.h"
16 16
17 namespace cc { 17 namespace cc {
18 18
19 namespace {
reveman 2015/08/20 21:43:37 nit: move this blank line below l.19 instead
ericrk 2015/08/21 12:48:22 I *think* I did what you meant? Tried to match oth
20 // Delay before a resource is considered expired.
21 const int kResourceExpirationDelayMs = 1000;
22 } // namespace
reveman 2015/08/20 21:43:37 nit: blank line above this
ericrk 2015/08/21 12:48:22 Done.
23
19 void ResourcePool::PoolResource::OnMemoryDump( 24 void ResourcePool::PoolResource::OnMemoryDump(
20 base::trace_event::ProcessMemoryDump* pmd, 25 base::trace_event::ProcessMemoryDump* pmd,
21 const ResourceProvider* resource_provider, 26 const ResourceProvider* resource_provider,
22 bool is_free) const { 27 bool is_free) const {
23 // Resource IDs are not process-unique, so log with the ResourceProvider's 28 // Resource IDs are not process-unique, so log with the ResourceProvider's
24 // unique id. 29 // unique id.
25 std::string parent_node = 30 std::string parent_node =
26 base::StringPrintf("cc/resource_memory/resource_provider_%d/resource_%d", 31 base::StringPrintf("cc/resource_memory/resource_provider_%d/resource_%d",
27 resource_provider->tracing_id(), id()); 32 resource_provider->tracing_id(), id());
28 33
(...skipping 10 matching lines...) Expand all
39 dump->AddScalar(base::trace_event::MemoryAllocatorDump::kNameSize, 44 dump->AddScalar(base::trace_event::MemoryAllocatorDump::kNameSize,
40 base::trace_event::MemoryAllocatorDump::kUnitsBytes, 45 base::trace_event::MemoryAllocatorDump::kUnitsBytes,
41 total_bytes); 46 total_bytes);
42 47
43 if (is_free) { 48 if (is_free) {
44 dump->AddScalar("free_size", 49 dump->AddScalar("free_size",
45 base::trace_event::MemoryAllocatorDump::kUnitsBytes, 50 base::trace_event::MemoryAllocatorDump::kUnitsBytes,
46 total_bytes); 51 total_bytes);
47 } 52 }
48 } 53 }
54
49 ResourcePool::ResourcePool(ResourceProvider* resource_provider) 55 ResourcePool::ResourcePool(ResourceProvider* resource_provider)
50 : resource_provider_(resource_provider), 56 : ResourcePool(resource_provider, 0) {}
51 target_(0),
52 max_memory_usage_bytes_(0),
53 max_unused_memory_usage_bytes_(0),
54 max_resource_count_(0),
55 memory_usage_bytes_(0),
56 unused_memory_usage_bytes_(0),
57 resource_count_(0) {
58 base::trace_event::MemoryDumpManager::GetInstance()->RegisterDumpProvider(
59 this, base::ThreadTaskRunnerHandle::Get());
60 }
61 57
62 ResourcePool::ResourcePool(ResourceProvider* resource_provider, GLenum target) 58 ResourcePool::ResourcePool(ResourceProvider* resource_provider, GLenum target)
63 : resource_provider_(resource_provider), 59 : resource_provider_(resource_provider),
64 target_(target), 60 target_(target),
65 max_memory_usage_bytes_(0), 61 max_memory_usage_bytes_(0),
66 max_unused_memory_usage_bytes_(0), 62 max_unused_memory_usage_bytes_(0),
67 max_resource_count_(0), 63 max_resource_count_(0),
68 memory_usage_bytes_(0), 64 memory_usage_bytes_(0),
69 unused_memory_usage_bytes_(0), 65 unused_memory_usage_bytes_(0),
70 resource_count_(0) { 66 resource_count_(0),
71 DCHECK_NE(0u, target); 67 evict_expired_resources_pending_(false),
68 resource_expiration_delay_(
69 base::TimeDelta::FromMilliseconds(kResourceExpirationDelayMs)),
70 weak_ptr_factory_(this) {
71 evict_expired_resources_callback_ = base::Bind(
reveman 2015/08/20 21:43:37 Unlike one-copy where worker threads are in play,
ericrk 2015/08/21 12:48:22 ok, makes sense.
72 &ResourcePool::EvictExpiredResources, weak_ptr_factory_.GetWeakPtr());
72 base::trace_event::MemoryDumpManager::GetInstance()->RegisterDumpProvider( 73 base::trace_event::MemoryDumpManager::GetInstance()->RegisterDumpProvider(
73 this, base::ThreadTaskRunnerHandle::Get()); 74 this, base::ThreadTaskRunnerHandle::Get());
74 } 75 }
75 76
76 ResourcePool::~ResourcePool() { 77 ResourcePool::~ResourcePool() {
77 base::trace_event::MemoryDumpManager::GetInstance()->UnregisterDumpProvider( 78 base::trace_event::MemoryDumpManager::GetInstance()->UnregisterDumpProvider(
78 this); 79 this);
79 80
80 DCHECK_EQ(0u, in_use_resources_.size()); 81 DCHECK_EQ(0u, in_use_resources_.size());
81 82
(...skipping 65 matching lines...) Expand 10 before | Expand all | Expand 10 after
147 resource->size(), resource->format()); 148 resource->size(), resource->format());
148 return resource; 149 return resource;
149 } 150 }
150 151
151 void ResourcePool::ReleaseResource(Resource* resource, uint64_t content_id) { 152 void ResourcePool::ReleaseResource(Resource* resource, uint64_t content_id) {
152 auto it = in_use_resources_.find(resource->id()); 153 auto it = in_use_resources_.find(resource->id());
153 DCHECK(it != in_use_resources_.end()); 154 DCHECK(it != in_use_resources_.end());
154 155
155 PoolResource* pool_resource = it->second; 156 PoolResource* pool_resource = it->second;
156 pool_resource->set_content_id(content_id); 157 pool_resource->set_content_id(content_id);
158 pool_resource->set_last_usage(base::TimeTicks::Now());
reveman 2015/08/20 21:43:37 Can we avoid calling Now() two times in this case.
ericrk 2015/08/21 12:48:22 Ok, refactored so we now only call Now() once in t
157 159
158 // Transfer resource to |busy_resources_|. 160 // Transfer resource to |busy_resources_|.
159 busy_resources_.push_back(in_use_resources_.take_and_erase(it)); 161 busy_resources_.push_back(in_use_resources_.take_and_erase(it));
162
163 // Now that we have evictable resources, schedule cleanup if necessary.
164 ScheduleEvictExpiredResources();
160 } 165 }
161 166
162 void ResourcePool::SetResourceUsageLimits(size_t max_memory_usage_bytes, 167 void ResourcePool::SetResourceUsageLimits(size_t max_memory_usage_bytes,
163 size_t max_unused_memory_usage_bytes, 168 size_t max_unused_memory_usage_bytes,
164 size_t max_resource_count) { 169 size_t max_resource_count) {
165 max_memory_usage_bytes_ = max_memory_usage_bytes; 170 max_memory_usage_bytes_ = max_memory_usage_bytes;
166 max_unused_memory_usage_bytes_ = max_unused_memory_usage_bytes; 171 max_unused_memory_usage_bytes_ = max_unused_memory_usage_bytes;
167 max_resource_count_ = max_resource_count; 172 max_resource_count_ = max_resource_count;
168 173
169 ReduceResourceUsage(); 174 ReduceResourceUsage();
170 } 175 }
171 176
172 void ResourcePool::ReduceResourceUsage() { 177 void ResourcePool::ReduceResourceUsage() {
173 while (!unused_resources_.empty()) { 178 while (!unused_resources_.empty()) {
174 if (!ResourceUsageTooHigh()) 179 if (!ResourceUsageTooHigh())
175 break; 180 break;
176 181
177 // LRU eviction pattern. Most recently used might be blocked by 182 // LRU eviction pattern. Most recently used might be blocked by
178 // a read lock fence but it's still better to evict the least 183 // a read lock fence but it's still better to evict the least
179 // recently used as it prevents a resource that is hard to reuse 184 // recently used as it prevents a resource that is hard to reuse
180 // because of unique size from being kept around. Resources that 185 // because of unique size from being kept around. Resources that
181 // can't be locked for write might also not be truly free-able. 186 // can't be locked for write might also not be truly free-able.
182 // We can free the resource here but it doesn't mean that the 187 // We can free the resource here but it doesn't mean that the
183 // memory is necessarily returned to the OS. 188 // memory is necessarily returned to the OS.
184 scoped_ptr<PoolResource> resource = unused_resources_.take_front(); 189 DeleteUnusedResource(unused_resources_.take_front());
185 unused_memory_usage_bytes_ -= ResourceUtil::UncheckedSizeInBytes<size_t>(
186 resource->size(), resource->format());
187 DeleteResource(resource.Pass());
188 } 190 }
189 } 191 }
190 192
191 bool ResourcePool::ResourceUsageTooHigh() { 193 bool ResourcePool::ResourceUsageTooHigh() {
192 if (resource_count_ > max_resource_count_) 194 if (resource_count_ > max_resource_count_)
193 return true; 195 return true;
194 if (memory_usage_bytes_ > max_memory_usage_bytes_) 196 if (memory_usage_bytes_ > max_memory_usage_bytes_)
195 return true; 197 return true;
196 if (unused_memory_usage_bytes_ > max_unused_memory_usage_bytes_) 198 if (unused_memory_usage_bytes_ > max_unused_memory_usage_bytes_)
197 return true; 199 return true;
198 return false; 200 return false;
199 } 201 }
200 202
201 void ResourcePool::DeleteResource(scoped_ptr<PoolResource> resource) { 203 void ResourcePool::DeleteResource(scoped_ptr<PoolResource> resource) {
202 size_t resource_bytes = ResourceUtil::UncheckedSizeInBytes<size_t>( 204 size_t resource_bytes = ResourceUtil::UncheckedSizeInBytes<size_t>(
203 resource->size(), resource->format()); 205 resource->size(), resource->format());
204 memory_usage_bytes_ -= resource_bytes; 206 memory_usage_bytes_ -= resource_bytes;
205 --resource_count_; 207 --resource_count_;
206 } 208 }
207 209
210 void ResourcePool::DeleteUnusedResource(scoped_ptr<PoolResource> resource) {
211 unused_memory_usage_bytes_ -= ResourceUtil::UncheckedSizeInBytes<size_t>(
212 resource->size(), resource->format());
213 DeleteResource(resource.Pass());
214 }
215
208 void ResourcePool::CheckBusyResources() { 216 void ResourcePool::CheckBusyResources() {
209 for (size_t i = 0; i < busy_resources_.size();) { 217 for (size_t i = 0; i < busy_resources_.size();) {
210 ResourceDeque::iterator it(busy_resources_.begin() + i); 218 ResourceDeque::iterator it(busy_resources_.begin() + i);
211 PoolResource* resource = *it; 219 PoolResource* resource = *it;
212 220
213 if (resource_provider_->CanLockForWrite(resource->id())) { 221 if (resource_provider_->CanLockForWrite(resource->id())) {
214 DidFinishUsingResource(busy_resources_.take(it)); 222 DidFinishUsingResource(busy_resources_.take(it));
215 } else if (resource_provider_->IsLost(resource->id())) { 223 } else if (resource_provider_->IsLost(resource->id())) {
216 // Remove lost resources from pool. 224 // Remove lost resources from pool.
217 DeleteResource(busy_resources_.take(it)); 225 DeleteResource(busy_resources_.take(it));
218 } else { 226 } else {
219 ++i; 227 ++i;
220 } 228 }
221 } 229 }
222 } 230 }
223 231
224 void ResourcePool::DidFinishUsingResource(scoped_ptr<PoolResource> resource) { 232 void ResourcePool::DidFinishUsingResource(scoped_ptr<PoolResource> resource) {
225 unused_memory_usage_bytes_ += ResourceUtil::UncheckedSizeInBytes<size_t>( 233 unused_memory_usage_bytes_ += ResourceUtil::UncheckedSizeInBytes<size_t>(
226 resource->size(), resource->format()); 234 resource->size(), resource->format());
227 unused_resources_.push_back(resource.Pass()); 235 unused_resources_.push_back(resource.Pass());
228 } 236 }
229 237
238 void ResourcePool::ScheduleEvictExpiredResources() {
239 if (evict_expired_resources_pending_)
240 return;
241
242 if (!HasEvictableResources())
243 return;
244
245 evict_expired_resources_pending_ = true;
246
247 // Schedule a call to EvictExpiredResources at the time when the LRU buffer
248 // expires.
249 base::TimeTicks reduce_resource_usage_time =
250 GetUsageTimeForLRUResource() + resource_expiration_delay_;
251 base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
reveman 2015/08/20 21:43:37 Should this task runner be passed to the ctor inst
ericrk 2015/08/21 12:48:22 Sure, we can do that.
252 FROM_HERE, evict_expired_resources_callback_,
253 reduce_resource_usage_time - base::TimeTicks::Now());
254 }
255
256 void ResourcePool::EvictExpiredResources() {
257 evict_expired_resources_pending_ = false;
258
259 if (!HasEvictableResources())
260 return;
261
262 EvictResourcesNotUsedSince(base::TimeTicks::Now() -
263 resource_expiration_delay_);
264
265 // Re-schedule this function if necessary.
266 ScheduleEvictExpiredResources();
267 }
268
269 void ResourcePool::EvictResourcesNotUsedSince(base::TimeTicks time_limit) {
270 while (!unused_resources_.empty()) {
271 // |unused_resources_| is not strictly ordered with regards to last_usage,
272 // as this may not exactly line up with the time a resource became non-busy.
273 // However, this should be roughly ordered, and will only introduce slight
274 // delays in freeing expired resources.
275 if (unused_resources_.front()->last_usage() > time_limit)
276 return;
277
278 DeleteUnusedResource(unused_resources_.take_front());
279 }
280
281 // Also free busy resources older than the delay. With a sufficiently large
282 // delay, such as the 1 second used here, any "busy" resources which have
283 // expired are not likely to be busy. Additionally, freeing a "busy" resource
284 // has no downside other than incorrect accounting.
285 while (!busy_resources_.empty()) {
286 if (busy_resources_.front()->last_usage() > time_limit)
287 return;
288
289 DeleteResource(busy_resources_.take_front());
290 }
291 }
292
293 bool ResourcePool::HasEvictableResources() const {
294 return !unused_resources_.empty() || !busy_resources_.empty();
295 }
296
297 base::TimeTicks ResourcePool::GetUsageTimeForLRUResource() const {
298 if (!unused_resources_.empty()) {
299 return unused_resources_.front()->last_usage();
300 }
301
302 if (!busy_resources_.empty()) {
reveman 2015/08/20 21:43:37 DCHECK(!busy_resources_.empty()) instead as we sho
ericrk 2015/08/21 12:48:22 sounds good
303 return busy_resources_.front()->last_usage();
304 }
305
306 return base::TimeTicks();
307 }
308
230 bool ResourcePool::OnMemoryDump(const base::trace_event::MemoryDumpArgs& args, 309 bool ResourcePool::OnMemoryDump(const base::trace_event::MemoryDumpArgs& args,
231 base::trace_event::ProcessMemoryDump* pmd) { 310 base::trace_event::ProcessMemoryDump* pmd) {
232 for (const auto& resource : unused_resources_) { 311 for (const auto& resource : unused_resources_) {
233 resource->OnMemoryDump(pmd, resource_provider_, true /* is_free */); 312 resource->OnMemoryDump(pmd, resource_provider_, true /* is_free */);
234 } 313 }
235 for (const auto& resource : busy_resources_) { 314 for (const auto& resource : busy_resources_) {
236 resource->OnMemoryDump(pmd, resource_provider_, false /* is_free */); 315 resource->OnMemoryDump(pmd, resource_provider_, false /* is_free */);
237 } 316 }
238 for (const auto& entry : in_use_resources_) { 317 for (const auto& entry : in_use_resources_) {
239 entry.second->OnMemoryDump(pmd, resource_provider_, false /* is_free */); 318 entry.second->OnMemoryDump(pmd, resource_provider_, false /* is_free */);
240 } 319 }
241 return true; 320 return true;
242 } 321 }
243 322
244 } // namespace cc 323 } // namespace cc
OLDNEW
« cc/resources/resource_pool.h ('K') | « cc/resources/resource_pool.h ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698