1 /*
2 Copyright (c) 2005-2022 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_semaphore_H
18 #define __TBB_semaphore_H
19
20 #include "oneapi/tbb/detail/_utils.h"
21
22 #if _WIN32||_WIN64
23 #include <windows.h>
24 #elif __APPLE__
25 #include <dispatch/dispatch.h>
26 #else
27 #include <semaphore.h>
28 #ifdef TBB_USE_DEBUG
29 #include <cerrno>
30 #endif
31 #endif /*_WIN32||_WIN64*/
32
33 #include <atomic>
34
35 #if __unix__
36 #if defined(__has_include)
37 #define __TBB_has_include __has_include
38 #else
39 #define __TBB_has_include(x) 0
40 #endif
41
42 /* Futex definitions */
43 #include <unistd.h>
44 #if defined(__linux__) || __TBB_has_include(<sys/syscall.h>)
45 #include <sys/syscall.h>
46 #endif
47
48 #if defined(SYS_futex)
49
50 /* This section is included for Linux and some other systems that may support futexes.*/
51
52 #define __TBB_USE_FUTEX 1
53
54 /*
55 If available, use typical headers where futex API is defined. While Linux and OpenBSD
56 are known to provide such headers, other systems might have them as well.
57 */
58 #if defined(__linux__) || __TBB_has_include(<linux/futex.h>)
59 #include <linux/futex.h>
60 #elif defined(__OpenBSD__) || __TBB_has_include(<sys/futex.h>)
61 #include <sys/futex.h>
62 #endif
63
64 #include <climits>
65 #include <cerrno>
66
67 /*
68 Some systems might not define the macros or use different names. In such case we expect
69 the actual parameter values to match Linux: 0 for wait, 1 for wake.
70 */
71 #if defined(FUTEX_WAIT_PRIVATE)
72 #define __TBB_FUTEX_WAIT FUTEX_WAIT_PRIVATE
73 #elif defined(FUTEX_WAIT)
74 #define __TBB_FUTEX_WAIT FUTEX_WAIT
75 #else
76 #define __TBB_FUTEX_WAIT 0
77 #endif
78
79 #if defined(FUTEX_WAKE_PRIVATE)
80 #define __TBB_FUTEX_WAKE FUTEX_WAKE_PRIVATE
81 #elif defined(FUTEX_WAKE)
82 #define __TBB_FUTEX_WAKE FUTEX_WAKE
83 #else
84 #define __TBB_FUTEX_WAKE 1
85 #endif
86
87 #endif // SYS_futex
88 #endif // __unix__
89
90 namespace tbb {
91 namespace detail {
92 namespace r1 {
93
94 ////////////////////////////////////////////////////////////////////////////////////////////////////
95 // Futex implementation
96 ////////////////////////////////////////////////////////////////////////////////////////////////////
97
98 #if __TBB_USE_FUTEX
99
futex_wait(void * futex,int comparand)100 static inline int futex_wait( void *futex, int comparand ) {
101 int r = ::syscall(SYS_futex, futex, __TBB_FUTEX_WAIT, comparand, nullptr, nullptr, 0);
102 #if TBB_USE_ASSERT
103 int e = errno;
104 __TBB_ASSERT(r == 0 || r == EWOULDBLOCK || (r == -1 && (e == EAGAIN || e == EINTR)), "futex_wait failed.");
105 #endif /* TBB_USE_ASSERT */
106 return r;
107 }
108
futex_wakeup_one(void * futex)109 static inline int futex_wakeup_one( void *futex ) {
110 int r = ::syscall(SYS_futex, futex, __TBB_FUTEX_WAKE, 1, nullptr, nullptr, 0);
111 __TBB_ASSERT(r == 0 || r == 1, "futex_wakeup_one: more than one thread woken up?");
112 return r;
113 }
114
115 // Additional possible methods that are not required right now
116 // static inline int futex_wakeup_all( void *futex ) {
117 // int r = ::syscall( SYS_futex,futex,__TBB_FUTEX_WAKE,INT_MAX,nullptr,nullptr,0 );
118 // __TBB_ASSERT( r>=0, "futex_wakeup_all: error in waking up threads" );
119 // return r;
120 // }
121
122 #endif // __TBB_USE_FUTEX
123
124 ////////////////////////////////////////////////////////////////////////////////////////////////////
125 #if _WIN32||_WIN64
126 typedef LONG sem_count_t;
127 //! Edsger Dijkstra's counting semaphore
128 class semaphore : no_copy {
129 static const int max_semaphore_cnt = MAXLONG;
130 public:
131 //! ctor
132 semaphore(size_t start_cnt_ = 0) {init_semaphore(start_cnt_);}
133 //! dtor
~semaphore()134 ~semaphore() {CloseHandle( sem );}
135 //! wait/acquire
P()136 void P() {WaitForSingleObjectEx( sem, INFINITE, FALSE );}
137 //! post/release
V()138 void V() {ReleaseSemaphore( sem, 1, nullptr);}
139 private:
140 HANDLE sem;
init_semaphore(size_t start_cnt_)141 void init_semaphore(size_t start_cnt_) {
142 sem = CreateSemaphoreEx( nullptr, LONG(start_cnt_), max_semaphore_cnt, nullptr, 0, SEMAPHORE_ALL_ACCESS );
143 }
144 };
145 #elif __APPLE__
146 //! Edsger Dijkstra's counting semaphore
147 class semaphore : no_copy {
148 public:
149 //! ctor
150 semaphore(int start_cnt_ = 0) { my_sem = dispatch_semaphore_create(start_cnt_); }
151 //! dtor
~semaphore()152 ~semaphore() { dispatch_release(my_sem); }
153 //! wait/acquire
P()154 void P() {
155 std::intptr_t ret = dispatch_semaphore_wait(my_sem, DISPATCH_TIME_FOREVER);
156 __TBB_ASSERT_EX(ret == 0, "dispatch_semaphore_wait() failed");
157 }
158 //! post/release
V()159 void V() { dispatch_semaphore_signal(my_sem); }
160 private:
161 dispatch_semaphore_t my_sem;
162 };
163 #else /* Linux/Unix */
164 typedef uint32_t sem_count_t;
165 //! Edsger Dijkstra's counting semaphore
166 class semaphore : no_copy {
167 public:
168 //! ctor
169 semaphore(int start_cnt_ = 0 ) { init_semaphore( start_cnt_ ); }
170
171 //! dtor
~semaphore()172 ~semaphore() {
173 int ret = sem_destroy( &sem );
174 __TBB_ASSERT_EX( !ret, nullptr);
175 }
176 //! wait/acquire
P()177 void P() {
178 while( sem_wait( &sem )!=0 )
179 __TBB_ASSERT( errno==EINTR, nullptr);
180 }
181 //! post/release
V()182 void V() { sem_post( &sem ); }
183 private:
184 sem_t sem;
init_semaphore(int start_cnt_)185 void init_semaphore(int start_cnt_) {
186 int ret = sem_init( &sem, /*shared among threads*/ 0, start_cnt_ );
187 __TBB_ASSERT_EX( !ret, nullptr);
188 }
189 };
190 #endif /* _WIN32||_WIN64 */
191
192
193 //! for performance reasons, we want specialized binary_semaphore
194 #if _WIN32||_WIN64
195 #if !__TBB_USE_SRWLOCK
196 //! binary_semaphore for concurrent_monitor
197 class binary_semaphore : no_copy {
198 public:
199 //! ctor
binary_semaphore()200 binary_semaphore() { my_sem = CreateEventEx( nullptr, nullptr, 0, EVENT_ALL_ACCESS ); }
201 //! dtor
~binary_semaphore()202 ~binary_semaphore() { CloseHandle( my_sem ); }
203 //! wait/acquire
P()204 void P() { WaitForSingleObjectEx( my_sem, INFINITE, FALSE ); }
205 //! post/release
V()206 void V() { SetEvent( my_sem ); }
207 private:
208 HANDLE my_sem;
209 };
210 #else /* __TBB_USE_SRWLOCK */
211
212 union srwl_or_handle {
213 SRWLOCK lock;
214 HANDLE h;
215 };
216
217 //! binary_semaphore for concurrent_monitor
218 class binary_semaphore : no_copy {
219 public:
220 //! ctor
221 binary_semaphore();
222 //! dtor
223 ~binary_semaphore();
224 //! wait/acquire
225 void P();
226 //! post/release
227 void V();
228 private:
229 srwl_or_handle my_sem;
230 };
231 #endif /* !__TBB_USE_SRWLOCK */
232 #elif __APPLE__
233 //! binary_semaphore for concurrent monitor
234 using binary_semaphore = semaphore;
235 #else /* Linux/Unix */
236
237 #if __TBB_USE_FUTEX
238 class binary_semaphore : no_copy {
239 // The implementation is equivalent to the "Mutex, Take 3" one
240 // in the paper "Futexes Are Tricky" by Ulrich Drepper
241 public:
242 //! ctor
binary_semaphore()243 binary_semaphore() { my_sem = 1; }
244 //! dtor
~binary_semaphore()245 ~binary_semaphore() {}
246 //! wait/acquire
P()247 void P() {
248 int s = 0;
249 if( !my_sem.compare_exchange_strong( s, 1 ) ) {
250 if( s!=2 )
251 s = my_sem.exchange( 2 );
252 while( s!=0 ) { // This loop deals with spurious wakeup
253 futex_wait( &my_sem, 2 );
254 s = my_sem.exchange( 2 );
255 }
256 }
257 }
258 //! post/release
V()259 void V() {
260 __TBB_ASSERT( my_sem.load(std::memory_order_relaxed)>=1, "multiple V()'s in a row?" );
261 if( my_sem.exchange( 0 )==2 )
262 futex_wakeup_one( &my_sem );
263 }
264 private:
265 std::atomic<int> my_sem; // 0 - open; 1 - closed, no waits; 2 - closed, possible waits
266 };
267 #else
268 typedef uint32_t sem_count_t;
269 //! binary_semaphore for concurrent monitor
270 class binary_semaphore : no_copy {
271 public:
272 //! ctor
binary_semaphore()273 binary_semaphore() {
274 int ret = sem_init( &my_sem, /*shared among threads*/ 0, 0 );
275 __TBB_ASSERT_EX( !ret, nullptr);
276 }
277 //! dtor
~binary_semaphore()278 ~binary_semaphore() {
279 int ret = sem_destroy( &my_sem );
280 __TBB_ASSERT_EX( !ret, nullptr);
281 }
282 //! wait/acquire
P()283 void P() {
284 while( sem_wait( &my_sem )!=0 )
285 __TBB_ASSERT( errno==EINTR, nullptr);
286 }
287 //! post/release
V()288 void V() { sem_post( &my_sem ); }
289 private:
290 sem_t my_sem;
291 };
292 #endif /* __TBB_USE_FUTEX */
293 #endif /* _WIN32||_WIN64 */
294
295 } // namespace r1
296 } // namespace detail
297 } // namespace tbb
298
299 #endif /* __TBB_semaphore_H */
300