|
||||
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
[ Source navigation ] | [ Diff markup ] | [ Identifier search ] | [ general search ] |
This page was automatically generated by the 2.3.7 LXR engine. The LXR team |