xref: /oneTBB/src/tbb/observer_proxy.h (revision c21e688a)
151c0b2f7Stbbdev /*
2*c21e688aSSergey Zheltov     Copyright (c) 2005-2022 Intel Corporation
351c0b2f7Stbbdev 
451c0b2f7Stbbdev     Licensed under the Apache License, Version 2.0 (the "License");
551c0b2f7Stbbdev     you may not use this file except in compliance with the License.
651c0b2f7Stbbdev     You may obtain a copy of the License at
751c0b2f7Stbbdev 
851c0b2f7Stbbdev         http://www.apache.org/licenses/LICENSE-2.0
951c0b2f7Stbbdev 
1051c0b2f7Stbbdev     Unless required by applicable law or agreed to in writing, software
1151c0b2f7Stbbdev     distributed under the License is distributed on an "AS IS" BASIS,
1251c0b2f7Stbbdev     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1351c0b2f7Stbbdev     See the License for the specific language governing permissions and
1451c0b2f7Stbbdev     limitations under the License.
1551c0b2f7Stbbdev */
1651c0b2f7Stbbdev 
1751c0b2f7Stbbdev #ifndef __TBB_observer_proxy_H
1851c0b2f7Stbbdev #define __TBB_observer_proxy_H
1951c0b2f7Stbbdev 
2049e08aacStbbdev #include "oneapi/tbb/detail/_config.h"
2149e08aacStbbdev #include "oneapi/tbb/detail/_aligned_space.h"
2251c0b2f7Stbbdev 
2349e08aacStbbdev #include "oneapi/tbb/task_scheduler_observer.h"
2449e08aacStbbdev #include "oneapi/tbb/spin_rw_mutex.h"
2551c0b2f7Stbbdev 
2651c0b2f7Stbbdev namespace tbb {
2751c0b2f7Stbbdev namespace detail {
2851c0b2f7Stbbdev namespace r1 {
2951c0b2f7Stbbdev 
3051c0b2f7Stbbdev class observer_list {
3151c0b2f7Stbbdev     friend class arena;
3251c0b2f7Stbbdev 
3351c0b2f7Stbbdev     // Mutex is wrapped with aligned_space to shut up warnings when its destructor
3451c0b2f7Stbbdev     // is called while threads are still using it.
3551c0b2f7Stbbdev     typedef aligned_space<spin_rw_mutex>  my_mutex_type;
3651c0b2f7Stbbdev 
3751c0b2f7Stbbdev     //! Pointer to the head of this list.
38b15aabb3Stbbdev     std::atomic<observer_proxy*> my_head{nullptr};
3951c0b2f7Stbbdev 
4051c0b2f7Stbbdev     //! Pointer to the tail of this list.
41b15aabb3Stbbdev     std::atomic<observer_proxy*> my_tail{nullptr};
4251c0b2f7Stbbdev 
4351c0b2f7Stbbdev     //! Mutex protecting this list.
4451c0b2f7Stbbdev     my_mutex_type my_mutex;
4551c0b2f7Stbbdev 
4651c0b2f7Stbbdev     //! Back-pointer to the arena this list belongs to.
4751c0b2f7Stbbdev     arena* my_arena;
4851c0b2f7Stbbdev 
4951c0b2f7Stbbdev     //! Decrement refcount of the proxy p if there are other outstanding references.
5057f524caSIlya Isaev     /** In case of success sets p to nullptr. Must be invoked from under the list lock. **/
5151c0b2f7Stbbdev     inline static void remove_ref_fast( observer_proxy*& p );
5251c0b2f7Stbbdev 
5351c0b2f7Stbbdev     //! Implements notify_entry_observers functionality.
5451c0b2f7Stbbdev     void do_notify_entry_observers( observer_proxy*& last, bool worker );
5551c0b2f7Stbbdev 
5651c0b2f7Stbbdev     //! Implements notify_exit_observers functionality.
5751c0b2f7Stbbdev     void do_notify_exit_observers( observer_proxy* last, bool worker );
5851c0b2f7Stbbdev 
5951c0b2f7Stbbdev public:
6051c0b2f7Stbbdev     observer_list () = default;
6151c0b2f7Stbbdev 
6251c0b2f7Stbbdev     //! Removes and destroys all observer proxies from the list.
6351c0b2f7Stbbdev     /** Cannot be used concurrently with other methods. **/
6451c0b2f7Stbbdev     void clear ();
6551c0b2f7Stbbdev 
6651c0b2f7Stbbdev     //! Add observer proxy to the tail of the list.
6751c0b2f7Stbbdev     void insert ( observer_proxy* p );
6851c0b2f7Stbbdev 
6951c0b2f7Stbbdev     //! Remove observer proxy from the list.
7051c0b2f7Stbbdev     void remove ( observer_proxy* p );
7151c0b2f7Stbbdev 
7251c0b2f7Stbbdev     //! Decrement refcount of the proxy and destroy it if necessary.
7351c0b2f7Stbbdev     /** When refcount reaches zero removes the proxy from the list and destructs it. **/
7451c0b2f7Stbbdev     void remove_ref( observer_proxy* p );
7551c0b2f7Stbbdev 
7651c0b2f7Stbbdev     //! Type of the scoped lock for the reader-writer mutex associated with the list.
7751c0b2f7Stbbdev     typedef spin_rw_mutex::scoped_lock scoped_lock;
7851c0b2f7Stbbdev 
7951c0b2f7Stbbdev     //! Accessor to the reader-writer mutex associated with the list.
mutex()8051c0b2f7Stbbdev     spin_rw_mutex& mutex () { return my_mutex.begin()[0]; }
8151c0b2f7Stbbdev 
8251c0b2f7Stbbdev     //! Call entry notifications on observers added after last was notified.
8351c0b2f7Stbbdev     /** Updates last to become the last notified observer proxy (in the global list)
8451c0b2f7Stbbdev         or leaves it to be nullptr. The proxy has its refcount incremented. **/
8551c0b2f7Stbbdev     inline void notify_entry_observers( observer_proxy*& last, bool worker );
8651c0b2f7Stbbdev 
8751c0b2f7Stbbdev     //! Call exit notifications on last and observers added before it.
8851c0b2f7Stbbdev     inline void notify_exit_observers( observer_proxy*& last, bool worker );
8951c0b2f7Stbbdev }; // class observer_list
9051c0b2f7Stbbdev 
9151c0b2f7Stbbdev //! Wrapper for an observer object
9251c0b2f7Stbbdev /** To maintain shared lists of observers the scheduler first wraps each observer
9351c0b2f7Stbbdev     object into a proxy so that a list item remained valid even after the corresponding
9451c0b2f7Stbbdev     proxy object is destroyed by the user code. **/
9551c0b2f7Stbbdev class observer_proxy {
96478de5b1Stbbdev     friend class d1::task_scheduler_observer;
9751c0b2f7Stbbdev     friend class observer_list;
9851c0b2f7Stbbdev     friend void observe(d1::task_scheduler_observer&, bool);
9951c0b2f7Stbbdev     //! Reference count used for garbage collection.
10051c0b2f7Stbbdev     /** 1 for reference from my task_scheduler_observer.
10151c0b2f7Stbbdev         1 for each task dispatcher's last observer pointer.
10251c0b2f7Stbbdev         No accounting for neighbors in the shared list. */
10351c0b2f7Stbbdev     std::atomic<std::uintptr_t> my_ref_count;
10451c0b2f7Stbbdev     //! Reference to the list this observer belongs to.
10551c0b2f7Stbbdev     observer_list* my_list;
10651c0b2f7Stbbdev     //! Pointer to next observer in the list specified by my_head.
10757f524caSIlya Isaev     /** nullptr for the last item in the list. **/
10851c0b2f7Stbbdev     observer_proxy* my_next;
10951c0b2f7Stbbdev     //! Pointer to the previous observer in the list specified by my_head.
11051c0b2f7Stbbdev     /** For the head of the list points to the last item. **/
11151c0b2f7Stbbdev     observer_proxy* my_prev;
11251c0b2f7Stbbdev     //! Associated observer
11351c0b2f7Stbbdev     d1::task_scheduler_observer* my_observer;
11451c0b2f7Stbbdev 
11551c0b2f7Stbbdev     //! Constructs proxy for the given observer and adds it to the specified list.
11651c0b2f7Stbbdev     observer_proxy( d1::task_scheduler_observer& );
11751c0b2f7Stbbdev 
11851c0b2f7Stbbdev     ~observer_proxy();
11951c0b2f7Stbbdev }; // class observer_proxy
12051c0b2f7Stbbdev 
remove_ref_fast(observer_proxy * & p)12151c0b2f7Stbbdev void observer_list::remove_ref_fast( observer_proxy*& p ) {
12251c0b2f7Stbbdev     if( p->my_observer ) {
12351c0b2f7Stbbdev         // Can decrement refcount quickly, as it cannot drop to zero while under the lock.
12451c0b2f7Stbbdev         std::uintptr_t r = --p->my_ref_count;
12557f524caSIlya Isaev         __TBB_ASSERT_EX( r, nullptr);
12657f524caSIlya Isaev         p = nullptr;
12751c0b2f7Stbbdev     } else {
12851c0b2f7Stbbdev         // Use slow form of refcount decrementing, after the lock is released.
12951c0b2f7Stbbdev     }
13051c0b2f7Stbbdev }
13151c0b2f7Stbbdev 
notify_entry_observers(observer_proxy * & last,bool worker)13251c0b2f7Stbbdev void observer_list::notify_entry_observers(observer_proxy*& last, bool worker) {
133b15aabb3Stbbdev     if (last == my_tail.load(std::memory_order_relaxed))
13451c0b2f7Stbbdev         return;
13551c0b2f7Stbbdev     do_notify_entry_observers(last, worker);
13651c0b2f7Stbbdev }
13751c0b2f7Stbbdev 
notify_exit_observers(observer_proxy * & last,bool worker)13851c0b2f7Stbbdev void observer_list::notify_exit_observers( observer_proxy*& last, bool worker ) {
13951c0b2f7Stbbdev     if (last == nullptr) {
14051c0b2f7Stbbdev         return;
14151c0b2f7Stbbdev     }
14257f524caSIlya Isaev     __TBB_ASSERT(!is_poisoned(last), nullptr);
14351c0b2f7Stbbdev     do_notify_exit_observers( last, worker );
14457f524caSIlya Isaev     __TBB_ASSERT(last != nullptr, nullptr);
14551c0b2f7Stbbdev     poison_pointer(last);
14651c0b2f7Stbbdev }
14751c0b2f7Stbbdev 
14851c0b2f7Stbbdev } // namespace r1
14951c0b2f7Stbbdev } // namespace detail
15051c0b2f7Stbbdev } // namespace tbb
15151c0b2f7Stbbdev 
15251c0b2f7Stbbdev #endif /* __TBB_observer_proxy_H */
153