Home ⌂Doc Index ◂Up ▴
Intel(R) Threading Building Blocks Doxygen Documentation  version 4.2.3
_flow_graph_item_buffer_impl.h
Go to the documentation of this file.
1 /*
2  Copyright (c) 2005-2020 Intel Corporation
3 
4  Licensed under the Apache License, Version 2.0 (the "License");
5  you may not use this file except in compliance with the License.
6  You may obtain a copy of the License at
7 
8  http://www.apache.org/licenses/LICENSE-2.0
9 
10  Unless required by applicable law or agreed to in writing, software
11  distributed under the License is distributed on an "AS IS" BASIS,
12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  See the License for the specific language governing permissions and
14  limitations under the License.
15 */
16 
17 #ifndef __TBB__flow_graph_item_buffer_impl_H
18 #define __TBB__flow_graph_item_buffer_impl_H
19 
20 #ifndef __TBB_flow_graph_H
21 #error Do not #include this internal file directly; use public TBB headers instead.
22 #endif
23 
24 #include "tbb/internal/_flow_graph_types_impl.h" // for aligned_pair
25 
26 // in namespace tbb::flow::interfaceX (included in _flow_graph_node_impl.h)
27 
29  //* tests for empty and so forth. No mutual exclusion is built in.
30  //* objects are constructed into and explicitly-destroyed. get_my_item gives
31  // a read-only reference to the item in the buffer. set_my_item may be called
32  // with either an empty or occupied slot.
33 
36 
37 namespace internal {
38 
39  template <typename T, typename A=cache_aligned_allocator<T> >
40  class item_buffer {
41  public:
42  typedef T item_type;
44  protected:
45  typedef size_t size_type;
50  static const size_type initial_buffer_size = 4;
53 
54  bool buffer_empty() const { return my_head == my_tail; }
55 
59  return my_array[i & (my_array_size - 1) ];
60  }
61 
62  const buffer_item_type &item(size_type i) const {
65  return my_array[i & (my_array_size-1)];
66  }
67 
68  bool my_item_valid(size_type i) const { return (i < my_tail) && (i >= my_head) && (item(i).second != no_item); }
69  bool my_item_reserved(size_type i) const { return item(i).second == reserved_item; }
70 
71  // object management in buffer
72  const item_type &get_my_item(size_t i) const {
73  __TBB_ASSERT(my_item_valid(i),"attempt to get invalid item");
74  item_type *itm = (tbb::internal::punned_cast<item_type *>(&(item(i).first)));
75  return *(const item_type *)itm;
76  }
77 
78  // may be called with an empty slot or a slot that has already been constructed into.
79  void set_my_item(size_t i, const item_type &o) {
80  if(item(i).second != no_item) {
81  destroy_item(i);
82  }
83  new(&(item(i).first)) item_type(o);
84  item(i).second = has_item;
85  }
86 
87  // destructively-fetch an object from the buffer
88  void fetch_item(size_t i, item_type &o) {
89  __TBB_ASSERT(my_item_valid(i), "Trying to fetch an empty slot");
90  o = get_my_item(i); // could have std::move assign semantics
91  destroy_item(i);
92  }
93 
94  // move an existing item from one slot to another. The moved-to slot must be unoccupied,
95  // the moved-from slot must exist and not be reserved. The after, from will be empty,
96  // to will be occupied but not reserved
97  void move_item(size_t to, size_t from) {
98  __TBB_ASSERT(!my_item_valid(to), "Trying to move to a non-empty slot");
99  __TBB_ASSERT(my_item_valid(from), "Trying to move from an empty slot");
100  set_my_item(to, get_my_item(from)); // could have std::move semantics
101  destroy_item(from);
102 
103  }
104 
105  // put an item in an empty slot. Return true if successful, else false
106  bool place_item(size_t here, const item_type &me) {
107 #if !TBB_DEPRECATED_SEQUENCER_DUPLICATES
108  if(my_item_valid(here)) return false;
109 #endif
110  set_my_item(here, me);
111  return true;
112  }
113 
114  // could be implemented with std::move semantics
115  void swap_items(size_t i, size_t j) {
116  __TBB_ASSERT(my_item_valid(i) && my_item_valid(j), "attempt to swap invalid item(s)");
117  item_type temp = get_my_item(i);
118  set_my_item(i, get_my_item(j));
119  set_my_item(j, temp);
120  }
121 
123  __TBB_ASSERT(my_item_valid(i), "destruction of invalid item");
124  (tbb::internal::punned_cast<item_type *>(&(item(i).first)))->~item_type();
125  item(i).second = no_item;
126  }
127 
128  // returns the front element
129  const item_type& front() const
130  {
131  __TBB_ASSERT(my_item_valid(my_head), "attempt to fetch head non-item");
132  return get_my_item(my_head);
133  }
134 
135  // returns the back element
136  const item_type& back() const
137  {
138  __TBB_ASSERT(my_item_valid(my_tail - 1), "attempt to fetch head non-item");
139  return get_my_item(my_tail - 1);
140  }
141 
142  // following methods are for reservation of the front of a buffer.
143  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; }
144  void release_item(size_type i) { __TBB_ASSERT(my_item_reserved(i), "item is not reserved"); item(i).second = has_item; }
145 
148 
149  // we have to be able to test against a new tail value without changing my_tail
150  // grow_array doesn't work if we change my_tail when the old array is too small
151  size_type size(size_t new_tail = 0) { return (new_tail ? new_tail : my_tail) - my_head; }
153  // sequencer_node does not use this method, so we don't
154  // need a version that passes in the new_tail value.
155  bool buffer_full() { return size() >= capacity(); }
156 
158  void grow_my_array( size_t minimum_size ) {
159  // test that we haven't made the structure inconsistent.
160  __TBB_ASSERT(capacity() >= my_tail - my_head, "total items exceed capacity");
162  while( new_size<minimum_size )
163  new_size*=2;
164 
165  buffer_item_type* new_array = allocator_type().allocate(new_size);
166 
167  // initialize validity to "no"
168  for( size_type i=0; i<new_size; ++i ) { new_array[i].second = no_item; }
169 
170  for( size_type i=my_head; i<my_tail; ++i) {
171  if(my_item_valid(i)) { // sequencer_node may have empty slots
172  // placement-new copy-construct; could be std::move
173  char *new_space = (char *)&(new_array[i&(new_size-1)].first);
174  (void)new(new_space) item_type(get_my_item(i));
175  new_array[i&(new_size-1)].second = item(i).second;
176  }
177  }
178 
179  clean_up_buffer(/*reset_pointers*/false);
180 
181  my_array = new_array;
183  }
184 
185  bool push_back(item_type &v) {
186  if(buffer_full()) {
187  grow_my_array(size() + 1);
188  }
189  set_my_item(my_tail, v);
190  ++my_tail;
191  return true;
192  }
193 
194  bool pop_back(item_type &v) {
195  if (!my_item_valid(my_tail-1)) {
196  return false;
197  }
198  v = this->back();
199  destroy_back();
200  return true;
201  }
202 
203  bool pop_front(item_type &v) {
204  if(!my_item_valid(my_head)) {
205  return false;
206  }
207  v = this->front();
208  destroy_front();
209  return true;
210  }
211 
212  // This is used both for reset and for grow_my_array. In the case of grow_my_array
213  // we want to retain the values of the head and tail.
214  void clean_up_buffer(bool reset_pointers) {
215  if (my_array) {
216  for( size_type i=my_head; i<my_tail; ++i ) {
217  if(my_item_valid(i))
218  destroy_item(i);
219  }
220  allocator_type().deallocate(my_array,my_array_size);
221  }
222  my_array = NULL;
223  if(reset_pointers) {
224  my_head = my_tail = my_array_size = 0;
225  }
226  }
227 
228  public:
231  my_head(0), my_tail(0) {
233  }
234 
236  clean_up_buffer(/*reset_pointers*/true);
237  }
238 
239  void reset() { clean_up_buffer(/*reset_pointers*/true); grow_my_array(initial_buffer_size); }
240 
241  };
242 
244  //* complete operation with pop_front(); use consume_front().
245  //* No synchronization built-in.
246  template<typename T, typename A=cache_aligned_allocator<T> >
247  class reservable_item_buffer : public item_buffer<T, A> {
248  protected:
251 
252  public:
255  protected:
256 
257  bool reserve_front(T &v) {
258  if(my_reserved || !my_item_valid(this->my_head)) return false;
259  my_reserved = true;
260  // reserving the head
261  v = this->front();
262  this->reserve_item(this->my_head);
263  return true;
264  }
265 
266  void consume_front() {
267  __TBB_ASSERT(my_reserved, "Attempt to consume a non-reserved item");
268  this->destroy_front();
269  my_reserved = false;
270  }
271 
272  void release_front() {
273  __TBB_ASSERT(my_reserved, "Attempt to release a non-reserved item");
274  this->release_item(this->my_head);
275  my_reserved = false;
276  }
277 
279  };
280 
281 } // namespace internal
282 
283 #endif // __TBB__flow_graph_item_buffer_impl_H
void move_item(size_t to, size_t from)
size_type size(size_t new_tail=0)
const item_type & get_my_item(size_t i) const
aligned_pair< item_type, buffer_item_state >::type buffer_item_type
const item_type & front() const
void grow_my_array(size_t minimum_size)
Grows the internal array.
item_buffer with reservable front-end. NOTE: if reserving, do not
void const char const char int ITT_FORMAT __itt_group_sync x void const char ITT_FORMAT __itt_group_sync s void ITT_FORMAT __itt_group_sync p void ITT_FORMAT p void ITT_FORMAT p no args __itt_suppress_mode_t unsigned int void size_t ITT_FORMAT d void ITT_FORMAT p void ITT_FORMAT p __itt_model_site __itt_model_site_instance ITT_FORMAT p __itt_model_task __itt_model_task_instance ITT_FORMAT p void ITT_FORMAT p void ITT_FORMAT p void size_t ITT_FORMAT d void ITT_FORMAT p const wchar_t ITT_FORMAT s const char ITT_FORMAT s const char ITT_FORMAT s const char ITT_FORMAT s no args void ITT_FORMAT p size_t ITT_FORMAT d no args const wchar_t const wchar_t ITT_FORMAT s __itt_heap_function void size_t int ITT_FORMAT d __itt_heap_function void ITT_FORMAT p __itt_heap_function void void size_t new_size
static const size_type initial_buffer_size
const item_type & back() const
type mimicking std::pair but with trailing fill to ensure each element of an array
bool my_item_reserved(size_type i) const
allocator_traits< Alloc >::template rebind_alloc< T >::other type
void fetch_item(size_t i, item_type &o)
buffer_item_type & item(size_type i)
void set_my_item(size_t i, const item_type &o)
tbb::internal::allocator_rebind< A, buffer_item_type >::type allocator_type
void swap_items(size_t i, size_t j)
bool my_item_valid(size_type i) const
auto first(Container &c) -> decltype(begin(c))
const buffer_item_type & item(size_type i) const
#define __TBB_ASSERT(predicate, comment)
No-op version of __TBB_ASSERT.
Definition: tbb_stddef.h:165
bool place_item(size_t here, const item_type &me)
void clean_up_buffer(bool reset_pointers)

Copyright © 2005-2020 Intel Corporation. All Rights Reserved.

Intel, Pentium, Intel Xeon, Itanium, Intel XScale and VTune are registered trademarks or trademarks of Intel Corporation or its subsidiaries in the United States and other countries.

* Other names and brands may be claimed as the property of others.