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

Unified Diff: base/debug/activity_tracker.cc

Issue 2387733002: Move memory management code into separate class for future reuse. (Closed)
Patch Set: added comment and tests Created 4 years, 2 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 side-by-side diff with in-line comments
Download patch
Index: base/debug/activity_tracker.cc
diff --git a/base/debug/activity_tracker.cc b/base/debug/activity_tracker.cc
index dc1f529ad8977946bc20ae1a7f96bb0d9a52b2fb..6f59db7bb62f740aa78536228fb55bddd1200b82 100644
--- a/base/debug/activity_tracker.cc
+++ b/base/debug/activity_tracker.cc
@@ -66,6 +66,85 @@ ActivityData ActivityData::ForThread(const PlatformThreadHandle& handle) {
return ForThread(thread_ref.as_id);
}
+ActivityTrackerMemoryAllocator::ActivityTrackerMemoryAllocator(
+ PersistentMemoryAllocator* allocator,
+ uint32_t object_type,
+ size_t object_size,
+ size_t cache_size)
+ : allocator_(allocator),
+ object_type_(object_type),
+ object_size_(object_size),
+ cache_size_(cache_size),
+ iterator_(allocator),
+ cache_values_(new Reference[cache_size]),
+ cache_used_(0) {
+ DCHECK(allocator);
+}
+
+ActivityTrackerMemoryAllocator::~ActivityTrackerMemoryAllocator() {}
+
+ActivityTrackerMemoryAllocator::Reference
+ActivityTrackerMemoryAllocator::GetObjectReference() {
+ // First see if there is a cached value that can be returned. This is much
+ // faster than searching the memory system for free blocks.
+ while (cache_used_ > 0) {
+ Reference cached = cache_values_[--cache_used_];
+ // Change the type of the cached object to the proper type and return it.
+ // If the type-change fails that means another thread has taken this from
+ // under us (via the search below) so ignore it and keep trying.
+ if (allocator_->ChangeType(cached, object_type_, ~object_type_))
+ return cached;
+ }
+
+ // Fetch the next "free" (~type) object from persistent memory. Rather
+ // than restart the iterator at the head each time and likely waste time
+ // going again through objects that aren't relevant, the iterator continues
+ // from where it last left off and is only reset when the end is reached.
+ // If the returned reference matches |last|, then it has wrapped without
+ // finding anything.
+ const Reference last = iterator_.GetLast();
+ while (true) {
+ uint32_t type;
+ Reference found = iterator_.GetNext(&type);
+ if (found && type == ~object_type_) {
Alexei Svitkine (slow) 2016/10/13 15:25:21 I don't like you inlining this ~ logic in multiple
bcwhite 2016/10/13 17:03:22 Done.
+ // Found a free object. Change it to the proper type and return it. If
+ // the type-change fails that means another thread has taken this from
+ // under us so ignore it and keep trying.
+ if (allocator_->ChangeType(found, object_type_, ~object_type_))
+ return found;
+ }
+ if (found == last) {
+ // Wrapped. No desired object was found.
+ break;
+ }
+ if (!found) {
+ // Reached end; start over at the beginning.
+ iterator_.Reset();
+ }
+ }
+
+ // No free block was found so instead allocate a new one.
+ Reference allocated = allocator_->Allocate(object_size_, object_type_);
+ if (allocated)
+ allocator_->MakeIterable(allocated);
+ return allocated;
+}
+
+void ActivityTrackerMemoryAllocator::ReleaseObjectReference(Reference ref) {
+ // Zero the memory so that it is ready for immediate use if needed later.
+ char* mem_base = allocator_->GetAsObject<char>(ref, object_type_);
+ DCHECK(mem_base);
+ memset(mem_base, 0, object_size_);
+
+ // Mark object as free (use ~type as free-type).
+ bool success = allocator_->ChangeType(ref, ~object_type_, object_type_);
+ DCHECK(success);
+
+ // Add this reference to our "free" cache if there is space.
Alexei Svitkine (slow) 2016/10/13 15:25:21 Nit: Can you expand comment to mention what the im
bcwhite 2016/10/13 17:03:22 Done.
+ if (cache_used_ < cache_size_)
+ cache_values_[cache_used_++] = ref;
+}
+
// static
void Activity::FillFrom(Activity* activity,
const void* origin,
@@ -499,60 +578,25 @@ void GlobalActivityTracker::CreateWithLocalMemory(size_t size,
ThreadActivityTracker* GlobalActivityTracker::CreateTrackerForCurrentThread() {
DCHECK(!this_thread_tracker_.Get());
- PersistentMemoryAllocator::Reference mem_reference =
- PersistentMemoryAllocator::kReferenceNull;
- DCHECK(!mem_reference); // invalid_value should be checkable with !
-
- while (true) {
- // Get the first available memory from the top of the FIFO.
- if (!available_memories_.pop(&mem_reference))
- break;
+ PersistentMemoryAllocator::Reference mem_reference;
- // Turn the reference back into one of the activity-tracker type. This can
- // fail if something else has already taken the block and changed its type.
- if (allocator_->ChangeType(mem_reference, kTypeIdActivityTracker,
- kTypeIdActivityTrackerFree)) {
- break;
- }
+ {
+ base::AutoLock autolock(thread_tracker_allocator_lock_);
+ mem_reference = thread_tracker_allocator_.GetObjectReference();
}
- // Handle the case where no known available memories were found.
if (!mem_reference) {
- // Allocate a block of memory from the persistent segment.
- mem_reference =
- allocator_->Allocate(stack_memory_size_, kTypeIdActivityTracker);
- if (mem_reference) {
- // Success. Make the allocation iterable so it can be found later.
- allocator_->MakeIterable(mem_reference);
- } else {
- // Failure. Look for any free blocks that weren't held in the cache
- // of available memories and try to claim it. This can happen if the
- // |available_memories_| stack isn't sufficiently large to hold all
- // released memories or if multiple independent processes are sharing
- // the memory segment.
- PersistentMemoryAllocator::Iterator iter(allocator_.get());
- while ((mem_reference = iter.GetNextOfType(kTypeIdActivityTrackerFree)) !=
- 0) {
- if (allocator_->ChangeType(mem_reference, kTypeIdActivityTracker,
- kTypeIdActivityTrackerFree)) {
- break;
- }
- mem_reference = 0;
- }
- if (!mem_reference) {
- // Dobule Failure. This shouldn't happen. But be graceful if it does,
- // probably because the underlying allocator wasn't given enough memory
- // to satisfy all possible requests.
- NOTREACHED();
- // Report the thread-count at which the allocator was full so that the
- // failure can be seen and underlying memory resized appropriately.
- UMA_HISTOGRAM_COUNTS_1000(
- "ActivityTracker.ThreadTrackers.MemLimitTrackerCount",
- thread_tracker_count_.load(std::memory_order_relaxed));
- // Return null, just as if tracking wasn't enabled.
- return nullptr;
- }
- }
+ // Failure. This shouldn't happen. But be graceful if it does, probably
+ // because the underlying allocator wasn't given enough memory to satisfy
+ // to all possible requests.
+ NOTREACHED();
+ // Report the thread-count at which the allocator was full so that the
+ // failure can be seen and underlying memory resized appropriately.
+ UMA_HISTOGRAM_COUNTS_1000(
+ "ActivityTracker.ThreadTrackers.MemLimitTrackerCount",
+ thread_tracker_count_.load(std::memory_order_relaxed));
+ // Return null, just as if tracking wasn't enabled.
+ return nullptr;
}
// Convert the memory block found above into an actual memory address.
@@ -589,7 +633,10 @@ GlobalActivityTracker::GlobalActivityTracker(
stack_memory_size_(ThreadActivityTracker::SizeForStackDepth(stack_depth)),
this_thread_tracker_(&OnTLSDestroy),
thread_tracker_count_(0),
- available_memories_(kMaxThreadCount) {
+ thread_tracker_allocator_(allocator_.get(),
+ kTypeIdActivityTracker,
+ stack_memory_size_,
+ kCachedThreadMemories) {
// Ensure the passed memory is valid and empty (iterator finds nothing).
uint32_t type;
DCHECK(!PersistentMemoryAllocator::Iterator(allocator_.get()).GetNext(&type));
@@ -612,25 +659,13 @@ void GlobalActivityTracker::ReturnTrackerMemory(
DCHECK(mem_reference);
DCHECK(mem_base);
- // Zero the memory so that it is ready for use if needed again later. It's
- // better to clear the memory now, when a thread is exiting, than to do it
- // when it is first needed by a thread doing actual work.
- memset(mem_base, 0, stack_memory_size_);
-
// Remove the destructed tracker from the set of known ones.
DCHECK_LE(1, thread_tracker_count_.load(std::memory_order_relaxed));
thread_tracker_count_.fetch_sub(1, std::memory_order_relaxed);
- // The memory was within the persistent memory allocator. Change its type
- // so it is effectively marked as "free".
- allocator_->ChangeType(mem_reference, kTypeIdActivityTrackerFree,
- kTypeIdActivityTracker);
-
- // Push this on the internal cache of available memory blocks so it can
- // be found and reused quickly. If the push somehow exceeds the maximum
- // size of the cache, it will fail but a fallback check in CreateTracker
- // will find it by (slow) iteration.
- available_memories_.push(mem_reference);
+ // Release this memory for re-use at a later time.
+ base::AutoLock autolock(thread_tracker_allocator_lock_);
+ thread_tracker_allocator_.ReleaseObjectReference(mem_reference);
}
// static

Powered by Google App Engine
This is Rietveld 408576698