Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2025-01-18 10:12:47

0001 /*
0002     Copyright (c) 2005-2020 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 "tbb/internal/_flow_graph_types_impl.h"  // for aligned_pair
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     using internal::aligned_pair;
0035     using internal::alignment_of;
0036 
0037 namespace internal {
0038 
0039     template <typename T, typename A=cache_aligned_allocator<T> >
0040     class item_buffer {
0041     public:
0042         typedef T item_type;
0043         enum buffer_item_state { no_item=0, has_item=1, reserved_item=2 };
0044     protected:
0045         typedef size_t size_type;
0046         typedef typename aligned_pair<item_type, buffer_item_state>::type buffer_item_type;
0047         typedef typename tbb::internal::allocator_rebind<A, buffer_item_type>::type allocator_type;
0048         buffer_item_type *my_array;
0049         size_type my_array_size;
0050         static const size_type initial_buffer_size = 4;
0051         size_type my_head;
0052         size_type my_tail;
0053 
0054         bool buffer_empty() const { return my_head == my_tail; }
0055 
0056         buffer_item_type &item(size_type i) {
0057             __TBB_ASSERT(!(size_type(&(my_array[i&(my_array_size-1)].second))%alignment_of<buffer_item_state>::value),NULL);
0058             __TBB_ASSERT(!(size_type(&(my_array[i&(my_array_size-1)].first))%alignment_of<item_type>::value), NULL);
0059             return my_array[i & (my_array_size - 1) ];
0060         }
0061 
0062         const buffer_item_type &item(size_type i) const {
0063             __TBB_ASSERT(!(size_type(&(my_array[i&(my_array_size-1)].second))%alignment_of<buffer_item_state>::value), NULL);
0064             __TBB_ASSERT(!(size_type(&(my_array[i&(my_array_size-1)].first))%alignment_of<item_type>::value), NULL);
0065             return my_array[i & (my_array_size-1)];
0066         }
0067 
0068         bool my_item_valid(size_type i) const { return (i < my_tail) && (i >= my_head) && (item(i).second != no_item); }
0069         bool my_item_reserved(size_type i) const { return item(i).second == reserved_item; }
0070 
0071         // object management in buffer
0072         const item_type &get_my_item(size_t i) const {
0073             __TBB_ASSERT(my_item_valid(i),"attempt to get invalid item");
0074             item_type *itm = (tbb::internal::punned_cast<item_type *>(&(item(i).first)));
0075             return *(const item_type *)itm;
0076         }
0077 
0078         // may be called with an empty slot or a slot that has already been constructed into.
0079         void set_my_item(size_t i, const item_type &o) {
0080             if(item(i).second != no_item) {
0081                 destroy_item(i);
0082             }
0083             new(&(item(i).first)) item_type(o);
0084             item(i).second = has_item;
0085         }
0086 
0087         // destructively-fetch an object from the buffer
0088         void fetch_item(size_t i, item_type &o) {
0089             __TBB_ASSERT(my_item_valid(i), "Trying to fetch an empty slot");
0090             o = get_my_item(i);  // could have std::move assign semantics
0091             destroy_item(i);
0092         }
0093 
0094         // move an existing item from one slot to another.  The moved-to slot must be unoccupied,
0095         // the moved-from slot must exist and not be reserved.  The after, from will be empty,
0096         // to will be occupied but not reserved
0097         void move_item(size_t to, size_t from) {
0098             __TBB_ASSERT(!my_item_valid(to), "Trying to move to a non-empty slot");
0099             __TBB_ASSERT(my_item_valid(from), "Trying to move from an empty slot");
0100             set_my_item(to, get_my_item(from));   // could have std::move semantics
0101             destroy_item(from);
0102 
0103         }
0104 
0105         // put an item in an empty slot.  Return true if successful, else false
0106         bool place_item(size_t here, const item_type &me) {
0107 #if !TBB_DEPRECATED_SEQUENCER_DUPLICATES
0108             if(my_item_valid(here)) return false;
0109 #endif
0110             set_my_item(here, me);
0111             return true;
0112         }
0113 
0114         // could be implemented with std::move semantics
0115         void swap_items(size_t i, size_t j) {
0116             __TBB_ASSERT(my_item_valid(i) && my_item_valid(j), "attempt to swap invalid item(s)");
0117             item_type temp = get_my_item(i);
0118             set_my_item(i, get_my_item(j));
0119             set_my_item(j, temp);
0120         }
0121 
0122         void destroy_item(size_type i) {
0123             __TBB_ASSERT(my_item_valid(i), "destruction of invalid item");
0124             (tbb::internal::punned_cast<item_type *>(&(item(i).first)))->~item_type();
0125             item(i).second = no_item;
0126         }
0127 
0128         // returns the front element
0129         const item_type& front() const
0130         {
0131             __TBB_ASSERT(my_item_valid(my_head), "attempt to fetch head non-item");
0132             return get_my_item(my_head);
0133         }
0134 
0135         // returns  the back element
0136         const item_type& back() const
0137         {
0138             __TBB_ASSERT(my_item_valid(my_tail - 1), "attempt to fetch head non-item");
0139             return get_my_item(my_tail - 1);
0140         }
0141 
0142         // following methods are for reservation of the front of a buffer.
0143         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; }
0144         void release_item(size_type i) { __TBB_ASSERT(my_item_reserved(i), "item is not reserved"); item(i).second = has_item; }
0145 
0146         void destroy_front() { destroy_item(my_head); ++my_head; }
0147         void destroy_back() { destroy_item(my_tail-1); --my_tail; }
0148 
0149         // we have to be able to test against a new tail value without changing my_tail
0150         // grow_array doesn't work if we change my_tail when the old array is too small
0151         size_type size(size_t new_tail = 0) { return (new_tail ? new_tail : my_tail) - my_head; }
0152         size_type capacity() { return my_array_size; }
0153         // sequencer_node does not use this method, so we don't
0154         // need a version that passes in the new_tail value.
0155         bool buffer_full() { return size() >= capacity(); }
0156 
0157         //! Grows the internal array.
0158         void grow_my_array( size_t minimum_size ) {
0159             // test that we haven't made the structure inconsistent.
0160             __TBB_ASSERT(capacity() >= my_tail - my_head, "total items exceed capacity");
0161             size_type new_size = my_array_size ? 2*my_array_size : initial_buffer_size;
0162             while( new_size<minimum_size )
0163                 new_size*=2;
0164 
0165             buffer_item_type* new_array = allocator_type().allocate(new_size);
0166 
0167             // initialize validity to "no"
0168             for( size_type i=0; i<new_size; ++i ) { new_array[i].second = no_item; }
0169 
0170             for( size_type i=my_head; i<my_tail; ++i) {
0171                 if(my_item_valid(i)) {  // sequencer_node may have empty slots
0172                     // placement-new copy-construct; could be std::move
0173                     char *new_space = (char *)&(new_array[i&(new_size-1)].first);
0174                     (void)new(new_space) item_type(get_my_item(i));
0175                     new_array[i&(new_size-1)].second = item(i).second;
0176                 }
0177             }
0178 
0179             clean_up_buffer(/*reset_pointers*/false);
0180 
0181             my_array = new_array;
0182             my_array_size = new_size;
0183         }
0184 
0185         bool push_back(item_type &v) {
0186             if(buffer_full()) {
0187                 grow_my_array(size() + 1);
0188             }
0189             set_my_item(my_tail, v);
0190             ++my_tail;
0191             return true;
0192         }
0193 
0194         bool pop_back(item_type &v) {
0195             if (!my_item_valid(my_tail-1)) {
0196                 return false;
0197             }
0198             v = this->back();
0199             destroy_back();
0200             return true;
0201         }
0202 
0203         bool pop_front(item_type &v) {
0204             if(!my_item_valid(my_head)) {
0205                 return false;
0206             }
0207             v = this->front();
0208             destroy_front();
0209             return true;
0210         }
0211 
0212         // This is used both for reset and for grow_my_array.  In the case of grow_my_array
0213         // we want to retain the values of the head and tail.
0214         void clean_up_buffer(bool reset_pointers) {
0215             if (my_array) {
0216                 for( size_type i=my_head; i<my_tail; ++i ) {
0217                     if(my_item_valid(i))
0218                         destroy_item(i);
0219                 }
0220                 allocator_type().deallocate(my_array,my_array_size);
0221             }
0222             my_array = NULL;
0223             if(reset_pointers) {
0224                 my_head = my_tail = my_array_size = 0;
0225             }
0226         }
0227 
0228     public:
0229         //! Constructor
0230         item_buffer( ) : my_array(NULL), my_array_size(0),
0231             my_head(0), my_tail(0) {
0232             grow_my_array(initial_buffer_size);
0233         }
0234 
0235         ~item_buffer() {
0236             clean_up_buffer(/*reset_pointers*/true);
0237         }
0238 
0239         void reset() { clean_up_buffer(/*reset_pointers*/true); grow_my_array(initial_buffer_size); }
0240 
0241     };
0242 
0243     //! item_buffer with reservable front-end.  NOTE: if reserving, do not
0244     //* complete operation with pop_front(); use consume_front().
0245     //* No synchronization built-in.
0246     template<typename T, typename A=cache_aligned_allocator<T> >
0247     class reservable_item_buffer : public item_buffer<T, A> {
0248     protected:
0249         using item_buffer<T, A>::my_item_valid;
0250         using item_buffer<T, A>::my_head;
0251 
0252     public:
0253         reservable_item_buffer() : item_buffer<T, A>(), my_reserved(false) {}
0254         void reset() {my_reserved = false; item_buffer<T,A>::reset(); }
0255     protected:
0256 
0257         bool reserve_front(T &v) {
0258             if(my_reserved || !my_item_valid(this->my_head)) return false;
0259             my_reserved = true;
0260             // reserving the head
0261             v = this->front();
0262             this->reserve_item(this->my_head);
0263             return true;
0264         }
0265 
0266         void consume_front() {
0267             __TBB_ASSERT(my_reserved, "Attempt to consume a non-reserved item");
0268             this->destroy_front();
0269             my_reserved = false;
0270         }
0271 
0272         void release_front() {
0273             __TBB_ASSERT(my_reserved, "Attempt to release a non-reserved item");
0274             this->release_item(this->my_head);
0275             my_reserved = false;
0276         }
0277 
0278         bool my_reserved;
0279     };
0280 
0281 }  // namespace internal
0282 
0283 #endif // __TBB__flow_graph_item_buffer_impl_H