| 1 | /* |
| 2 | Copyright (c) 2005-2019 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_observer_proxy_H |
| 18 | #define _TBB_observer_proxy_H |
| 19 | |
| 20 | #if __TBB_SCHEDULER_OBSERVER |
| 21 | |
| 22 | #include "scheduler_common.h" // to include task.h |
| 23 | #include "tbb/task_scheduler_observer.h" |
| 24 | #include "tbb/spin_rw_mutex.h" |
| 25 | #include "tbb/aligned_space.h" |
| 26 | |
| 27 | namespace tbb { |
| 28 | namespace internal { |
| 29 | |
| 30 | class observer_list { |
| 31 | friend class arena; |
| 32 | |
| 33 | // Mutex is wrapped with aligned_space to shut up warnings when its destructor |
| 34 | // is called while threads are still using it. |
| 35 | typedef aligned_space<spin_rw_mutex> my_mutex_type; |
| 36 | |
| 37 | //! Pointer to the head of this list. |
| 38 | observer_proxy* my_head; |
| 39 | |
| 40 | //! Pointer to the tail of this list. |
| 41 | observer_proxy* my_tail; |
| 42 | |
| 43 | //! Mutex protecting this list. |
| 44 | my_mutex_type my_mutex; |
| 45 | |
| 46 | //! Back-pointer to the arena this list belongs to. |
| 47 | arena* my_arena; |
| 48 | |
| 49 | //! Decrement refcount of the proxy p if there are other outstanding references. |
| 50 | /** In case of success sets p to NULL. Must be invoked from under the list lock. **/ |
| 51 | inline static void remove_ref_fast( observer_proxy*& p ); |
| 52 | |
| 53 | //! Implements notify_entry_observers functionality. |
| 54 | void do_notify_entry_observers( observer_proxy*& last, bool worker ); |
| 55 | |
| 56 | //! Implements notify_exit_observers functionality. |
| 57 | void do_notify_exit_observers( observer_proxy* last, bool worker ); |
| 58 | |
| 59 | public: |
| 60 | observer_list () : my_head(NULL), my_tail(NULL) {} |
| 61 | |
| 62 | //! Removes and destroys all observer proxies from the list. |
| 63 | /** Cannot be used concurrently with other methods. **/ |
| 64 | void clear (); |
| 65 | |
| 66 | //! Add observer proxy to the tail of the list. |
| 67 | void insert ( observer_proxy* p ); |
| 68 | |
| 69 | //! Remove observer proxy from the list. |
| 70 | void remove ( observer_proxy* p ); |
| 71 | |
| 72 | //! Decrement refcount of the proxy and destroy it if necessary. |
| 73 | /** When refcount reaches zero removes the proxy from the list and destructs it. **/ |
| 74 | void remove_ref( observer_proxy* p ); |
| 75 | |
| 76 | //! Type of the scoped lock for the reader-writer mutex associated with the list. |
| 77 | typedef spin_rw_mutex::scoped_lock scoped_lock; |
| 78 | |
| 79 | //! Accessor to the reader-writer mutex associated with the list. |
| 80 | spin_rw_mutex& mutex () { return my_mutex.begin()[0]; } |
| 81 | |
| 82 | bool empty () const { return my_head == NULL; } |
| 83 | |
| 84 | //! Call entry notifications on observers added after last was notified. |
| 85 | /** Updates last to become the last notified observer proxy (in the global list) |
| 86 | or leaves it to be NULL. The proxy has its refcount incremented. **/ |
| 87 | inline void notify_entry_observers( observer_proxy*& last, bool worker ); |
| 88 | |
| 89 | //! Call exit notifications on last and observers added before it. |
| 90 | inline void notify_exit_observers( observer_proxy*& last, bool worker ); |
| 91 | }; // class observer_list |
| 92 | |
| 93 | //! Wrapper for an observer object |
| 94 | /** To maintain shared lists of observers the scheduler first wraps each observer |
| 95 | object into a proxy so that a list item remained valid even after the corresponding |
| 96 | proxy object is destroyed by the user code. **/ |
| 97 | class observer_proxy { |
| 98 | friend class task_scheduler_observer_v3; |
| 99 | friend class observer_list; |
| 100 | //! Reference count used for garbage collection. |
| 101 | /** 1 for reference from my task_scheduler_observer. |
| 102 | 1 for each task dispatcher's last observer pointer. |
| 103 | No accounting for neighbors in the shared list. */ |
| 104 | atomic<int> my_ref_count; |
| 105 | //! Reference to the list this observer belongs to. |
| 106 | observer_list* my_list; |
| 107 | //! Pointer to next observer in the list specified by my_head. |
| 108 | /** NULL for the last item in the list. **/ |
| 109 | observer_proxy* my_next; |
| 110 | //! Pointer to the previous observer in the list specified by my_head. |
| 111 | /** For the head of the list points to the last item. **/ |
| 112 | observer_proxy* my_prev; |
| 113 | //! Associated observer |
| 114 | task_scheduler_observer_v3* my_observer; |
| 115 | //! Version |
| 116 | char my_version; |
| 117 | |
| 118 | #if __TBB_ARENA_OBSERVER |
| 119 | interface6::task_scheduler_observer* get_v6_observer(); |
| 120 | #endif |
| 121 | #if __TBB_ARENA_OBSERVER |
| 122 | bool is_global(); //TODO: move them back inline when un-CPF'ing |
| 123 | #endif |
| 124 | |
| 125 | //! Constructs proxy for the given observer and adds it to the specified list. |
| 126 | observer_proxy( task_scheduler_observer_v3& ); |
| 127 | |
| 128 | #if TBB_USE_ASSERT |
| 129 | ~observer_proxy(); |
| 130 | #endif /* TBB_USE_ASSERT */ |
| 131 | |
| 132 | //! Shut up the warning |
| 133 | observer_proxy& operator = ( const observer_proxy& ); |
| 134 | }; // class observer_proxy |
| 135 | |
| 136 | inline void observer_list::remove_ref_fast( observer_proxy*& p ) { |
| 137 | if( p->my_observer ) { |
| 138 | // Can decrement refcount quickly, as it cannot drop to zero while under the lock. |
| 139 | int r = --p->my_ref_count; |
| 140 | __TBB_ASSERT_EX( r, NULL ); |
| 141 | p = NULL; |
| 142 | } else { |
| 143 | // Use slow form of refcount decrementing, after the lock is released. |
| 144 | } |
| 145 | } |
| 146 | |
| 147 | inline void observer_list::notify_entry_observers( observer_proxy*& last, bool worker ) { |
| 148 | if ( last == my_tail ) |
| 149 | return; |
| 150 | do_notify_entry_observers( last, worker ); |
| 151 | } |
| 152 | |
| 153 | inline void observer_list::notify_exit_observers( observer_proxy*& last, bool worker ) { |
| 154 | if ( !last ) |
| 155 | return; |
| 156 | __TBB_ASSERT(is_alive((uintptr_t)last), NULL); |
| 157 | do_notify_exit_observers( last, worker ); |
| 158 | __TBB_ASSERT(last, NULL); |
| 159 | poison_value(last); |
| 160 | } |
| 161 | |
| 162 | extern padded<observer_list> the_global_observer_list; |
| 163 | |
| 164 | } // namespace internal |
| 165 | } // namespace tbb |
| 166 | |
| 167 | #endif /* __TBB_SCHEDULER_OBSERVER */ |
| 168 | |
| 169 | #endif /* _TBB_observer_proxy_H */ |
| 170 | |