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 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 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 137 ~semaphore() {CloseHandle( sem );} 138 //! wait/acquire 139 void P() {WaitForSingleObjectEx( sem, INFINITE, FALSE );} 140 //! post/release 141 void V() {ReleaseSemaphore( sem, 1, NULL );} 142 private: 143 HANDLE sem; 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 153 semaphore(int start_cnt_ = 0) : sem(start_cnt_) { init_semaphore(start_cnt_); } 154 //! dtor 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 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 168 void V() { semaphore_signal( sem ); } 169 private: 170 semaphore_t sem; 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 185 ~semaphore() { 186 int ret = sem_destroy( &sem ); 187 __TBB_ASSERT_EX( !ret, NULL ); 188 } 189 //! wait/acquire 190 void P() { 191 while( sem_wait( &sem )!=0 ) 192 __TBB_ASSERT( errno==EINTR, NULL ); 193 } 194 //! post/release 195 void V() { sem_post( &sem ); } 196 private: 197 sem_t sem; 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 213 binary_semaphore() { my_sem = CreateEventEx( NULL, NULL, 0, EVENT_ALL_ACCESS ); } 214 //! dtor 215 ~binary_semaphore() { CloseHandle( my_sem ); } 216 //! wait/acquire 217 void P() { WaitForSingleObjectEx( my_sem, INFINITE, FALSE ); } 218 //! post/release 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 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 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 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 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 280 binary_semaphore() { my_sem = 1; } 281 //! dtor 282 ~binary_semaphore() {} 283 //! wait/acquire 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 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 310 binary_semaphore() { 311 int ret = sem_init( &my_sem, /*shared among threads*/ 0, 0 ); 312 __TBB_ASSERT_EX( !ret, NULL ); 313 } 314 //! dtor 315 ~binary_semaphore() { 316 int ret = sem_destroy( &my_sem ); 317 __TBB_ASSERT_EX( !ret, NULL ); 318 } 319 //! wait/acquire 320 void P() { 321 while( sem_wait( &my_sem )!=0 ) 322 __TBB_ASSERT( errno==EINTR, NULL ); 323 } 324 //! post/release 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