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