|
| 1 | + |
| 2 | +#include <atomic> |
| 3 | + |
| 4 | +#include "../tbb/include/oneapi/tbb/detail/_namespace_injection.h" |
| 5 | +#include "../tbb/include/oneapi/tbb/task_arena.h" |
| 6 | + |
| 7 | +#include "../tbb/src/tbb/observer_proxy.h" |
| 8 | +#include "../tbb/src/tbb/main.h" |
| 9 | +#include "../tbb/src/tbb/thread_data.h" |
| 10 | + |
| 11 | +namespace tbb { |
| 12 | + |
| 13 | +namespace interface6 { |
| 14 | +class task_scheduler_observer; |
| 15 | +} |
| 16 | + |
| 17 | +namespace internal { |
| 18 | + |
| 19 | +class task_scheduler_observer_v3 { |
| 20 | + friend class tbb::detail::r1::observer_proxy; |
| 21 | + friend class tbb::detail::r1::observer_list; |
| 22 | + friend class interface6::task_scheduler_observer; |
| 23 | + |
| 24 | + //! Pointer to the proxy holding this observer. |
| 25 | + /** Observers are proxied by the scheduler to maintain persistent lists of them. **/ |
| 26 | + tbb::detail::r1::observer_proxy* my_proxy; |
| 27 | + |
| 28 | + //! Counter preventing the observer from being destroyed while in use by the scheduler. |
| 29 | + /** Valid only when observation is on. **/ |
| 30 | + std::atomic<intptr_t> my_busy_count; |
| 31 | + |
| 32 | +public: |
| 33 | + //! Enable or disable observation |
| 34 | + /** For local observers the method can be used only when the current thread |
| 35 | + has the task scheduler initialized or is attached to an arena. |
| 36 | + Repeated calls with the same state are no-ops. **/ |
| 37 | + void __TBB_EXPORTED_METHOD observe( bool state=true ); |
| 38 | + |
| 39 | + //! Returns true if observation is enabled, false otherwise. |
| 40 | + bool is_observing() const {return my_proxy!=NULL;} |
| 41 | + |
| 42 | + //! Construct observer with observation disabled. |
| 43 | + task_scheduler_observer_v3() : my_proxy(NULL) { my_busy_count.store(0); } |
| 44 | + |
| 45 | + //! Entry notification |
| 46 | + /** Invoked from inside observe(true) call and whenever a worker enters the arena |
| 47 | + this observer is associated with. If a thread is already in the arena when |
| 48 | + the observer is activated, the entry notification is called before it |
| 49 | + executes the first stolen task. |
| 50 | + Obsolete semantics. For global observers it is called by a thread before |
| 51 | + the first steal since observation became enabled. **/ |
| 52 | + virtual void on_scheduler_entry( bool /*is_worker*/ ) {} |
| 53 | + |
| 54 | + //! Exit notification |
| 55 | + /** Invoked from inside observe(false) call and whenever a worker leaves the |
| 56 | + arena this observer is associated with. |
| 57 | + Obsolete semantics. For global observers it is called by a thread before |
| 58 | + the first steal since observation became enabled. **/ |
| 59 | + virtual void on_scheduler_exit( bool /*is_worker*/ ) {} |
| 60 | + |
| 61 | + //! Destructor automatically switches observation off if it is enabled. |
| 62 | + virtual ~task_scheduler_observer_v3() { if(my_proxy) observe(false);} |
| 63 | +}; |
| 64 | + |
| 65 | +} // namespace internal |
| 66 | + |
| 67 | +namespace interface6 { |
| 68 | + |
| 69 | +class task_scheduler_observer : public internal::task_scheduler_observer_v3 { |
| 70 | + friend class internal::task_scheduler_observer_v3; |
| 71 | + friend class tbb::detail::r1::observer_proxy; |
| 72 | + friend class tbb::detail::r1::observer_list; |
| 73 | + |
| 74 | + /** Negative numbers with the largest absolute value to minimize probability |
| 75 | + of coincidence in case of a bug in busy count usage. **/ |
| 76 | + // TODO: take more high bits for version number |
| 77 | + static const intptr_t v6_trait = (intptr_t)((~(uintptr_t)0 >> 1) + 1); |
| 78 | + |
| 79 | + //! contains task_arena pointer or tag indicating local or global semantics of the observer |
| 80 | + intptr_t my_context_tag; |
| 81 | + enum { global_tag = 0, implicit_tag = 1 }; |
| 82 | + |
| 83 | +public: |
| 84 | + //! Construct local or global observer in inactive state (observation disabled). |
| 85 | + /** For a local observer entry/exit notifications are invoked whenever a worker |
| 86 | + thread joins/leaves the arena of the observer's owner thread. If a thread is |
| 87 | + already in the arena when the observer is activated, the entry notification is |
| 88 | + called before it executes the first stolen task. **/ |
| 89 | + /** TODO: Obsolete. |
| 90 | + Global observer semantics is obsolete as it violates master thread isolation |
| 91 | + guarantees and is not composable. Thus the current default behavior of the |
| 92 | + constructor is obsolete too and will be changed in one of the future versions |
| 93 | + of the library. **/ |
| 94 | + explicit task_scheduler_observer( bool local = false ) { |
| 95 | + my_context_tag = local? implicit_tag : global_tag; |
| 96 | + } |
| 97 | + |
| 98 | + //! Construct local observer for a given arena in inactive state (observation disabled). |
| 99 | + /** entry/exit notifications are invoked whenever a thread joins/leaves arena. |
| 100 | + If a thread is already in the arena when the observer is activated, the entry notification |
| 101 | + is called before it executes the first stolen task. **/ |
| 102 | + explicit task_scheduler_observer( task_arena & a) { |
| 103 | + my_context_tag = (intptr_t)&a; |
| 104 | + } |
| 105 | + |
| 106 | + /** Destructor protects instance of the observer from concurrent notification. |
| 107 | + It is recommended to disable observation before destructor of a derived class starts, |
| 108 | + otherwise it can lead to concurrent notification callback on partly destroyed object **/ |
| 109 | + virtual ~task_scheduler_observer() { if(my_proxy) observe(false); } |
| 110 | + |
| 111 | + //! Enable or disable observation |
| 112 | + /** Warning: concurrent invocations of this method are not safe. |
| 113 | + Repeated calls with the same state are no-ops. **/ |
| 114 | + void observe( bool state=true ) { |
| 115 | + if( state && !my_proxy ) { |
| 116 | + __TBB_ASSERT( !my_busy_count, "Inconsistent state of task_scheduler_observer instance"); |
| 117 | + my_busy_count.store(v6_trait); |
| 118 | + } |
| 119 | + internal::task_scheduler_observer_v3::observe(state); |
| 120 | + } |
| 121 | +}; |
| 122 | + |
| 123 | +} // namespace interface6 |
| 124 | + |
| 125 | +} // namespace tbb |
| 126 | + |
| 127 | +namespace tbb { |
| 128 | +namespace internal { |
| 129 | + |
| 130 | +__declspec(dllexport) |
| 131 | +void __TBB_EXPORTED_FUNC task_scheduler_observer_v3::observe( bool enable ) { |
| 132 | + auto* tso = (tbb::detail::d1::task_scheduler_observer*) (this); |
| 133 | + tbb::detail::r1::observe(*tso, enable); |
| 134 | +} |
| 135 | + |
| 136 | +} // namespace internal |
| 137 | +} // namespace tbb |
0 commit comments