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