xref: /oneTBB/include/oneapi/tbb/global_control.h (revision 5fc0a5f6)
1 /*
2     Copyright (c) 2005-2021 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_global_control_H
18 #define __TBB_global_control_H
19 
20 #include "detail/_config.h"
21 
22 #include "detail/_assert.h"
23 #include "detail/_attach.h"
24 #include "detail/_exception.h"
25 #include "detail/_namespace_injection.h"
26 #include "detail/_template_helpers.h"
27 
28 #include <cstddef>
29 #include <new> // std::nothrow_t
30 
31 namespace tbb {
32 namespace detail {
33 
34 namespace d1 {
35 class global_control;
36 class task_scheduler_handle;
37 }
38 
39 namespace r1 {
40 TBB_EXPORT void __TBB_EXPORTED_FUNC create(d1::global_control&);
41 TBB_EXPORT void __TBB_EXPORTED_FUNC destroy(d1::global_control&);
42 TBB_EXPORT std::size_t __TBB_EXPORTED_FUNC global_control_active_value(int);
43 struct global_control_impl;
44 struct control_storage_comparator;
45 void release_impl(d1::task_scheduler_handle& handle);
46 bool finalize_impl(d1::task_scheduler_handle& handle);
47 TBB_EXPORT void __TBB_EXPORTED_FUNC get(d1::task_scheduler_handle&);
48 TBB_EXPORT bool __TBB_EXPORTED_FUNC finalize(d1::task_scheduler_handle&, std::intptr_t mode);
49 }
50 
51 namespace d1 {
52 
53 class global_control {
54 public:
55     enum parameter {
56         max_allowed_parallelism,
57         thread_stack_size,
58         terminate_on_exception,
59         scheduler_handle, // not a public parameter
60         parameter_max // insert new parameters above this point
61     };
62 
global_control(parameter p,std::size_t value)63     global_control(parameter p, std::size_t value) :
64         my_value(value), my_reserved(), my_param(p) {
65         suppress_unused_warning(my_reserved);
66         __TBB_ASSERT(my_param < parameter_max, "Invalid parameter");
67 #if __TBB_WIN8UI_SUPPORT && (_WIN32_WINNT < 0x0A00)
68         // For Windows 8 Store* apps it's impossible to set stack size
69         if (p==thread_stack_size)
70             return;
71 #elif __TBB_x86_64 && (_WIN32 || _WIN64)
72         if (p==thread_stack_size)
73             __TBB_ASSERT_RELEASE((unsigned)value == value, "Stack size is limited to unsigned int range");
74 #endif
75         if (my_param==max_allowed_parallelism)
76             __TBB_ASSERT_RELEASE(my_value>0, "max_allowed_parallelism cannot be 0.");
77         r1::create(*this);
78     }
79 
~global_control()80     ~global_control() {
81         __TBB_ASSERT(my_param < parameter_max, "Invalid parameter");
82 #if __TBB_WIN8UI_SUPPORT && (_WIN32_WINNT < 0x0A00)
83         // For Windows 8 Store* apps it's impossible to set stack size
84         if (my_param==thread_stack_size)
85             return;
86 #endif
87         r1::destroy(*this);
88     }
89 
active_value(parameter p)90     static std::size_t active_value(parameter p) {
91         __TBB_ASSERT(p < parameter_max, "Invalid parameter");
92         return r1::global_control_active_value((int)p);
93     }
94 
95 private:
96     std::size_t my_value;
97     std::intptr_t my_reserved; // TODO: substitution of global_control* not to break backward compatibility
98     parameter my_param;
99 
100     friend struct r1::global_control_impl;
101     friend struct r1::control_storage_comparator;
102 };
103 
104 //! Finalization options.
105 //! Outside of the class to avoid extensive friendship.
106 static constexpr std::intptr_t release_nothrowing = 0;
107 static constexpr std::intptr_t finalize_nothrowing = 1;
108 static constexpr std::intptr_t finalize_throwing = 2;
109 
110 //! User side wrapper for a task scheduler lifetime control object
111 class task_scheduler_handle {
112 public:
113     //! Creates an empty task_scheduler_handle
114     task_scheduler_handle() = default;
115 
116     //! Creates an attached instance of task_scheduler_handle
task_scheduler_handle(attach)117     task_scheduler_handle(attach) {
118         r1::get(*this);
119     }
120 
121     //! Release a reference if any
~task_scheduler_handle()122     ~task_scheduler_handle() {
123         release();
124     }
125 
126     //! No copy
127     task_scheduler_handle(const task_scheduler_handle& other) = delete;
128     task_scheduler_handle& operator=(const task_scheduler_handle& other) = delete;
129 
130     //! Move only
task_scheduler_handle(task_scheduler_handle && other)131     task_scheduler_handle(task_scheduler_handle&& other) noexcept {
132         std::swap(m_ctl, other.m_ctl);
133     }
134     task_scheduler_handle& operator=(task_scheduler_handle&& other) noexcept {
135         std::swap(m_ctl, other.m_ctl);
136         return *this;
137     };
138 
139     //! Checks if the task_scheduler_handle is empty
140     explicit operator bool() const noexcept {
141         return m_ctl != nullptr;
142     }
143 
144     //! Release the reference and deactivate handle
release()145     void release() {
146         if (m_ctl != nullptr) {
147             r1::finalize(*this, release_nothrowing);
148             m_ctl = nullptr;
149         }
150     }
151 
152 private:
153     friend void r1::release_impl(task_scheduler_handle& handle);
154     friend bool r1::finalize_impl(task_scheduler_handle& handle);
155     friend void __TBB_EXPORTED_FUNC r1::get(task_scheduler_handle&);
156 
157     friend void finalize(task_scheduler_handle&);
158     friend bool finalize(task_scheduler_handle&, const std::nothrow_t&) noexcept;
159 
160     global_control* m_ctl{nullptr};
161 };
162 
163 #if TBB_USE_EXCEPTIONS
164 //! Waits for worker threads termination. Throws exception on error.
finalize(task_scheduler_handle & handle)165 inline void finalize(task_scheduler_handle& handle) {
166     try_call([&] {
167         if (handle.m_ctl != nullptr) {
168             bool finalized = r1::finalize(handle, finalize_throwing);
169             __TBB_ASSERT_EX(finalized, "r1::finalize did not respect finalize_throwing ?");
170 
171         }
172     }).on_completion([&] {
173         __TBB_ASSERT(!handle, "The handle should be empty after finalize");
174     });
175 }
176 #endif
177 //! Waits for worker threads termination. Returns false on error.
finalize(task_scheduler_handle & handle,const std::nothrow_t &)178 inline bool finalize(task_scheduler_handle& handle, const std::nothrow_t&) noexcept {
179     bool finalized = true;
180     if (handle.m_ctl != nullptr) {
181         finalized = r1::finalize(handle, finalize_nothrowing);
182     }
183     __TBB_ASSERT(!handle, "The handle should be empty after finalize");
184     return finalized;
185 }
186 
187 } // namespace d1
188 } // namespace detail
189 
190 inline namespace v1 {
191 using detail::d1::global_control;
192 using detail::d1::attach;
193 using detail::d1::finalize;
194 using detail::d1::task_scheduler_handle;
195 using detail::r1::unsafe_wait;
196 } // namespace v1
197 
198 } // namespace tbb
199 
200 #endif // __TBB_global_control_H
201