hmbdc
simplify-high-performance-messaging-programming
BlockingBuffer.hpp
1 #include "hmbdc/Copyright.hpp"
2 #pragma once
3 #include "hmbdc/Compile.hpp"
4 #include "hmbdc/time/Time.hpp"
5 #include "hmbdc/Config.hpp"
6 #include <mutex> // std::mutex, std::unique_lock
7 #include <condition_variable> // std::condition_variable
8 #include <malloc.h>
9 
10 
11 namespace hmbdc { namespace pattern {
12 
13 namespace blocking_buffer_detail {
14 struct iterator;
15 }
16 
19  using value_type = void *;
20  BlockingBuffer(size_t maxItemSize, size_t capacity)
21  : maxItemSize_(maxItemSize)
22  , capacity_(capacity)
23  , size_(0)
24  , seq_(0) {
25  store_ = (char*)memalign(SMP_CACHE_BYTES, capacity*maxItemSize);
26  }
27 
28  ~BlockingBuffer() {
29  ::free(store_);
30  }
31 
32  BlockingBuffer(BlockingBuffer const&) = delete;
33  BlockingBuffer& operator = (BlockingBuffer const&) = delete;
34 
35  value_type getItem(size_t seq) {
36  return store_ + seq % capacity_ * maxItemSize_;
37  }
38 
39  size_t maxItemSize() const {return maxItemSize_;}
40  size_t capacity() const {return capacity_;}
41  void put(void const* item, size_t sizeHint = 0) {
42  std::unique_lock<std::mutex> lck(mutex_);
43  hasSlot_.wait(lck, [this]{return capacity_ > size_;});
44  memcpy(getItem(seq_++), item, sizeHint?sizeHint:maxItemSize_);
45  if (++size_ == 1) {
46  lck.unlock();
47  hasItem_.notify_one();
48  }
49  }
50 
51  template <typename T, typename... Ts>
52  void
53  put(T&& item, Ts&&... items) {
54  std::unique_lock<std::mutex> lck(mutex_);
55  hasSlot_.wait(lck, [this]{return capacity_ > size_ + sizeof...(Ts);});
56  fill(std::forward<T>(item), std::forward<Ts>(items)...);
57  size_ += sizeof...(items) + 1;
58  if (size_ == sizeof...(items) + 1) {
59  lck.unlock();
60  hasItem_.notify_one();
61  }
62  }
63 
64  template <typename It>
65  bool
66  tryPutBatch(It begin, size_t n) {
67  std::unique_lock<std::mutex> lck(mutex_);
68  if (hasSlot_.wait_for(
69  lck, std::chrono::seconds(0), [this, n]{return capacity_ > size_ + n - 1;})) {
70  fillBatch(begin, n);
71  size_ += n;
72  if (size_ == n) {
73  lck.unlock();
74  hasItem_.notify_all();
75  }
76  return true;
77  }
78  return false;
79  }
80 
81  template <typename Item, typename It>
82  bool
83  tryPutBatchInPlace(It begin, size_t n) {
84  std::unique_lock<std::mutex> lck(mutex_);
85  if (hasSlot_.wait_for(
86  lck, std::chrono::seconds(0), [this, n]{return capacity_ > size_ + n - 1;})) {
87  fillBatchInPlace<Item>(begin, n);
88  size_ += n;
89  if (size_ == n) {
90  lck.unlock();
91  hasItem_.notify_all();
92  }
93  return true;
94  }
95  return false;
96  }
97 
98 
99  template <typename T> void putSome(T const& item) {put(&item);}
100  template <typename T, typename ...Args>
101  void putInPlace(Args&&... args) {
102  std::unique_lock<std::mutex> lck(mutex_);
103  hasSlot_.wait(lck, [this](){return capacity_ > size_;});
104  new (getItem(seq_++)) T(std::forward<Args>(args)...);
105  if (++size_ == 1) {
106  lck.unlock();
107  hasItem_.notify_one();
108  }
109  }
110  template <typename T, typename ...Args>
111  bool tryPutInPlace(Args&&... args) {
112  std::unique_lock<std::mutex> lck(mutex_);
113  if (!hasSlot_.wait_for(lck, std::chrono::seconds(0), [this](){return capacity_ > size_;}))
114  return false;
115  new (getItem(seq_++)) T(std::forward<Args>(args)...);
116  if (++size_ == 1) {
117  lck.unlock();
118  hasItem_.notify_one();
119  }
120  return true;
121  }
122 
123  bool tryPut(void const* item, size_t sizeHint = 0
124  , time::Duration timeout = time::Duration::seconds(0)) {
125  std::unique_lock<std::mutex> lck(mutex_);
126  if (!hasSlot_.wait_for(lck, std::chrono::nanoseconds(timeout.nanoseconds())
127  , [this](){return capacity_ > size_;}))
128  return false;
129  memcpy(getItem(seq_++), item, sizeHint?sizeHint:maxItemSize_);
130  if (++size_ == 1) {
131  lck.unlock();
132  hasItem_.notify_one();
133  }
134  return true;
135  }
136 
137  template <typename T>
138  bool tryPut(T const& item
139  , time::Duration timeout = time::Duration::seconds(0)) {
140  return tryPut(&item, sizeof(item), timeout);
141  }
142 
143  bool isFull() const {return size_ == capacity_;}
144  void take(void *dest, size_t sizeHint = 0) {
145  std::unique_lock<std::mutex> lck(mutex_);
146  hasItem_.wait(lck, [this](){return size_;});
147  memcpy(dest, getItem(seq_ - size_), sizeHint?sizeHint:maxItemSize_);
148  if (size_-- == capacity_) {
149  lck.unlock();
150  hasSlot_.notify_all();
151  }
152  }
153 
154  template <typename T>
155  T& take(T& dest) {
156  take(&dest, sizeof(T));
157  return dest;
158  }
159 
160  bool tryTake(void *dest, size_t sizeHint = 0, time::Duration timeout = time::Duration::seconds(0)) {
161  std::unique_lock<std::mutex> lck(mutex_);
162  if (!hasItem_.wait_for(lck, std::chrono::nanoseconds(timeout.nanoseconds()), [this](){return size_;})) return false;
163  memcpy(dest, getItem(seq_ - size_), sizeHint?sizeHint:maxItemSize_);
164  if (size_-- == capacity_) {
165  lck.unlock();
166  hasSlot_.notify_all();
167  }
168  return true;
169  }
170 
171  template <typename T>
172  bool tryTake(T& dest) {
173  return tryTake(&dest, sizeof(T));
174  }
175 
176  template <typename itOut>
177  size_t take(itOut b, itOut e) {
178  std::unique_lock<std::mutex> lck(mutex_);
179  hasItem_.wait(lck, [this](){return size_;});
180  auto s = size_;
181  while (s && b != e) {
182  memcpy(&*b++, getItem(seq_ - s--), std::min(maxItemSize_, sizeof(*b)));
183  }
184  auto ret = size_ - s;
185  size_ = s;
186  if (size_ + ret == capacity_) {
187  lck.unlock();
188  hasSlot_.notify_all();
189  }
190  return ret;
191  }
192 
193  iterator peek();
194  size_t peek(iterator& b, iterator& e);
195  void wasteAfterPeek(size_t len) {
196  std::unique_lock<std::mutex> lck(mutex_);
197  if (size_ == capacity_) hasSlot_.notify_all();
198  size_ -= len;
199  }
200 
201  void waitItem(time::Duration timeout) {
202  std::unique_lock<std::mutex> lck(mutex_);
203  hasItem_.wait_for(lck, std::chrono::nanoseconds(timeout.nanoseconds())
204  , [this](){return size_;});
205  }
206 
207  size_t remainingSize() const {
208  return size_;
209  }
210  void reset() {
211  size_ = 0;
212  seq_ = 0;
213  hasSlot_.notify_all();
214  }
215 
216 private:
217  void fill(){}
218  template <typename T, typename... Ts>
219  void
220  fill(T&& item, Ts&&... items) {
221  new (getItem(seq_++)) T(std::forward<T>(item));
222  fill(std::forward<Ts>(items)...);
223  }
224 
225  template <typename It>
226  void
227  fillBatch(It begin, size_t n) {
228  for (auto it = begin; n; ++it, --n) {
229  using T = decltype(*begin);
230  new (getItem(seq_++)) T(*it);
231  }
232  }
233 
234  template <typename Item, typename It>
235  void
236  fillBatchInPlace(It begin, size_t n) {
237  for (auto it = begin; n; ++it, --n) {
238  new (getItem(seq_++)) Item(*it);
239  }
240  }
241 
242  size_t maxItemSize_;
243  size_t capacity_;
244  char* store_;
245  size_t size_;
246  size_t seq_;
247  std::mutex mutex_;
248  std::condition_variable hasItem_;
249  std::condition_variable hasSlot_;
250 };
251 
252 namespace blocking_buffer_detail {
253 struct iterator {
254  using Sequence = uint64_t;
255  iterator(BlockingBuffer* HMBDC_RESTRICT buf, Sequence seq)
256  : buf_(buf), seq_(seq){}
257  iterator() : buf_(nullptr), seq_(0){}
258  void clear() {buf_ = nullptr;}
259 
260  iterator& operator ++() HMBDC_RESTRICT {
261  ++seq_;
262  return *this;
263  }
264 
265  iterator operator ++(int) HMBDC_RESTRICT {
266  iterator tmp = *this;
267  ++*this;
268  return tmp;
269  }
270 
271  iterator operator + (size_t dis) const HMBDC_RESTRICT {
272  iterator tmp = *this;
273  tmp.seq_ += dis;
274  return tmp;
275  }
276 
277  iterator& operator += (size_t dis) HMBDC_RESTRICT {
278  seq_ += dis;
279  return *this;
280  }
281 
282  size_t operator - (iterator const& other) const HMBDC_RESTRICT {
283  return seq_ - other.seq_;
284  }
285 
286  explicit operator bool() const HMBDC_RESTRICT {
287  return buf_;
288  }
289 
290  bool operator < (iterator const& other) const HMBDC_RESTRICT {
291  return seq_ < other.seq_;
292  }
293 
294  bool operator == (iterator const& other) const HMBDC_RESTRICT {return seq_ == other.seq_;}
295  bool operator != (iterator const& other) const HMBDC_RESTRICT {return seq_ != other.seq_;}
296  void* operator*() const HMBDC_RESTRICT {return buf_->getItem(seq_);}
297  template <typename T> T& get() const HMBDC_RESTRICT {return *static_cast<T*>(**this);}
298  template <typename T>
299  T* operator->() HMBDC_RESTRICT {return static_cast<T*>(buf_->getItem(seq_));}
300 //private:
301  BlockingBuffer* buf_;
302  Sequence seq_;
303 };
304 } //blocking_buffer_detail
305 
306 inline
307 size_t
308 BlockingBuffer::
309 peek(iterator& b, iterator& e) {
310  std::unique_lock<std::mutex> lck(mutex_);
311  e = iterator(this, seq_);
312  b = iterator(this, seq_ - size_);
313  return size_;
314 }
315 
316 inline
317 BlockingBuffer::iterator
318 BlockingBuffer::
319 peek() {
320  std::unique_lock<std::mutex> lck(mutex_);
321  if (size_) {
322  return iterator(this, seq_ - size_);
323  }
324  return iterator();
325 }
326 }}
Definition: BlockingBuffer.hpp:17
Definition: Time.hpp:134
Definition: BlockingBuffer.hpp:253
Definition: Base.hpp:12
Definition: LockFreeBufferMisc.hpp:89