Back to home page

EIC code displayed by LXR

 
 

    


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

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__aggregator_impl_H
0018 #define __TBB__aggregator_impl_H
0019 
0020 #include "../atomic.h"
0021 #if !__TBBMALLOC_BUILD
0022 #include "../tbb_profiling.h"
0023 #endif
0024 
0025 namespace tbb {
0026 namespace interface6 {
0027 namespace internal {
0028 
0029 using namespace tbb::internal;
0030 
0031 //! aggregated_operation base class
0032 template <typename Derived>
0033 class aggregated_operation {
0034  public:
0035     //! Zero value means "wait" status, all other values are "user" specified values and are defined into the scope of a class which uses "status".
0036     uintptr_t status;
0037 
0038     Derived *next;
0039     aggregated_operation() : status(0), next(NULL) {}
0040 };
0041 
0042 //! Aggregator base class
0043 /** An aggregator for collecting operations coming from multiple sources and executing
0044     them serially on a single thread.  operation_type must be derived from
0045     aggregated_operation. The parameter handler_type is a functor that will be passed the
0046     list of operations and is expected to handle each operation appropriately, setting the
0047     status of each operation to non-zero.*/
0048 template < typename operation_type >
0049 class aggregator_generic {
0050 public:
0051     aggregator_generic() : handler_busy(false) { pending_operations = NULL; }
0052 
0053     //! Execute an operation
0054     /** Places an operation into the waitlist (pending_operations), and either handles the list,
0055         or waits for the operation to complete, or returns.
0056         The long_life_time parameter specifies the life time of the given operation object.
0057         Operations with long_life_time == true may be accessed after execution.
0058         A "short" life time operation (long_life_time == false) can be destroyed
0059         during execution, and so any access to it after it was put into the waitlist,
0060         including status check, is invalid. As a consequence, waiting for completion
0061         of such operation causes undefined behavior.
0062     */
0063     template < typename handler_type >
0064     void execute(operation_type *op, handler_type &handle_operations, bool long_life_time = true) {
0065         operation_type *res;
0066         // op->status should be read before inserting the operation into the
0067         // aggregator waitlist since it can become invalid after executing a
0068         // handler (if the operation has 'short' life time.)
0069         const uintptr_t status = op->status;
0070 
0071         // ITT note: &(op->status) tag is used to cover accesses to this op node. This
0072         // thread has created the operation, and now releases it so that the handler
0073         // thread may handle the associated operation w/o triggering a race condition;
0074         // thus this tag will be acquired just before the operation is handled in the
0075         // handle_operations functor.
0076         call_itt_notify(releasing, &(op->status));
0077         // insert the operation in the queue.
0078         do {
0079             // Tools may flag the following line as a race; it is a false positive:
0080             // This is an atomic read; we don't provide itt_hide_load_word for atomics
0081             op->next = res = pending_operations; // NOT A RACE
0082         } while (pending_operations.compare_and_swap(op, res) != res);
0083         if (!res) { // first in the list; handle the operations.
0084             // ITT note: &pending_operations tag covers access to the handler_busy flag,
0085             // which this waiting handler thread will try to set before entering
0086             // handle_operations.
0087             call_itt_notify(acquired, &pending_operations);
0088             start_handle_operations(handle_operations);
0089             // The operation with 'short' life time can already be destroyed.
0090             if (long_life_time)
0091                 __TBB_ASSERT(op->status, NULL);
0092         }
0093         // not first; wait for op to be ready.
0094         else if (!status) { // operation is blocking here.
0095             __TBB_ASSERT(long_life_time, "Waiting for an operation object that might be destroyed during processing.");
0096             call_itt_notify(prepare, &(op->status));
0097             spin_wait_while_eq(op->status, uintptr_t(0));
0098             itt_load_word_with_acquire(op->status);
0099         }
0100     }
0101 
0102  private:
0103     //! An atomically updated list (aka mailbox) of pending operations
0104     atomic<operation_type *> pending_operations;
0105     //! Controls thread access to handle_operations
0106     uintptr_t handler_busy;
0107 
0108     //! Trigger the handling of operations when the handler is free
0109     template < typename handler_type >
0110     void start_handle_operations( handler_type &handle_operations ) {
0111         operation_type *op_list;
0112 
0113         // ITT note: &handler_busy tag covers access to pending_operations as it is passed
0114         // between active and waiting handlers.  Below, the waiting handler waits until
0115         // the active handler releases, and the waiting handler acquires &handler_busy as
0116         // it becomes the active_handler. The release point is at the end of this
0117         // function, when all operations in pending_operations have been handled by the
0118         // owner of this aggregator.
0119         call_itt_notify(prepare, &handler_busy);
0120         // get the handler_busy:
0121         // only one thread can possibly spin here at a time
0122         spin_wait_until_eq(handler_busy, uintptr_t(0));
0123         call_itt_notify(acquired, &handler_busy);
0124         // acquire fence not necessary here due to causality rule and surrounding atomics
0125         __TBB_store_with_release(handler_busy, uintptr_t(1));
0126 
0127         // ITT note: &pending_operations tag covers access to the handler_busy flag
0128         // itself. Capturing the state of the pending_operations signifies that
0129         // handler_busy has been set and a new active handler will now process that list's
0130         // operations.
0131         call_itt_notify(releasing, &pending_operations);
0132         // grab pending_operations
0133         op_list = pending_operations.fetch_and_store(NULL);
0134 
0135         // handle all the operations
0136         handle_operations(op_list);
0137 
0138         // release the handler
0139         itt_store_word_with_release(handler_busy, uintptr_t(0));
0140     }
0141 };
0142 
0143 template < typename handler_type, typename operation_type >
0144 class aggregator : public aggregator_generic<operation_type> {
0145     handler_type handle_operations;
0146 public:
0147     aggregator() {}
0148     explicit aggregator(handler_type h) : handle_operations(h) {}
0149 
0150     void initialize_handler(handler_type h) { handle_operations = h; }
0151 
0152     void execute(operation_type *op) {
0153         aggregator_generic<operation_type>::execute(op, handle_operations);
0154     }
0155 };
0156 
0157 // the most-compatible friend declaration (vs, gcc, icc) is
0158 //    template<class U, class V> friend class aggregating_functor;
0159 template<typename aggregating_class, typename operation_list>
0160 class aggregating_functor {
0161     aggregating_class *fi;
0162 public:
0163     aggregating_functor() : fi() {}
0164     aggregating_functor(aggregating_class *fi_) : fi(fi_) {}
0165     void operator()(operation_list* op_list) { fi->handle_operations(op_list); }
0166 };
0167 
0168 } // namespace internal
0169 } // namespace interface6
0170 
0171 namespace internal {
0172     using interface6::internal::aggregated_operation;
0173     using interface6::internal::aggregator_generic;
0174     using interface6::internal::aggregator;
0175     using interface6::internal::aggregating_functor;
0176 } // namespace internal
0177 
0178 } // namespace tbb
0179 
0180 #endif  // __TBB__aggregator_impl_H