BRE12
_flow_graph_item_buffer_impl.h
1 /*
2  Copyright 2005-2016 Intel Corporation. All Rights Reserved.
3 
4  This file is part of Threading Building Blocks. Threading Building Blocks is free software;
5  you can redistribute it and/or modify it under the terms of the GNU General Public License
6  version 2 as published by the Free Software Foundation. Threading Building Blocks is
7  distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the
8  implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
9  See the GNU General Public License for more details. You should have received a copy of
10  the GNU General Public License along with Threading Building Blocks; if not, write to the
11  Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
12 
13  As a special exception, you may use this file as part of a free software library without
14  restriction. Specifically, if other files instantiate templates or use macros or inline
15  functions from this file, or you compile this file and link it with other files to produce
16  an executable, this file does not by itself cause the resulting executable to be covered
17  by the GNU General Public License. This exception does not however invalidate any other
18  reasons why the executable file might be covered by the GNU General Public License.
19 */
20 
21 #ifndef __TBB__flow_graph_item_buffer_impl_H
22 #define __TBB__flow_graph_item_buffer_impl_H
23 
24 #ifndef __TBB_flow_graph_H
25 #error Do not #include this internal file directly; use public TBB headers instead.
26 #endif
27 
28 #include "tbb/internal/_flow_graph_types_impl.h" // for aligned_pair
29 
30 // in namespace tbb::flow::interfaceX (included in _flow_graph_node_impl.h)
31 
33  //* tests for empty and so forth. No mutual exclusion is built in.
34  //* objects are constructed into and explicitly-destroyed. get_my_item gives
35  // a read-only reference to the item in the buffer. set_my_item may be called
36  // with either an empty or occupied slot.
37 
40 
41 namespace internal {
42 
43  template <typename T, typename A=cache_aligned_allocator<T> >
44  class item_buffer {
45  public:
46  typedef T item_type;
47  enum buffer_item_state { no_item=0, has_item=1, reserved_item=2 };
48  protected:
49  typedef size_t size_type;
51  typedef typename A::template rebind<buffer_item_type>::other allocator_type;
52 
53  buffer_item_type *my_array;
54  size_type my_array_size;
55  static const size_type initial_buffer_size = 4;
56  size_type my_head;
57  size_type my_tail;
58 
59  bool buffer_empty() { return my_head == my_tail; }
60 
61  buffer_item_type &item(size_type i) {
62  __TBB_ASSERT(!(size_type(&(my_array[i&(my_array_size-1)].second))%alignment_of<buffer_item_state>::value),NULL);
63  __TBB_ASSERT(!(size_type(&(my_array[i&(my_array_size-1)].first))%alignment_of<item_type>::value), NULL);
64  return my_array[i & (my_array_size - 1) ];
65  }
66 
67  bool my_item_valid(size_type i) { return (i < my_tail) && (i >= my_head) && (item(i).second != no_item); }
68  bool my_item_reserved(size_type i) { return item(i).second == reserved_item; }
69 
70  // object management in buffer
71  const item_type &get_my_item(size_t i) {
72  __TBB_ASSERT(my_item_valid(i),"attempt to get invalid item");
73  item_type *itm = (tbb::internal::punned_cast<item_type *>(&(item(i).first)));
74  return *(const item_type *)itm;
75  }
76 
77  // may be called with an empty slot or a slot that has already been constructed into.
78  void set_my_item(size_t i, const item_type &o) {
79  if(item(i).second != no_item) {
80  destroy_item(i);
81  }
82  new(&(item(i).first)) item_type(o);
83  item(i).second = has_item;
84  }
85 
86  // destructively-fetch an object from the buffer
87  void fetch_item(size_t i, item_type &o) {
88  __TBB_ASSERT(my_item_valid(i), "Trying to fetch an empty slot");
89  o = get_my_item(i); // could have std::move assign semantics
90  destroy_item(i);
91  }
92 
93  // move an existing item from one slot to another. The moved-to slot must be unoccupied,
94  // the moved-from slot must exist and not be reserved. The after, from will be empty,
95  // to will be occupied but not reserved
96  void move_item(size_t to, size_t from) {
97  __TBB_ASSERT(!my_item_valid(to), "Trying to move to a non-empty slot");
98  __TBB_ASSERT(my_item_valid(from), "Trying to move from an empty slot");
99  set_my_item(to, get_my_item(from)); // could have std::move semantics
100  destroy_item(from);
101 
102  }
103 
104  // put an item in an empty slot. Return true if successful, else false
105  bool place_item(size_t here, const item_type &me) {
106 #if !TBB_DEPRECATED_SEQUENCER_DUPLICATES
107  if(my_item_valid(here)) return false;
108 #endif
109  set_my_item(here, me);
110  return true;
111  }
112 
113  // could be implemented with std::move semantics
114  void swap_items(size_t i, size_t j) {
115  __TBB_ASSERT(my_item_valid(i) && my_item_valid(j), "attempt to swap invalid item(s)");
116  item_type temp = get_my_item(i);
117  set_my_item(i, get_my_item(j));
118  set_my_item(j, temp);
119  }
120 
121  void destroy_item(size_type i) {
122  __TBB_ASSERT(my_item_valid(i), "destruction of invalid item");
123  (tbb::internal::punned_cast<item_type *>(&(item(i).first)))->~item_type();
124  item(i).second = no_item;
125  }
126 
127  // returns the front element
128  const item_type& front() {
129  __TBB_ASSERT(my_item_valid(my_head), "attempt to fetch head non-item");
130  return get_my_item(my_head);
131  }
132 
133  // returns the back element
134  const item_type& back() {
135  __TBB_ASSERT(my_item_valid(my_tail-1), "attempt to fetch tail non-item");
136  return get_my_item(my_tail-1);
137  }
138 
139  // following methods are for reservation of the front of a bufffer.
140  void reserve_item(size_type i) { __TBB_ASSERT(my_item_valid(i) && !my_item_reserved(i), "item cannot be reserved"); item(i).second = reserved_item; }
141  void release_item(size_type i) { __TBB_ASSERT(my_item_reserved(i), "item is not reserved"); item(i).second = has_item; }
142 
143  void destroy_front() { destroy_item(my_head); ++my_head; }
144  void destroy_back() { destroy_item(my_tail-1); --my_tail; }
145 
146  // we have to be able to test against a new tail value without changing my_tail
147  // grow_array doesn't work if we change my_tail when the old array is too small
148  size_type size(size_t new_tail = 0) { return (new_tail ? new_tail : my_tail) - my_head; }
149  size_type capacity() { return my_array_size; }
150  // sequencer_node does not use this method, so we don't
151  // need a version that passes in the new_tail value.
152  bool buffer_full() { return size() >= capacity(); }
153 
155  void grow_my_array( size_t minimum_size ) {
156  // test that we haven't made the structure inconsistent.
157  __TBB_ASSERT(capacity() >= my_tail - my_head, "total items exceed capacity");
158  size_type new_size = my_array_size ? 2*my_array_size : initial_buffer_size;
159  while( new_size<minimum_size )
160  new_size*=2;
161 
162  buffer_item_type* new_array = allocator_type().allocate(new_size);
163 
164  // initialize validity to "no"
165  for( size_type i=0; i<new_size; ++i ) { new_array[i].second = no_item; }
166 
167  for( size_type i=my_head; i<my_tail; ++i) {
168  if(my_item_valid(i)) { // sequencer_node may have empty slots
169  // placement-new copy-construct; could be std::move
170  char *new_space = (char *)&(new_array[i&(new_size-1)].first);
171  (void)new(new_space) item_type(get_my_item(i));
172  new_array[i&(new_size-1)].second = item(i).second;
173  }
174  }
175 
176  clean_up_buffer(/*reset_pointers*/false);
177 
178  my_array = new_array;
179  my_array_size = new_size;
180  }
181 
182  bool push_back(item_type &v) {
183  if(buffer_full()) {
184  grow_my_array(size() + 1);
185  }
186  set_my_item(my_tail, v);
187  ++my_tail;
188  return true;
189  }
190 
191  bool pop_back(item_type &v) {
192  if (!my_item_valid(my_tail-1)) {
193  return false;
194  }
195  v = this->back();
196  destroy_back();
197  return true;
198  }
199 
200  bool pop_front(item_type &v) {
201  if(!my_item_valid(my_head)) {
202  return false;
203  }
204  v = this->front();
205  destroy_front();
206  return true;
207  }
208 
209  // This is used both for reset and for grow_my_array. In the case of grow_my_array
210  // we want to retain the values of the head and tail.
211  void clean_up_buffer(bool reset_pointers) {
212  if (my_array) {
213  for( size_type i=my_head; i<my_tail; ++i ) {
214  if(my_item_valid(i))
215  destroy_item(i);
216  }
217  allocator_type().deallocate(my_array,my_array_size);
218  }
219  my_array = NULL;
220  if(reset_pointers) {
221  my_head = my_tail = my_array_size = 0;
222  }
223  }
224 
225  public:
227  item_buffer( ) : my_array(NULL), my_array_size(0),
228  my_head(0), my_tail(0) {
229  grow_my_array(initial_buffer_size);
230  }
231 
232  ~item_buffer() {
233  clean_up_buffer(/*reset_pointers*/true);
234  }
235 
236  void reset() { clean_up_buffer(/*reset_pointers*/true); grow_my_array(initial_buffer_size); }
237 
238  };
239 
241  //* complete operation with pop_front(); use consume_front().
242  //* No synchronization built-in.
243  template<typename T, typename A=cache_aligned_allocator<T> >
244  class reservable_item_buffer : public item_buffer<T, A> {
245  protected:
248 
249  public:
250  reservable_item_buffer() : item_buffer<T, A>(), my_reserved(false) {}
251  void reset() {my_reserved = false; item_buffer<T,A>::reset(); }
252  protected:
253 
254  bool reserve_front(T &v) {
255  if(my_reserved || !my_item_valid(this->my_head)) return false;
256  my_reserved = true;
257  // reserving the head
258  v = this->front();
259  this->reserve_item(this->my_head);
260  return true;
261  }
262 
263  void consume_front() {
264  __TBB_ASSERT(my_reserved, "Attempt to consume a non-reserved item");
265  this->destroy_front();
266  my_reserved = false;
267  }
268 
269  void release_front() {
270  __TBB_ASSERT(my_reserved, "Attempt to release a non-reserved item");
271  this->release_item(this->my_head);
272  my_reserved = false;
273  }
274 
275  bool my_reserved;
276  };
277 
278 } // namespace internal
279 
280 #endif // __TBB__flow_graph_item_buffer_impl_H
Definition: _flow_graph_types_impl.h:393
item_buffer with reservable front-end. NOTE: if reserving, do not
Definition: _flow_graph_item_buffer_impl.h:244
Definition: _flow_graph_types_impl.h:402
item_buffer()
Constructor.
Definition: _flow_graph_item_buffer_impl.h:227
type mimicking std::pair but with trailing fill to ensure each element of an array ...
Definition: _flow_graph_types_impl.h:381
Definition: _flow_graph_async_msg_impl.h:32
void grow_my_array(size_t minimum_size)
Grows the internal array.
Definition: _flow_graph_item_buffer_impl.h:155
Definition: _flow_graph_item_buffer_impl.h:44