Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2025-07-30 08:46:16

0001 /*
0002     Copyright (c) 2005-2022 Intel Corporation
0003 
0004     Licensed under the Apache License, Version 2.0 (the "License");
0005     you may not use this file except in compliance with the License.
0006     You may obtain a copy of the License at
0007 
0008         http://www.apache.org/licenses/LICENSE-2.0
0009 
0010     Unless required by applicable law or agreed to in writing, software
0011     distributed under the License is distributed on an "AS IS" BASIS,
0012     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
0013     See the License for the specific language governing permissions and
0014     limitations under the License.
0015 */
0016 
0017 #ifndef __TBB__flow_graph_item_buffer_impl_H
0018 #define __TBB__flow_graph_item_buffer_impl_H
0019 
0020 #ifndef __TBB_flow_graph_H
0021 #error Do not #include this internal file directly; use public TBB headers instead.
0022 #endif
0023 
0024 #include "_aligned_space.h"
0025 
0026 // in namespace tbb::flow::interfaceX (included in _flow_graph_node_impl.h)
0027 
0028 //! Expandable buffer of items.  The possible operations are push, pop,
0029 //* tests for empty and so forth.  No mutual exclusion is built in.
0030 //* objects are constructed into and explicitly-destroyed.  get_my_item gives
0031 // a read-only reference to the item in the buffer.  set_my_item may be called
0032 // with either an empty or occupied slot.
0033 
0034 template <typename T, typename A=cache_aligned_allocator<T> >
0035 class item_buffer {
0036 public:
0037     typedef T item_type;
0038     enum buffer_item_state { no_item=0, has_item=1, reserved_item=2 };
0039 protected:
0040     typedef size_t size_type;
0041     typedef std::pair<item_type, buffer_item_state> aligned_space_item;
0042     typedef aligned_space<aligned_space_item> buffer_item_type;
0043     typedef typename allocator_traits<A>::template rebind_alloc<buffer_item_type> allocator_type;
0044     buffer_item_type *my_array;
0045     size_type my_array_size;
0046     static const size_type initial_buffer_size = 4;
0047     size_type my_head;
0048     size_type my_tail;
0049 
0050     bool buffer_empty() const { return my_head == my_tail; }
0051 
0052     aligned_space_item &item(size_type i) {
0053         __TBB_ASSERT(!(size_type(&(my_array[i&(my_array_size-1)].begin()->second))%alignment_of<buffer_item_state>::value), nullptr);
0054         __TBB_ASSERT(!(size_type(&(my_array[i&(my_array_size-1)].begin()->first))%alignment_of<item_type>::value), nullptr);
0055         return *my_array[i & (my_array_size - 1) ].begin();
0056     }
0057 
0058     const aligned_space_item &item(size_type i) const {
0059         __TBB_ASSERT(!(size_type(&(my_array[i&(my_array_size-1)].begin()->second))%alignment_of<buffer_item_state>::value), nullptr);
0060         __TBB_ASSERT(!(size_type(&(my_array[i&(my_array_size-1)].begin()->first))%alignment_of<item_type>::value), nullptr);
0061         return *my_array[i & (my_array_size-1)].begin();
0062     }
0063 
0064     bool my_item_valid(size_type i) const { return (i < my_tail) && (i >= my_head) && (item(i).second != no_item); }
0065 #if TBB_USE_ASSERT
0066     bool my_item_reserved(size_type i) const { return item(i).second == reserved_item; }
0067 #endif
0068 
0069     // object management in buffer
0070     const item_type &get_my_item(size_t i) const {
0071         __TBB_ASSERT(my_item_valid(i),"attempt to get invalid item");
0072         item_type* itm = const_cast<item_type*>(reinterpret_cast<const item_type*>(&item(i).first));
0073         return *itm;
0074     }
0075 
0076     // may be called with an empty slot or a slot that has already been constructed into.
0077     void set_my_item(size_t i, const item_type &o) {
0078         if(item(i).second != no_item) {
0079             destroy_item(i);
0080         }
0081         new(&(item(i).first)) item_type(o);
0082         item(i).second = has_item;
0083     }
0084 
0085     // destructively-fetch an object from the buffer
0086     void fetch_item(size_t i, item_type &o) {
0087         __TBB_ASSERT(my_item_valid(i), "Trying to fetch an empty slot");
0088         o = get_my_item(i);  // could have std::move assign semantics
0089         destroy_item(i);
0090     }
0091 
0092     // move an existing item from one slot to another.  The moved-to slot must be unoccupied,
0093     // the moved-from slot must exist and not be reserved.  The after, from will be empty,
0094     // to will be occupied but not reserved
0095     void move_item(size_t to, size_t from) {
0096         __TBB_ASSERT(!my_item_valid(to), "Trying to move to a non-empty slot");
0097         __TBB_ASSERT(my_item_valid(from), "Trying to move from an empty slot");
0098         set_my_item(to, get_my_item(from));   // could have std::move semantics
0099         destroy_item(from);
0100 
0101     }
0102 
0103     // put an item in an empty slot.  Return true if successful, else false
0104     bool place_item(size_t here, const item_type &me) {
0105 #if !TBB_DEPRECATED_SEQUENCER_DUPLICATES
0106         if(my_item_valid(here)) return false;
0107 #endif
0108         set_my_item(here, me);
0109         return true;
0110     }
0111 
0112     // could be implemented with std::move semantics
0113     void swap_items(size_t i, size_t j) {
0114         __TBB_ASSERT(my_item_valid(i) && my_item_valid(j), "attempt to swap invalid item(s)");
0115         item_type temp = get_my_item(i);
0116         set_my_item(i, get_my_item(j));
0117         set_my_item(j, temp);
0118     }
0119 
0120     void destroy_item(size_type i) {
0121         __TBB_ASSERT(my_item_valid(i), "destruction of invalid item");
0122         item(i).first.~item_type();
0123         item(i).second = no_item;
0124     }
0125 
0126     // returns the front element
0127     const item_type& front() const
0128     {
0129         __TBB_ASSERT(my_item_valid(my_head), "attempt to fetch head non-item");
0130         return get_my_item(my_head);
0131     }
0132 
0133     // returns  the back element
0134     const item_type& back() const
0135     {
0136         __TBB_ASSERT(my_item_valid(my_tail - 1), "attempt to fetch head non-item");
0137         return get_my_item(my_tail - 1);
0138     }
0139 
0140     // following methods are for reservation of the front of a buffer.
0141     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; }
0142     void release_item(size_type i) { __TBB_ASSERT(my_item_reserved(i), "item is not reserved"); item(i).second = has_item; }
0143 
0144     void destroy_front() { destroy_item(my_head); ++my_head; }
0145     void destroy_back() { destroy_item(my_tail-1); --my_tail; }
0146 
0147     // we have to be able to test against a new tail value without changing my_tail
0148     // grow_array doesn't work if we change my_tail when the old array is too small
0149     size_type size(size_t new_tail = 0) { return (new_tail ? new_tail : my_tail) - my_head; }
0150     size_type capacity() { return my_array_size; }
0151     // sequencer_node does not use this method, so we don't
0152     // need a version that passes in the new_tail value.
0153     bool buffer_full() { return size() >= capacity(); }
0154 
0155     //! Grows the internal array.
0156     void grow_my_array( size_t minimum_size ) {
0157         // test that we haven't made the structure inconsistent.
0158         __TBB_ASSERT(capacity() >= my_tail - my_head, "total items exceed capacity");
0159         size_type new_size = my_array_size ? 2*my_array_size : initial_buffer_size;
0160         while( new_size<minimum_size )
0161             new_size*=2;
0162 
0163         buffer_item_type* new_array = allocator_type().allocate(new_size);
0164 
0165         // initialize validity to "no"
0166         for( size_type i=0; i<new_size; ++i ) { new_array[i].begin()->second = no_item; }
0167 
0168         for( size_type i=my_head; i<my_tail; ++i) {
0169             if(my_item_valid(i)) {  // sequencer_node may have empty slots
0170                 // placement-new copy-construct; could be std::move
0171                 char *new_space = (char *)&(new_array[i&(new_size-1)].begin()->first);
0172                 (void)new(new_space) item_type(get_my_item(i));
0173                 new_array[i&(new_size-1)].begin()->second = item(i).second;
0174             }
0175         }
0176 
0177         clean_up_buffer(/*reset_pointers*/false);
0178 
0179         my_array = new_array;
0180         my_array_size = new_size;
0181     }
0182 
0183     bool push_back(item_type &v) {
0184         if(buffer_full()) {
0185             grow_my_array(size() + 1);
0186         }
0187         set_my_item(my_tail, v);
0188         ++my_tail;
0189         return true;
0190     }
0191 
0192     bool pop_back(item_type &v) {
0193         if (!my_item_valid(my_tail-1)) {
0194             return false;
0195         }
0196         v = this->back();
0197         destroy_back();
0198         return true;
0199     }
0200 
0201     bool pop_front(item_type &v) {
0202         if(!my_item_valid(my_head)) {
0203             return false;
0204         }
0205         v = this->front();
0206         destroy_front();
0207         return true;
0208     }
0209 
0210     // This is used both for reset and for grow_my_array.  In the case of grow_my_array
0211     // we want to retain the values of the head and tail.
0212     void clean_up_buffer(bool reset_pointers) {
0213         if (my_array) {
0214             for( size_type i=my_head; i<my_tail; ++i ) {
0215                 if(my_item_valid(i))
0216                     destroy_item(i);
0217             }
0218             allocator_type().deallocate(my_array,my_array_size);
0219         }
0220         my_array = nullptr;
0221         if(reset_pointers) {
0222             my_head = my_tail = my_array_size = 0;
0223         }
0224     }
0225 
0226 public:
0227     //! Constructor
0228     item_buffer( ) : my_array(nullptr), my_array_size(0),
0229                      my_head(0), my_tail(0) {
0230         grow_my_array(initial_buffer_size);
0231     }
0232 
0233     ~item_buffer() {
0234         clean_up_buffer(/*reset_pointers*/true);
0235     }
0236 
0237     void reset() { clean_up_buffer(/*reset_pointers*/true); grow_my_array(initial_buffer_size); }
0238 
0239 };
0240 
0241 //! item_buffer with reservable front-end.  NOTE: if reserving, do not
0242 //* complete operation with pop_front(); use consume_front().
0243 //* No synchronization built-in.
0244 template<typename T, typename A=cache_aligned_allocator<T> >
0245 class reservable_item_buffer : public item_buffer<T, A> {
0246 protected:
0247     using item_buffer<T, A>::my_item_valid;
0248     using item_buffer<T, A>::my_head;
0249 
0250 public:
0251     reservable_item_buffer() : item_buffer<T, A>(), my_reserved(false) {}
0252     void reset() {my_reserved = false; item_buffer<T,A>::reset(); }
0253 protected:
0254 
0255     bool reserve_front(T &v) {
0256         if(my_reserved || !my_item_valid(this->my_head)) return false;
0257         my_reserved = true;
0258         // reserving the head
0259         v = this->front();
0260         this->reserve_item(this->my_head);
0261         return true;
0262     }
0263 
0264     void consume_front() {
0265         __TBB_ASSERT(my_reserved, "Attempt to consume a non-reserved item");
0266         this->destroy_front();
0267         my_reserved = false;
0268     }
0269 
0270     void release_front() {
0271         __TBB_ASSERT(my_reserved, "Attempt to release a non-reserved item");
0272         this->release_item(this->my_head);
0273         my_reserved = false;
0274     }
0275 
0276     bool my_reserved;
0277 };
0278 
0279 #endif // __TBB__flow_graph_item_buffer_impl_H