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