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