mirror of https://github.com/grpc/grpc.git
Create a CircularBuffer to store events generated by latent_see. This bounds the total number of generated events.
PiperOrigin-RevId: 658611718pull/37317/head
parent
3e704c7552
commit
449d1b248f
16 changed files with 343 additions and 3 deletions
@ -0,0 +1,109 @@ |
||||
#ifndef GRPC_SRC_CORE_UTIL_RING_BUFFER_H_ |
||||
#define GRPC_SRC_CORE_UTIL_RING_BUFFER_H_ |
||||
|
||||
#include <grpc/support/port_platform.h> |
||||
|
||||
#include <array> |
||||
#include <cstddef> |
||||
#include <iterator> |
||||
|
||||
#include "absl/types/optional.h" |
||||
|
||||
namespace grpc_core { |
||||
|
||||
template <typename T, int kCapacity> |
||||
class RingBuffer { |
||||
public: |
||||
class RingBufferIterator { |
||||
public: |
||||
using iterator_category = std::forward_iterator_tag; |
||||
using value_type = const T; |
||||
using pointer = void; |
||||
using reference = void; |
||||
using difference_type = std::ptrdiff_t; |
||||
|
||||
RingBufferIterator& operator++() { |
||||
if (--size_ <= 0) { |
||||
head_ = 0; |
||||
size_ = 0; |
||||
buffer_ = nullptr; |
||||
} else { |
||||
head_ = (head_ + 1) % kCapacity; |
||||
} |
||||
return *this; |
||||
} |
||||
|
||||
RingBufferIterator operator++(int) { |
||||
RingBufferIterator tmp(*this); |
||||
operator++(); |
||||
return tmp; |
||||
} |
||||
|
||||
bool operator==(const RingBufferIterator& rhs) const { |
||||
return (buffer_ == rhs.buffer_ && head_ == rhs.head_ && |
||||
size_ == rhs.size_); |
||||
} |
||||
|
||||
bool operator!=(const RingBufferIterator& rhs) const { |
||||
return !operator==(rhs); |
||||
} |
||||
|
||||
T operator*() { return buffer_->data_[head_]; } |
||||
|
||||
RingBufferIterator() : buffer_(nullptr), head_(0), size_(0) {}; |
||||
RingBufferIterator(const RingBufferIterator& other) = default; |
||||
RingBufferIterator(const RingBuffer<T, kCapacity>* buffer) |
||||
: buffer_(buffer), head_(buffer->head_), size_(buffer->size_) { |
||||
if (!size_) { |
||||
buffer_ = nullptr; |
||||
} |
||||
} |
||||
|
||||
private: |
||||
friend class RingBuffer<T, kCapacity>; |
||||
const RingBuffer<T, kCapacity>* buffer_; |
||||
int head_ = 0; |
||||
int size_ = 0; |
||||
}; |
||||
|
||||
RingBuffer() = default; |
||||
|
||||
void Append(T data) { |
||||
if (size_ < kCapacity) { |
||||
data_[size_] = std::move(data); |
||||
size_++; |
||||
} else { |
||||
data_[head_] = std::move(data); |
||||
head_ = (head_ + 1) % kCapacity; |
||||
} |
||||
} |
||||
|
||||
// Returns the data of the first element in the buffer and removes it from
|
||||
// the buffer. If the buffer is empty, returns absl::nullopt.
|
||||
absl::optional<T> PopIfNotEmpty() { |
||||
if (!size_) return absl::nullopt; |
||||
T data = std::move(data_[head_]); |
||||
--size_; |
||||
head_ = (head_ + 1) % kCapacity; |
||||
return data; |
||||
} |
||||
|
||||
void Clear() { |
||||
head_ = 0; |
||||
size_ = 0; |
||||
} |
||||
|
||||
RingBufferIterator begin() const { return RingBufferIterator(this); } |
||||
|
||||
RingBufferIterator end() const { return RingBufferIterator(); } |
||||
|
||||
private: |
||||
friend class RingBufferIterator; |
||||
std::array<T, kCapacity> data_; |
||||
int head_ = 0; |
||||
int size_ = 0; |
||||
}; |
||||
|
||||
} // namespace grpc_core
|
||||
|
||||
#endif // GRPC_SRC_CORE_UTIL_RING_BUFFER_H_
|
@ -0,0 +1,88 @@ |
||||
//
|
||||
//
|
||||
// Copyright 2015 gRPC authors.
|
||||
//
|
||||
// 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.
|
||||
//
|
||||
//
|
||||
|
||||
#include <grpc/support/port_platform.h> |
||||
|
||||
#include "gtest/gtest.h" |
||||
#include "src/core/util/ring_buffer.h" |
||||
|
||||
namespace grpc_core { |
||||
|
||||
constexpr int kBufferCapacity = 1000; |
||||
|
||||
TEST(RingBufferTest, BufferAppendPopTest) { |
||||
RingBuffer<int, kBufferCapacity> buffer; |
||||
EXPECT_FALSE(buffer.PopIfNotEmpty().has_value()); |
||||
|
||||
for (int i = 0; i < (3 * kBufferCapacity)/2; ++i) { |
||||
buffer.Append(i); |
||||
} |
||||
// Pop half of the elements. Elements in [kBufferCapacity / 2,
|
||||
// kBufferCapacity) are popped.
|
||||
int j = kBufferCapacity / 2; |
||||
for (int i = 0; i < kBufferCapacity / 2; ++i) { |
||||
EXPECT_EQ(buffer.PopIfNotEmpty(), j++); |
||||
} |
||||
EXPECT_EQ(j, kBufferCapacity); |
||||
// Iterate over the remaining elements.
|
||||
for (auto it = buffer.begin(); it != buffer.end(); ++it) { |
||||
EXPECT_EQ(*it, j++); |
||||
} |
||||
// Elements in [kBufferCapacity, (3 * kBufferCapacity) / 2) should be present.
|
||||
EXPECT_EQ(j, (3 * kBufferCapacity) / 2); |
||||
|
||||
// Append some more elements. The buffer should now have elements in
|
||||
// [kBufferCapacity, 2 * kBufferCapacity).
|
||||
for (int i = 0; i < kBufferCapacity / 2; ++i) { |
||||
buffer.Append(j++); |
||||
} |
||||
// Pop all the elements.
|
||||
j = kBufferCapacity; |
||||
while (true) { |
||||
auto ret = buffer.PopIfNotEmpty(); |
||||
if (!ret.has_value()) break; |
||||
EXPECT_EQ(*ret, j++); |
||||
} |
||||
EXPECT_EQ(j, 2 * kBufferCapacity); |
||||
} |
||||
|
||||
TEST(RingBufferTest, BufferAppendIterateTest) { |
||||
RingBuffer<int, kBufferCapacity> buffer; |
||||
for (int i = 0; i < 5 * kBufferCapacity; ++i) { |
||||
buffer.Append(i); |
||||
int j = std::max(0, i + 1 - kBufferCapacity); |
||||
// If i >= kBufferCapacity, check that the buffer contains only the last
|
||||
// kBufferCapacity elements [i + 1 - kBufferCapacity, i]. Otherwise check
|
||||
// that the buffer contains all elements from 0 to i.
|
||||
for (auto it = buffer.begin(); it != buffer.end(); ++it) { |
||||
EXPECT_EQ(*it, j++); |
||||
} |
||||
// Check that j was incremented at each step which implies that all the
|
||||
// required elements were present in the buffer.
|
||||
EXPECT_EQ(j, i + 1); |
||||
} |
||||
buffer.Clear(); |
||||
EXPECT_EQ(buffer.begin(), buffer.end()); |
||||
} |
||||
|
||||
} // namespace grpc_core
|
||||
|
||||
int main(int argc, char** argv) { |
||||
::testing::InitGoogleTest(&argc, argv); |
||||
return RUN_ALL_TESTS(); |
||||
} |
Loading…
Reference in new issue