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 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 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 134 ~semaphore() {CloseHandle( sem );} 135 //! wait/acquire 136 void P() {WaitForSingleObjectEx( sem, INFINITE, FALSE );} 137 //! post/release 138 void V() {ReleaseSemaphore( sem, 1, nullptr);} 139 private: 140 HANDLE sem; 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 152 ~semaphore() { dispatch_release(my_sem); } 153 //! wait/acquire 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 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 172 ~semaphore() { 173 int ret = sem_destroy( &sem ); 174 __TBB_ASSERT_EX( !ret, nullptr); 175 } 176 //! wait/acquire 177 void P() { 178 while( sem_wait( &sem )!=0 ) 179 __TBB_ASSERT( errno==EINTR, nullptr); 180 } 181 //! post/release 182 void V() { sem_post( &sem ); } 183 private: 184 sem_t sem; 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 200 binary_semaphore() { my_sem = CreateEventEx( nullptr, nullptr, 0, EVENT_ALL_ACCESS ); } 201 //! dtor 202 ~binary_semaphore() { CloseHandle( my_sem ); } 203 //! wait/acquire 204 void P() { WaitForSingleObjectEx( my_sem, INFINITE, FALSE ); } 205 //! post/release 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 243 binary_semaphore() { my_sem = 1; } 244 //! dtor 245 ~binary_semaphore() {} 246 //! wait/acquire 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 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 273 binary_semaphore() { 274 int ret = sem_init( &my_sem, /*shared among threads*/ 0, 0 ); 275 __TBB_ASSERT_EX( !ret, nullptr); 276 } 277 //! dtor 278 ~binary_semaphore() { 279 int ret = sem_destroy( &my_sem ); 280 __TBB_ASSERT_EX( !ret, nullptr); 281 } 282 //! wait/acquire 283 void P() { 284 while( sem_wait( &my_sem )!=0 ) 285 __TBB_ASSERT( errno==EINTR, nullptr); 286 } 287 //! post/release 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