rclcpp  master
C++ ROS Client Library API
mapped_ring_buffer.hpp
Go to the documentation of this file.
1 // Copyright 2015 Open Source Robotics Foundation, Inc.
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 // http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 
15 #ifndef RCLCPP__MAPPED_RING_BUFFER_HPP_
16 #define RCLCPP__MAPPED_RING_BUFFER_HPP_
17 
18 #include <algorithm>
19 #include <cstddef>
20 #include <cstdint>
21 #include <memory>
22 #include <mutex>
23 #include <stdexcept>
24 #include <utility>
25 #include <vector>
26 
28 #include "rclcpp/macros.hpp"
30 
31 namespace rclcpp
32 {
33 namespace mapped_ring_buffer
34 {
35 
37 {
38 public:
40 };
41 
43 
59 template<typename T, typename Alloc = std::allocator<void>>
61 {
62 public:
64  using ElemAllocTraits = allocator::AllocRebind<T, Alloc>;
65  using ElemAlloc = typename ElemAllocTraits::allocator_type;
66  using ElemDeleter = allocator::Deleter<ElemAlloc, T>;
67 
68  using ConstElemSharedPtr = std::shared_ptr<const T>;
69  using ElemUniquePtr = std::unique_ptr<T, ElemDeleter>;
70 
72 
78  explicit MappedRingBuffer(size_t size, std::shared_ptr<Alloc> allocator = nullptr)
79  : elements_(size), head_(0)
80  {
81  if (size == 0) {
82  throw std::invalid_argument("size must be a positive, non-zero value");
83  }
84  if (!allocator) {
85  allocator_ = std::make_shared<ElemAlloc>();
86  } else {
87  allocator_ = std::make_shared<ElemAlloc>(*allocator.get());
88  }
89  }
90 
91  virtual ~MappedRingBuffer() {}
92 
94 
105  void
106  get(uint64_t key, ElemUniquePtr & value)
107  {
108  std::lock_guard<std::mutex> lock(data_mutex_);
109  auto it = get_iterator_of_key(key);
110  value = nullptr;
111  if (it != elements_.end() && it->in_use) {
112  if (it->unique_value) {
113  ElemDeleter deleter = it->unique_value.get_deleter();
114  auto ptr = ElemAllocTraits::allocate(*allocator_.get(), 1);
115  ElemAllocTraits::construct(*allocator_.get(), ptr, *it->unique_value);
116  value = ElemUniquePtr(ptr, deleter);
117  } else if (it->shared_value) {
118  ElemDeleter * deleter = std::get_deleter<ElemDeleter, const T>(it->shared_value);
119  auto ptr = ElemAllocTraits::allocate(*allocator_.get(), 1);
120  ElemAllocTraits::construct(*allocator_.get(), ptr, *it->shared_value);
121  if (deleter) {
122  value = ElemUniquePtr(ptr, *deleter);
123  } else {
124  value = ElemUniquePtr(ptr);
125  }
126  } else {
127  throw std::runtime_error("Unexpected empty MappedRingBuffer element.");
128  }
129  }
130  }
131 
133 
143  void
144  get(uint64_t key, ConstElemSharedPtr & value)
145  {
146  std::lock_guard<std::mutex> lock(data_mutex_);
147  auto it = get_iterator_of_key(key);
148  value.reset();
149  if (it != elements_.end() && it->in_use) {
150  if (!it->shared_value) {
151  // The stored unique_ptr is upgraded to a shared_ptr here.
152  // All the remaining get and pop calls done with unique_ptr
153  // signature will receive a copy.
154  if (!it->unique_value) {
155  throw std::runtime_error("Unexpected empty MappedRingBuffer element.");
156  }
157  it->shared_value = std::move(it->unique_value);
158  }
159  value = it->shared_value;
160  }
161  }
162 
164 
178  void
179  pop(uint64_t key, ElemUniquePtr & value)
180  {
181  std::lock_guard<std::mutex> lock(data_mutex_);
182  auto it = get_iterator_of_key(key);
183  value = nullptr;
184  if (it != elements_.end() && it->in_use) {
185  if (it->unique_value) {
186  value = std::move(it->unique_value);
187  } else if (it->shared_value) {
188  auto ptr = ElemAllocTraits::allocate(*allocator_.get(), 1);
189  ElemAllocTraits::construct(*allocator_.get(), ptr, *it->shared_value);
190  auto deleter = std::get_deleter<ElemDeleter, const T>(it->shared_value);
191  if (deleter) {
192  value = ElemUniquePtr(ptr, *deleter);
193  } else {
194  value = ElemUniquePtr(ptr);
195  }
196  it->shared_value.reset();
197  } else {
198  throw std::runtime_error("Unexpected empty MappedRingBuffer element.");
199  }
200  it->in_use = false;
201  }
202  }
203 
205 
215  void
216  pop(uint64_t key, ConstElemSharedPtr & value)
217  {
218  std::lock_guard<std::mutex> lock(data_mutex_);
219  auto it = get_iterator_of_key(key);
220  if (it != elements_.end() && it->in_use) {
221  if (it->shared_value) {
222  value = std::move(it->shared_value);
223  } else if (it->unique_value) {
224  value = std::move(it->unique_value);
225  } else {
226  throw std::runtime_error("Unexpected empty MappedRingBuffer element.");
227  }
228  it->in_use = false;
229  }
230  }
231 
233 
244  bool
246  {
247  std::lock_guard<std::mutex> lock(data_mutex_);
248  bool did_replace = elements_[head_].in_use;
249  Element & element = elements_[head_];
250  element.key = key;
251  element.unique_value.reset();
252  element.shared_value.reset();
253  element.shared_value = value;
254  element.in_use = true;
255  head_ = (head_ + 1) % elements_.size();
256  return did_replace;
257  }
258 
260 
263  bool
264  push_and_replace(uint64_t key, ElemUniquePtr value)
265  {
266  std::lock_guard<std::mutex> lock(data_mutex_);
267  bool did_replace = elements_[head_].in_use;
268  Element & element = elements_[head_];
269  element.key = key;
270  element.unique_value.reset();
271  element.shared_value.reset();
272  element.unique_value = std::move(value);
273  element.in_use = true;
274  head_ = (head_ + 1) % elements_.size();
275  return did_replace;
276  }
277 
279  bool
280  has_key(uint64_t key)
281  {
282  std::lock_guard<std::mutex> lock(data_mutex_);
283  return elements_.end() != get_iterator_of_key(key);
284  }
285 
286 private:
288 
289  struct Element
290  {
291  uint64_t key;
292  ElemUniquePtr unique_value;
293  ConstElemSharedPtr shared_value;
294  bool in_use;
295  };
296 
297  using VectorAlloc = typename std::allocator_traits<Alloc>::template rebind_alloc<Element>;
298 
300  get_iterator_of_key(uint64_t key)
301  {
302  auto it = std::find_if(
303  elements_.begin(), elements_.end(),
304  [key](Element & e) -> bool {
305  return e.key == key && e.in_use;
306  });
307  return it;
308  }
309 
311  size_t head_;
312  std::shared_ptr<ElemAlloc> allocator_;
313  std::mutex data_mutex_;
314 };
315 
316 } // namespace mapped_ring_buffer
317 } // namespace rclcpp
318 
319 #endif // RCLCPP__MAPPED_RING_BUFFER_HPP_
#define RCLCPP_DISABLE_COPY(...)
Definition: macros.hpp:26
void pop(uint64_t key, ConstElemSharedPtr &value)
Give the ownership of the stored value to the caller, at the given key.
Definition: mapped_ring_buffer.hpp:216
bool push_and_replace(uint64_t key, ElemUniquePtr value)
Insert a key-value pair, displacing an existing pair if necessary.
Definition: mapped_ring_buffer.hpp:264
This header provides the get_node_topics_interface() template function.
Definition: allocator_common.hpp:24
bool push_and_replace(uint64_t key, ConstElemSharedPtr value)
Insert a key-value pair, displacing an existing pair if necessary.
Definition: mapped_ring_buffer.hpp:245
Ring buffer container of shared_ptr&#39;s or unique_ptr&#39;s of T, which can be accessed by a key...
Definition: mapped_ring_buffer.hpp:60
typename std::allocator_traits< Alloc >::template rebind_traits< T > AllocRebind
Definition: allocator_common.hpp:30
Definition: mapped_ring_buffer.hpp:36
#define RCLCPP_SMART_PTR_DEFINITIONS(...)
Definition: macros.hpp:36
virtual ~MappedRingBuffer()
Definition: mapped_ring_buffer.hpp:91
allocator::AllocRebind< T, Alloc > ElemAllocTraits
Definition: mapped_ring_buffer.hpp:64
T move(T... args)
allocator::Deleter< ElemAlloc, T > ElemDeleter
Definition: mapped_ring_buffer.hpp:66
T find_if(T... args)
#define RCLCPP_PUBLIC
Definition: visibility_control.hpp:50
void pop(uint64_t key, ElemUniquePtr &value)
Give the ownership of the stored value to the caller if possible, or copy and release.
Definition: mapped_ring_buffer.hpp:179
typename std::conditional< std::is_same< typename std::allocator_traits< Alloc >::template rebind_alloc< T >, typename std::allocator< void >::template rebind< T >::other >::value, std::default_delete< T >, AllocatorDeleter< Alloc > >::type Deleter
Definition: allocator_deleter.hpp:101
typename ElemAllocTraits::allocator_type ElemAlloc
Definition: mapped_ring_buffer.hpp:65
bool has_key(uint64_t key)
Return true if the key is found in the ring buffer, otherwise false.
Definition: mapped_ring_buffer.hpp:280