Committing TBB 2019 Update 9 source code
[platform/upstream/tbb.git] / src / tbb / market.h
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_market_H
18 #define _TBB_market_H
19
20 #include "tbb/tbb_stddef.h"
21
22 #include "scheduler_common.h"
23 #include "tbb/atomic.h"
24 #include "tbb/spin_rw_mutex.h"
25 #include "../rml/include/rml_tbb.h"
26
27 #include "intrusive_list.h"
28
29 #if defined(_MSC_VER) && defined(_Wp64)
30     // Workaround for overzealous compiler warnings in /Wp64 mode
31     #pragma warning (push)
32     #pragma warning (disable: 4244)
33 #endif
34
35 namespace tbb {
36
37 class task_group_context;
38
39 namespace internal {
40
41 //------------------------------------------------------------------------
42 // Class market
43 //------------------------------------------------------------------------
44
45 class market : no_copy, rml::tbb_client {
46     friend class generic_scheduler;
47     friend class arena;
48     friend class tbb::interface7::internal::task_arena_base;
49     template<typename SchedulerTraits> friend class custom_scheduler;
50     friend class tbb::task_group_context;
51 private:
52     friend void ITT_DoUnsafeOneTimeInitialization ();
53
54     typedef intrusive_list<arena> arena_list_type;
55     typedef intrusive_list<generic_scheduler> scheduler_list_type;
56
57     //! Currently active global market
58     static market* theMarket;
59
60     typedef scheduler_mutex_type global_market_mutex_type;
61
62     //! Mutex guarding creation/destruction of theMarket, insertions/deletions in my_arenas, and cancellation propagation
63     static global_market_mutex_type  theMarketMutex;
64
65     //! Lightweight mutex guarding accounting operations with arenas list
66     typedef spin_rw_mutex arenas_list_mutex_type;
67     arenas_list_mutex_type my_arenas_list_mutex;
68
69     //! Pointer to the RML server object that services this TBB instance.
70     rml::tbb_server* my_server;
71
72     //! Maximal number of workers allowed for use by the underlying resource manager
73     /** It can't be changed after market creation. **/
74     unsigned my_num_workers_hard_limit;
75
76     //! Current application-imposed limit on the number of workers (see set_active_num_workers())
77     /** It can't be more than my_num_workers_hard_limit. **/
78     unsigned my_num_workers_soft_limit;
79
80     //! Number of workers currently requested from RML
81     int my_num_workers_requested;
82
83     //! First unused index of worker
84     /** Used to assign indices to the new workers coming from RML, and busy part
85         of my_workers array. **/
86     atomic<unsigned> my_first_unused_worker_idx;
87
88     //! Number of workers that were requested by all arenas
89     int my_total_demand;
90
91 #if __TBB_ENQUEUE_ENFORCED_CONCURRENCY
92     //! How many times mandatory concurrency was requested from the market
93     int my_mandatory_num_requested;
94 #endif
95
96 #if __TBB_TASK_PRIORITY
97     //! Highest priority among active arenas in the market.
98     /** Arena priority level is its tasks highest priority (specified by arena's
99         my_top_priority member).
100         Arena is active when it has outstanding request for workers. Note that
101         inactive arena may have workers lingering there for some time. **/
102     intptr_t my_global_top_priority;
103
104     //! Lowest priority among active arenas in the market.
105     /** See also my_global_top_priority **/
106     intptr_t my_global_bottom_priority;
107
108     //! Tracks events that may bring tasks in offload areas to the top priority level.
109     /** Incremented when global top priority is decremented or a task group priority
110         is elevated to the current top level. **/
111     uintptr_t my_global_reload_epoch;
112
113     //! Information about arenas at a particular priority level
114     struct priority_level_info {
115         //! List of arenas at this priority level
116         arena_list_type arenas;
117
118         //! The first arena to be checked when idle worker seeks for an arena to enter
119         /** The check happens in round-robin fashion. **/
120         arena *next_arena;
121
122         //! Total amount of workers requested by arenas at this priority level.
123         int workers_requested;
124
125         //! Maximal amount of workers the market can tell off to this priority level.
126         int workers_available;
127     }; // struct priority_level_info
128
129     //! Information about arenas at different priority levels
130     priority_level_info my_priority_levels[num_priority_levels];
131
132 #else /* !__TBB_TASK_PRIORITY */
133
134     //! List of registered arenas
135     arena_list_type my_arenas;
136
137     //! The first arena to be checked when idle worker seeks for an arena to enter
138     /** The check happens in round-robin fashion. **/
139     arena *my_next_arena;
140 #endif /* !__TBB_TASK_PRIORITY */
141
142     //! ABA prevention marker to assign to newly created arenas
143     uintptr_t my_arenas_aba_epoch;
144
145     //! Reference count controlling market object lifetime
146     unsigned my_ref_count;
147
148     //! Count of master threads attached
149     unsigned my_public_ref_count;
150
151     //! Stack size of worker threads
152     size_t my_stack_size;
153
154     //! Shutdown mode
155     bool my_join_workers;
156
157     //! The value indicating that the soft limit warning is unnecessary
158     static const unsigned skip_soft_limit_warning = ~0U;
159
160     //! Either workers soft limit to be reported via runtime_warning() or skip_soft_limit_warning
161     unsigned my_workers_soft_limit_to_report;
162 #if __TBB_COUNT_TASK_NODES
163     //! Net number of nodes that have been allocated from heap.
164     /** Updated each time a scheduler or arena is destroyed. */
165     atomic<intptr_t> my_task_node_count;
166 #endif /* __TBB_COUNT_TASK_NODES */
167
168     //! Constructor
169     market ( unsigned workers_soft_limit, unsigned workers_hard_limit, size_t stack_size );
170
171     //! Factory method creating new market object
172     static market& global_market ( bool is_public, unsigned max_num_workers = 0, size_t stack_size = 0 );
173
174     //! Destroys and deallocates market object created by market::create()
175     void destroy ();
176
177 #if __TBB_TASK_PRIORITY
178     //! Returns next arena that needs more workers, or NULL.
179     arena* arena_in_need ( arena* prev_arena );
180
181     //! Recalculates the number of workers requested from RML and updates the allotment.
182     int update_workers_request();
183
184     //! Recalculates the number of workers assigned to each arena at and below the specified priority.
185     /** The actual number of workers servicing a particular arena may temporarily
186         deviate from the calculated value. **/
187     void update_allotment ( intptr_t highest_affected_priority );
188
189     //! Changes arena's top priority and updates affected priority levels info in the market.
190     void update_arena_top_priority ( arena& a, intptr_t newPriority );
191
192     //! Changes market's global top priority and related settings.
193     inline void update_global_top_priority ( intptr_t newPriority );
194
195     //! Resets empty market's global top and bottom priority to the normal level.
196     inline void reset_global_priority ();
197
198     inline void advance_global_reload_epoch () {
199         __TBB_store_with_release( my_global_reload_epoch, my_global_reload_epoch + 1 );
200     }
201
202     void assert_market_valid () const {
203         __TBB_ASSERT( (my_priority_levels[my_global_top_priority].workers_requested > 0
204                            && !my_priority_levels[my_global_top_priority].arenas.empty())
205                        || (my_global_top_priority == my_global_bottom_priority &&
206                            my_global_top_priority == normalized_normal_priority), NULL );
207     }
208
209 #else /* !__TBB_TASK_PRIORITY */
210
211     //! Recalculates the number of workers assigned to each arena in the list.
212     /** The actual number of workers servicing a particular arena may temporarily
213         deviate from the calculated value. **/
214     void update_allotment () {
215         if ( my_total_demand )
216             update_allotment( my_arenas, my_total_demand, (int)my_num_workers_soft_limit );
217     }
218
219     // TODO: consider to rewrite the code with is_arena_in_list function
220     //! Returns next arena that needs more workers, or NULL.
221     arena* arena_in_need (arena*) {
222         if(__TBB_load_with_acquire(my_total_demand) <= 0)
223             return NULL;
224         arenas_list_mutex_type::scoped_lock lock(my_arenas_list_mutex, /*is_writer=*/false);
225         return arena_in_need(my_arenas, my_next_arena);
226     }
227     void assert_market_valid () const {}
228 #endif /* !__TBB_TASK_PRIORITY */
229
230     ////////////////////////////////////////////////////////////////////////////////
231     // Helpers to unify code branches dependent on priority feature presence
232
233     void insert_arena_into_list ( arena& a );
234
235     void remove_arena_from_list ( arena& a );
236
237     arena* arena_in_need ( arena_list_type &arenas, arena *hint );
238
239     int update_allotment ( arena_list_type& arenas, int total_demand, int max_workers );
240
241     bool is_arena_in_list( arena_list_type &arenas, arena *a );
242
243
244     ////////////////////////////////////////////////////////////////////////////////
245     // Implementation of rml::tbb_client interface methods
246
247     version_type version () const __TBB_override { return 0; }
248
249     unsigned max_job_count () const __TBB_override { return my_num_workers_hard_limit; }
250
251     size_t min_stack_size () const __TBB_override { return worker_stack_size(); }
252
253     policy_type policy () const __TBB_override { return throughput; }
254
255     job* create_one_job () __TBB_override;
256
257     void cleanup( job& j ) __TBB_override;
258
259     void acknowledge_close_connection () __TBB_override;
260
261     void process( job& j ) __TBB_override;
262
263 public:
264     //! Creates an arena object
265     /** If necessary, also creates global market instance, and boosts its ref count.
266         Each call to create_arena() must be matched by the call to arena::free_arena(). **/
267     static arena* create_arena ( int num_slots, int num_reserved_slots, size_t stack_size );
268
269     //! Removes the arena from the market's list
270     void try_destroy_arena ( arena*, uintptr_t aba_epoch );
271
272     //! Removes the arena from the market's list
273     void detach_arena ( arena& );
274
275     //! Decrements market's refcount and destroys it in the end
276     bool release ( bool is_public, bool blocking_terminate );
277
278 #if __TBB_ENQUEUE_ENFORCED_CONCURRENCY
279     //! Imlpementation of mandatory concurrency enabling
280     void enable_mandatory_concurrency_impl ( arena *a );
281
282     //! Inform the master that there is an arena with mandatory concurrency
283     void enable_mandatory_concurrency ( arena *a );
284
285     //! Inform the master that the arena is no more interested in mandatory concurrency
286     void disable_mandatory_concurrency_impl(arena* a);
287
288     //! Inform the master that the arena is no more interested in mandatory concurrency
289     void mandatory_concurrency_disable ( arena *a );
290 #endif /* __TBB_ENQUEUE_ENFORCED_CONCURRENCY */
291
292     //! Request that arena's need in workers should be adjusted.
293     /** Concurrent invocations are possible only on behalf of different arenas. **/
294     void adjust_demand ( arena&, int delta );
295
296     //! Used when RML asks for join mode during workers termination.
297     bool must_join_workers () const { return my_join_workers; }
298
299     //! Returns the requested stack size of worker threads.
300     size_t worker_stack_size () const { return my_stack_size; }
301
302     //! Set number of active workers
303     static void set_active_num_workers( unsigned w );
304
305     //! Reports active parallelism level according to user's settings
306     static unsigned app_parallelism_limit();
307
308 #if _WIN32||_WIN64
309     //! register master with the resource manager
310     void register_master( ::rml::server::execution_resource_t& rsc_handle ) {
311         __TBB_ASSERT( my_server, "RML server not defined?" );
312         // the server may ignore registration and set master_exec_resource to NULL.
313         my_server->register_master( rsc_handle );
314     }
315
316     //! unregister master with the resource manager
317     void unregister_master( ::rml::server::execution_resource_t& rsc_handle ) const {
318         my_server->unregister_master( rsc_handle );
319     }
320 #endif /* WIN */
321
322 #if __TBB_TASK_GROUP_CONTEXT
323     //! Finds all contexts affected by the state change and propagates the new state to them.
324     /** The propagation is relayed to the market because tasks created by one
325         master thread can be passed to and executed by other masters. This means
326         that context trees can span several arenas at once and thus state change
327         propagation cannot be generally localized to one arena only. **/
328     template <typename T>
329     bool propagate_task_group_state ( T task_group_context::*mptr_state, task_group_context& src, T new_state );
330 #endif /* __TBB_TASK_GROUP_CONTEXT */
331
332 #if __TBB_TASK_PRIORITY
333     //! Lowers arena's priority is not higher than newPriority
334     /** Returns true if arena priority was actually elevated. **/
335     bool lower_arena_priority ( arena& a, intptr_t new_priority, uintptr_t old_reload_epoch );
336
337     //! Makes sure arena's priority is not lower than newPriority
338     /** Returns true if arena priority was elevated. Also updates arena's bottom
339         priority boundary if necessary.
340
341         This method is called whenever a user changes priority, because whether
342         it was hiked or sunk can be determined for sure only under the lock used
343         by this function. **/
344     bool update_arena_priority ( arena& a, intptr_t new_priority );
345 #endif /* __TBB_TASK_PRIORITY */
346
347 #if __TBB_COUNT_TASK_NODES
348     //! Net number of nodes that have been allocated from heap.
349     /** Updated each time a scheduler or arena is destroyed. */
350     void update_task_node_count( intptr_t delta ) { my_task_node_count += delta; }
351 #endif /* __TBB_COUNT_TASK_NODES */
352
353 #if __TBB_TASK_GROUP_CONTEXT
354     //! List of registered master threads
355     scheduler_list_type my_masters;
356
357     //! Array of pointers to the registered workers
358     /** Used by cancellation propagation mechanism.
359         Must be the last data member of the class market. **/
360     generic_scheduler* my_workers[1];
361 #endif /* __TBB_TASK_GROUP_CONTEXT */
362
363     static unsigned max_num_workers() {
364         global_market_mutex_type::scoped_lock lock( theMarketMutex );
365         return theMarket? theMarket->my_num_workers_hard_limit : 0;
366     }
367 }; // class market
368
369 } // namespace internal
370 } // namespace tbb
371
372 #if defined(_MSC_VER) && defined(_Wp64)
373     // Workaround for overzealous compiler warnings in /Wp64 mode
374     #pragma warning (pop)
375 #endif // warning 4244 is back
376
377 #endif /* _TBB_market_H */