xref: /oneTBB/src/tbb/semaphore.h (revision 0f0d1cb9)
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