/* * Copyright (C) 2020 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef SRC_PROFILING_PERF_UNWIND_QUEUE_H_ #define SRC_PROFILING_PERF_UNWIND_QUEUE_H_ #include #include #include "perfetto/base/logging.h" namespace perfetto { namespace profiling { struct WriteView { bool valid; // if false: buffer is full, and |write_pos| is invalid uint64_t write_pos; }; struct ReadView { uint64_t read_pos; uint64_t write_pos; }; // Single-writer, single-reader ring buffer of fixed-size entries (of any // default-constructible type). Size of the buffer is static for the lifetime of // UnwindQueue, and must be a power of two. // Writer side appends entries one at a time, and must stop if there // is no available capacity. // Reader side sees all unconsumed entries, and can advance the reader position // by any amount. template class UnwindQueue { public: UnwindQueue() { static_assert(QueueSize != 0 && ((QueueSize & (QueueSize - 1)) == 0), "not a power of two"); } UnwindQueue(const UnwindQueue&) = delete; UnwindQueue& operator=(const UnwindQueue&) = delete; UnwindQueue(UnwindQueue&&) = delete; UnwindQueue& operator=(UnwindQueue&&) = delete; T& at(uint64_t pos) { return data_[pos % QueueSize]; } WriteView BeginWrite() { uint64_t rd = rd_pos_.load(std::memory_order_acquire); uint64_t wr = wr_pos_.load(std::memory_order_relaxed); PERFETTO_DCHECK(wr >= rd); if (wr - rd >= QueueSize) return WriteView{false, 0}; // buffer fully occupied return WriteView{true, wr}; } void CommitWrite() { wr_pos_.fetch_add(1u, std::memory_order_release); } ReadView BeginRead() { uint64_t wr = wr_pos_.load(std::memory_order_acquire); uint64_t rd = rd_pos_.load(std::memory_order_relaxed); PERFETTO_DCHECK(wr >= rd && wr - rd <= QueueSize); return ReadView{rd, wr}; } void CommitNewReadPosition(uint64_t pos) { rd_pos_.store(pos, std::memory_order_release); } private: std::array data_; std::atomic wr_pos_{0}; std::atomic rd_pos_{0}; }; } // namespace profiling } // namespace perfetto #endif // SRC_PROFILING_PERF_UNWIND_QUEUE_H_