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