// Copyright 2015 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #ifndef CONTENT_RENDERER_DEVTOOLS_LOCK_FREE_CIRCULAR_QUEUE_H_ #define CONTENT_RENDERER_DEVTOOLS_LOCK_FREE_CIRCULAR_QUEUE_H_ #include #include "base/atomicops.h" #include "base/macros.h" #include "base/memory/aligned_memory.h" #define CACHELINE_ALIGNED ALIGNAS(64) namespace content { MSVC_PUSH_DISABLE_WARNING(4324) // structure was padded due to align // Lock-free cache-friendly sampling circular queue for large // records. Intended for fast transfer of large records between a // single producer and a single consumer. If the queue is full, // StartEnqueue will return nullptr. The queue is designed with // a goal in mind to evade cache lines thrashing by preventing // simultaneous reads and writes to adjanced memory locations. template class LockFreeCircularQueue { public: // Executed on the application thread. LockFreeCircularQueue(); ~LockFreeCircularQueue(); // StartEnqueue returns a pointer to a memory location for storing the next // record or nullptr if all entries are full at the moment. T* StartEnqueue(); // Notifies the queue that the producer has complete writing data into the // memory returned by StartEnqueue and it can be passed to the consumer. void FinishEnqueue(); // Executed on the consumer (analyzer) thread. // Retrieves, but does not remove, the head of this queue, returning nullptr // if this queue is empty. After the record had been read by a consumer, // Remove must be called. T* Peek(); void Remove(); // The class fields have stricter alignment requirements than a normal new // can fulfil, so we need to provide our own new/delete here. void* operator new(size_t size); void operator delete(void* ptr); private: // Reserved values for the entry marker. enum { kEmpty, // Marks clean (processed) entries. kFull // Marks entries already filled by the producer but not yet // completely processed by the consumer. }; struct CACHELINE_ALIGNED Entry { Entry() : marker(kEmpty) {} T record; base::subtle::Atomic32 marker; }; Entry* Next(Entry* entry); Entry buffer_[Length]; CACHELINE_ALIGNED Entry* enqueue_pos_; CACHELINE_ALIGNED Entry* dequeue_pos_; DISALLOW_COPY_AND_ASSIGN(LockFreeCircularQueue); }; MSVC_POP_WARNING() template LockFreeCircularQueue::LockFreeCircularQueue() : enqueue_pos_(buffer_), dequeue_pos_(buffer_) { } template LockFreeCircularQueue::~LockFreeCircularQueue() { } template T* LockFreeCircularQueue::Peek() { base::subtle::MemoryBarrier(); if (base::subtle::Acquire_Load(&dequeue_pos_->marker) == kFull) { return &dequeue_pos_->record; } return nullptr; } template void LockFreeCircularQueue::Remove() { base::subtle::Release_Store(&dequeue_pos_->marker, kEmpty); dequeue_pos_ = Next(dequeue_pos_); } template T* LockFreeCircularQueue::StartEnqueue() { base::subtle::MemoryBarrier(); if (base::subtle::Acquire_Load(&enqueue_pos_->marker) == kEmpty) { return &enqueue_pos_->record; } return nullptr; } template void LockFreeCircularQueue::FinishEnqueue() { base::subtle::Release_Store(&enqueue_pos_->marker, kFull); enqueue_pos_ = Next(enqueue_pos_); } template typename LockFreeCircularQueue::Entry* LockFreeCircularQueue::Next( Entry* entry) { Entry* next = entry + 1; if (next == &buffer_[L]) return buffer_; return next; } template void* LockFreeCircularQueue::operator new(size_t size) { typedef LockFreeCircularQueue QueueTypeAlias; return base::AlignedAlloc(size, ALIGNOF(QueueTypeAlias)); } template void LockFreeCircularQueue::operator delete(void* ptr) { base::AlignedFree(ptr); } } // namespace content #endif // CONTENT_RENDERER_DEVTOOLS_LOCK_FREE_CIRCULAR_QUEUE_H_