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

Unified Diff: base/debug/activity_tracker.h

Issue 2255503002: New cache for the activity tracker. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: addressed some review comments by manzagop Created 4 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 side-by-side diff with in-line comments
Download patch
« no previous file with comments | « no previous file | base/debug/activity_tracker.cc » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: base/debug/activity_tracker.h
diff --git a/base/debug/activity_tracker.h b/base/debug/activity_tracker.h
index d6ff724cf2daee6106c3fbce75b9622f1d7a9eda..c3e9e4bfb5053063d15ea2a015e002beb7a2a40f 100644
--- a/base/debug/activity_tracker.h
+++ b/base/debug/activity_tracker.h
@@ -41,6 +41,173 @@ namespace debug {
class ThreadActivityTracker;
+
+//=============================================================================
+// This class provides a lock-free FIFO of any atomic type with the
+// limitation that there must be at least one "invalid" value. This is
+// built as a completely generic type and can (hopefully) be moved to a
+// more generally useful place in the future.
+template <typename T, size_t SIZE, T INVALID_VALUE>
+class LockFreeSimpleFifo {
+ enum : size_t {
+ CAPACITY = SIZE + 1 // +1 because one slot must always be free.
+ };
+
+ public:
+ LockFreeSimpleFifo() : head_(0), tail_(0) {
+ // Ensure that the underlying atomics are also lock-free. This should
+ // evaluate to a constant at compile time and so produce no code, but
+ // a static_assert will not compile.
+ CHECK(head_.is_lock_free());
+ CHECK(stack_[0].is_lock_free());
+
+ // All elements must be "invalid" to start in order for the push/pop
+ // operations to work.
+ for (size_t i = 0; i < CAPACITY; ++i)
+ stack_[i].store(INVALID_VALUE, std::memory_order_relaxed);
+ }
+
+ T invalid_value() { return INVALID_VALUE; }
+ size_t size() { return SIZE; }
+ size_t used() {
+ return (head_.load(std::memory_order_relaxed) + CAPACITY -
+ tail_.load(std::memory_order_relaxed)) %
+ CAPACITY;
+ }
+ bool empty() {
+ return empty(head_.load(std::memory_order_relaxed),
+ tail_.load(std::memory_order_relaxed));
+ }
+ bool full() {
+ return full(head_.load(std::memory_order_relaxed),
+ tail_.load(std::memory_order_relaxed));
+ }
+
+ // Adds a new |value| to the end of the FIFO and returns true on success
+ // or false if the stack was full.
+ bool push(T value);
+
+ // Retrieves the first value off the FIFO and returns it or the "invalid"
+ // value if the stack is empty.
+ T pop();
+
+ private:
+ // Reports if the stack is empty/full based on explicit head/tail values.
+ // Note that the % operaton in C/C++ is a "remainder" operator and thus will
+ // not provide correct "modulus" arithmetic if the left value is negative.
+ bool empty(size_t head, size_t tail) { return head == tail; }
+ bool full(size_t head, size_t tail) {
+ return (tail + CAPACITY - 1) % CAPACITY == head;
+ }
+
+ std::atomic<T> stack_[CAPACITY];
+ std::atomic<size_t> head_; // One past the newest value; where to push.
+ std::atomic<size_t> tail_; // The oldest value; first to pop.
+
+ DISALLOW_COPY_AND_ASSIGN(LockFreeSimpleFifo);
+};
+
+template <typename T, size_t SIZE, T INVALID_VALUE>
+bool LockFreeSimpleFifo<T, SIZE, INVALID_VALUE>::push(T value) {
+ // Pushing the "invalid" value is not allowed; it would be skipped by pop.
+ CHECK_NE(INVALID_VALUE, value);
+
+ while (true) {
+ // Get the head of the stack and acquire its contents.
+ size_t head = head_.load(std::memory_order_acquire);
+ DCHECK_LE(0U, head);
+ DCHECK_GT(CAPACITY, head);
+
+ // If the stack is full, fail.
+ if (full(head, tail_.load(std::memory_order_relaxed)))
+ return false;
+
+ // Write the value being pushed to the top of the fifo, exchanging it
+ // with the "invalid" value that should be there. If the atomic operation
+ // fails then something else has snuck in and pushed something else to
+ // that slot. A "strong" exchange is used to avoid mistakenly incrementing
+ // past the new value.
+ T value_expected = INVALID_VALUE;
+ if (!stack_[head].compare_exchange_strong(value_expected, value,
+ std::memory_order_release,
+ std::memory_order_relaxed)) {
+ // Something got pushed so increment the head pointer past it. This is
+ // done to handle the case where whatever has written the value somehow
+ // died between writing the value and incrementing the head pointer. In
+ // that (unusual) case, simply trying again would lead to an infinite
+ // loop. We try but don't care if it fails because failure just indicates
+ // that the other thread is behaving properly. A "strong" exchange is
+ // necessary to avoid false failures.
+ head_.compare_exchange_strong(head, head + 1,
manzagop (departed) 2016/08/18 14:14:46 I've just noticed this could do a spurious push, i
+ std::memory_order_relaxed,
+ std::memory_order_relaxed);
+
+ // Try again.
+ continue;
+ }
+
+ // Increment the head, releasing the newly stored value. This could fail
+ // if another thread tried a simultaneous push, saw this value, and
+ // bumped the head in the crash-safty increment just above. Because the
+ // result is ignored, a "strong" exchange is necessary to ensure a
+ // false failure doesn't occur.
+ head_.compare_exchange_strong(head, head + 1,
+ std::memory_order_release,
+ std::memory_order_relaxed);
+
+ // Success!
+ return true;
+ }
+}
+
+template <typename T, size_t SIZE, T INVALID_VALUE>
+T LockFreeSimpleFifo<T, SIZE, INVALID_VALUE>::pop() {
+ while (true) {
+ // Get the current number of elements on the stack.
+ size_t tail = tail_.load(std::memory_order_relaxed);
+ DCHECK_LE(0U, tail);
+ DCHECK_GT(CAPACITY, tail);
+
+ // If the stack is empty, fail.
+ if (empty(head_.load(std::memory_order_relaxed), tail))
+ return INVALID_VALUE;
+
+ // Read a value from the bottom of the fifo, writing the "invalid" value
+ // in its place.
+ T value = stack_[tail].exchange(INVALID_VALUE, std::memory_order_relaxed);
+ if (value == INVALID_VALUE) {
+ // Another thread has already taken the "tail" value so increment the
+ // tail pointer past it. This is done to handle the case where whatever
+ // took the value somehow died between reading the value and incrementing
+ // the tail pointer. In that (unusual) case, simply trying again would
+ // lead to an infinite loop. We try but don't care if it fails because
+ // that just indicates that the other thread is behaving properly. A
+ // "strong" exchange is necessary to avoid false failures.
+ tail_.compare_exchange_strong(tail, tail + 1,
+ std::memory_order_relaxed,
+ std::memory_order_relaxed);
+
+ // Try again.
+ continue;
+ }
+
+ // Increment the tail, releasing the newly stored value. This could fail
+ // if another thread tried a simultaneous pop, saw the update, and
+ // bumped the tail in the crash-safty increment just above. Because the
+ // result is ignored, a "strong" exchange is necessary to ensure a
+ // false failure doesn't occur.
+ tail_.compare_exchange_strong(tail, tail + 1,
+ std::memory_order_release,
+ std::memory_order_relaxed);
+
+ // Success!
+ DCHECK_NE(INVALID_VALUE, value);
+ return value;
+ }
+}
+//=============================================================================
+
+
enum : int {
// The maximum number of call-stack addresses stored per activity. This
// cannot be changed without also changing the version number of the
@@ -508,9 +675,10 @@ class BASE_EXPORT GlobalActivityTracker {
// These have to be lock-free because lock activity is tracked and causes
// re-entry problems.
std::atomic<int> thread_tracker_count_;
- std::atomic<int> available_memories_count_;
- std::atomic<PersistentMemoryAllocator::Reference>
- available_memories_[kMaxThreadCount];
+ LockFreeSimpleFifo<PersistentMemoryAllocator::Reference,
+ kMaxThreadCount,
+ PersistentMemoryAllocator::kReferenceNull>
+ available_memories_;
// The active global activity tracker.
static GlobalActivityTracker* g_tracker_;
« no previous file with comments | « no previous file | base/debug/activity_tracker.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698