1#pragma once 2 3#if __cplusplus < 201103L 4#error "C++ version lower than C++11" 5#endif 6 7//#if defined(RT_USING_PTHREADS) 8 9#include <pthread.h> 10 11#include <system_error> 12#include <chrono> 13#include <utility> 14#include <functional> 15 16#include "__utils.h" 17 18#define rt_cpp_mutex_t pthread_mutex_t 19 20namespace std 21{ 22 // Base class on which to build std::mutex and std::timed_mutex 23 class __mutex_base 24 { 25 protected: 26 typedef rt_cpp_mutex_t __native_type; 27 28 __native_type _m_mutex = PTHREAD_MUTEX_INITIALIZER; 29 30 constexpr __mutex_base() noexcept = default; 31 __mutex_base(const __mutex_base&) = delete; 32 __mutex_base& operator=(const __mutex_base&) = delete; 33 }; 34 35 36 class mutex : private __mutex_base 37 { 38 public: 39 constexpr mutex() = default; 40 ~mutex() = default; 41 42 mutex(const mutex&) = delete; 43 mutex& operator=(const mutex&) = delete; 44 45 void lock() 46 { 47 int err = pthread_mutex_lock(&_m_mutex); 48 49 if (err) 50 { 51 throw_system_error(err, "mutex:lock failed."); 52 } 53 } 54 55 bool try_lock() noexcept 56 { 57 return !pthread_mutex_trylock(&_m_mutex); 58 } 59 60 void unlock() noexcept 61 { 62 pthread_mutex_unlock(&_m_mutex); 63 } 64 65 typedef __native_type* native_handle_type; 66 67 native_handle_type native_handle() 68 { 69 return &_m_mutex; 70 }; 71 72 }; 73 74 inline int __rt_cpp_recursive_mutex_init(rt_cpp_mutex_t* m) 75 { 76 pthread_mutexattr_t attr; 77 int res; 78 79 res = pthread_mutexattr_init(&attr); 80 if (res) 81 return res; 82 res = pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE); 83 if (res) 84 goto attr_cleanup; 85 res = pthread_mutex_init(m, &attr); 86 87 attr_cleanup: 88 int err = pthread_mutexattr_destroy(&attr); 89 return res ? res : err; 90 } 91 92 class __recursive_mutex_base 93 { 94 protected: 95 typedef rt_cpp_mutex_t __native_type; 96 97 __native_type _m_recursive_mutex; 98 99 __recursive_mutex_base(const __recursive_mutex_base&) = delete; 100 __recursive_mutex_base& operator=(const __recursive_mutex_base&) = delete; 101 102 __recursive_mutex_base() 103 { 104 int err = __rt_cpp_recursive_mutex_init(&_m_recursive_mutex); 105 if (err) 106 throw_system_error(err, "Recursive mutex failed to construct"); 107 } 108 109 ~__recursive_mutex_base() 110 { 111 pthread_mutex_destroy(&_m_recursive_mutex); 112 } 113 }; 114 115 class recursive_mutex : private __recursive_mutex_base 116 { 117 public: 118 typedef __native_type* native_handle_type; 119 recursive_mutex() = default; 120 ~recursive_mutex() = default; 121 122 recursive_mutex(const recursive_mutex&) = delete; 123 recursive_mutex& operator=(const recursive_mutex&) = delete; 124 void lock() 125 { 126 int err = pthread_mutex_lock(&_m_recursive_mutex); 127 128 if (err) 129 throw_system_error(err, "recursive_mutex::lock failed"); 130 } 131 132 bool try_lock() noexcept 133 { 134 return !pthread_mutex_trylock(&_m_recursive_mutex); 135 } 136 137 void unlock() noexcept 138 { 139 pthread_mutex_unlock(&_m_recursive_mutex); 140 } 141 142 native_handle_type native_handle() 143 { return &_m_recursive_mutex; } 144 }; 145 146#ifdef RT_PTHREAD_TIMED_MUTEX 147 148 class timed_mutex; 149 150 class recursive_timed_mutex; 151 152#endif // RT_PTHREAD_TIMED_MUTEX 153 154 155 struct defer_lock_t {}; 156 struct try_to_lock_t {}; 157 struct adopt_lock_t {}; // take ownership of a locked mtuex 158 159 constexpr defer_lock_t defer_lock { }; 160 constexpr try_to_lock_t try_to_lock { }; 161 constexpr adopt_lock_t adopt_lock { }; 162 163 template <class Mutex> 164 class lock_guard 165 { 166 public: 167 typedef Mutex mutex_type; 168 169 explicit lock_guard(mutex_type& m) : pm(m) { pm.lock(); } 170 lock_guard(mutex_type& m, adopt_lock_t) noexcept : pm(m) 171 { } 172 ~lock_guard() 173 { pm.unlock(); } 174 175 lock_guard(lock_guard const&) = delete; 176 lock_guard& operator=(lock_guard const&) = delete; 177 178 private: 179 mutex_type& pm; 180 181 }; 182 183 template <class Mutex> 184 class unique_lock 185 { 186 public: 187 typedef Mutex mutex_type; 188 189 unique_lock() noexcept : pm(nullptr), owns(false) { } 190 191 explicit unique_lock(mutex_type& m) 192 : pm(std::addressof(m)), owns(false) 193 { 194 lock(); 195 owns = true; 196 } 197 198 unique_lock(mutex_type& m, defer_lock_t) noexcept 199 : pm(std::addressof(m)), owns(false) 200 { } 201 202 unique_lock(mutex_type& m, try_to_lock_t) noexcept 203 : pm(std::addressof(m)), owns(pm->try_lock()) 204 { } 205 206 unique_lock(mutex_type& m, adopt_lock_t) noexcept 207 : pm(std::addressof(m)), owns(true) 208 { } 209 210 // any lock-involving timed mutex API is currently only for custom implementations 211 // the standard ones are not available 212 template <class Clock, class Duration> 213 unique_lock(mutex_type& m, const chrono::time_point<Clock, Duration>& abs_time) noexcept 214 : pm(std::addressof(m)), owns(pm->try_lock_until(abs_time)) 215 { } 216 217 template <class Rep, class Period> 218 unique_lock(mutex_type& m, const chrono::duration<Rep, Period>& rel_time) noexcept 219 : pm(std::addressof(m)), owns(pm->try_lock_for(rel_time)) 220 { } 221 222 ~unique_lock() 223 { 224 if (owns) 225 unlock(); 226 } 227 228 unique_lock(unique_lock const&) = delete; 229 unique_lock& operator=(unique_lock const&) = delete; 230 231 unique_lock(unique_lock&& u) noexcept 232 : pm(u.pm), owns(u.owns) 233 { 234 u.pm = nullptr; 235 u.owns = false; 236 } 237 238 unique_lock& operator=(unique_lock&& u) noexcept 239 { 240 if (owns) 241 unlock(); 242 243 unique_lock(std::move(u)).swap(*this); 244 245 u.pm = nullptr; 246 u.owns = false; 247 248 return *this; 249 } 250 251 void lock() 252 { 253 if (!pm) 254 throw_system_error(int(errc::operation_not_permitted), 255 "unique_lock::lock: references null mutex"); 256 else if (owns) 257 throw_system_error(int(errc::resource_deadlock_would_occur), 258 "unique_lock::lock: already locked" ); 259 else { 260 pm->lock(); 261 owns = true; 262 } 263 } 264 265 bool try_lock() 266 { 267 if (!pm) 268 throw_system_error(int(errc::operation_not_permitted), 269 "unique_lock::try_lock: references null mutex"); 270 else if (owns) 271 throw_system_error(int(errc::resource_deadlock_would_occur), 272 "unique_lock::try_lock: already locked" ); 273 else { 274 owns = pm->try_lock(); 275 } 276 return owns; 277 } 278 279 template <class Rep, class Period> 280 bool try_lock_for(const chrono::duration<Rep, Period>& rel_time) 281 { 282 if (!pm) 283 throw_system_error(int(errc::operation_not_permitted), 284 "unique_lock::try_lock_for: references null mutex"); 285 else if (owns) 286 throw_system_error(int(errc::resource_deadlock_would_occur), 287 "unique_lock::try_lock_for: already locked"); 288 else { 289 owns = pm->try_lock_for(rel_time); 290 } 291 return owns; 292 } 293 294 template <class Clock, class Duration> 295 bool try_lock_until(const chrono::time_point<Clock, Duration>& abs_time) 296 { 297 if (!pm) 298 throw_system_error(int(errc::operation_not_permitted), 299 "unique_lock::try_lock_until: references null mutex"); 300 else if (owns) 301 throw_system_error(int(errc::resource_deadlock_would_occur), 302 "unique_lock::try_lock_until: already locked"); 303 else { 304 owns = pm->try_lock_until(abs_time); 305 } 306 return owns; 307 } 308 309 void unlock() 310 { 311 if (!owns) 312 throw_system_error(int(errc::operation_not_permitted), 313 "unique_lock::unlock: not locked"); 314 else { 315 pm->unlock(); 316 owns = false; 317 } 318 } 319 320 void swap(unique_lock& u) noexcept 321 { 322 std::swap(pm, u.pm); 323 std::swap(owns, u.owns); 324 } 325 326 mutex_type *release() noexcept 327 { 328 mutex_type* ret_mutex = pm; 329 pm = nullptr; 330 owns = false; 331 332 return ret_mutex; 333 } 334 335 bool owns_lock() const noexcept 336 { return owns; } 337 338 explicit operator bool() const noexcept 339 { return owns_lock(); } 340 341 mutex_type* mutex() const noexcept 342 { return pm; } 343 344 345 private: 346 mutex_type *pm; 347 bool owns; 348 }; 349 350 template <class Mutex> 351 void swap(unique_lock<Mutex>& x, unique_lock<Mutex>& y) 352 { 353 x.swap(y); 354 } 355 356 template <class L0, class L1> 357 int try_lock(L0& l0, L1& l1) 358 { 359 unique_lock<L0> u0(l0, try_to_lock); // try to lock the first Lockable 360 // using unique_lock since we don't want to unlock l0 manually if l1 fails to lock 361 if (u0.owns_lock()) 362 { 363 if (l1.try_lock()) // lock the second one 364 { 365 u0.release(); // do not let RAII of a unique_lock unlock l0 366 return -1; 367 } 368 else 369 return 1; 370 } 371 return 0; 372 } 373 374 375 template <class L0, class L1, class L2, class... L3> 376 int try_lock(L0& l0, L1& l1, L2& l2, L3&... l3) 377 { 378 int r = 0; 379 unique_lock<L0> u0(l0, try_to_lock); 380 // automatically unlock is done through RAII of unique_lock 381 if (u0.owns_lock()) 382 { 383 r = try_lock(l1, l2, l3...); 384 if (r == -1) 385 u0.release(); 386 else 387 ++r; 388 } 389 return r; 390 } 391 392 template <class L0, class L1, class L2, class ...L3> 393 void 394 __lock_first(int i, L0& l0, L1& l1, L2& l2, L3&... l3) 395 { 396 while (true) 397 { 398 // we first lock the one that is the most difficult to lock 399 switch (i) 400 { 401 case 0: 402 { 403 unique_lock<L0> u0(l0); 404 i = try_lock(l1, l2, l3...); 405 if (i == -1) 406 { 407 u0.release(); 408 return; 409 } 410 } 411 ++i; 412 sched_yield(); 413 break; 414 case 1: 415 { 416 unique_lock<L1> u1(l1); 417 i = try_lock(l2, l3..., l0); 418 if (i == -1) 419 { 420 u1.release(); 421 return; 422 } 423 } 424 if (i == sizeof...(L3) + 1) // all except l0 are locked 425 i = 0; 426 else 427 i += 2; // since i was two-based above 428 sched_yield(); 429 break; 430 default: 431 __lock_first(i - 2, l2, l3..., l0, l1); 432 return; 433 } 434 } 435 } 436 437 438 template <class L0, class L1> 439 void lock(L0& l0, L1& l1) 440 { 441 while (true) 442 { 443 { 444 unique_lock<L0> u0(l0); 445 if (l1.try_lock()) 446 { 447 u0.release(); 448 break; 449 } 450 } 451 sched_yield(); 452 // wait and try the other way 453 { 454 unique_lock<L1> u1(l1); 455 if (l0.try_lock()) 456 { 457 u1.release(); 458 break; 459 } 460 } 461 sched_yield(); 462 } 463 } 464 465 template <class L0, class L1, class... L2> 466 void lock(L0& l0, L1& l1, L2&... l2) 467 { 468 __lock_first(0, l0, l1, l2...); 469 } 470 471 struct once_flag 472 { 473 constexpr once_flag() noexcept = default; 474 475 once_flag(const once_flag&) = delete; 476 once_flag& operator=(const once_flag&) = delete; 477 478 template <class Callable, class... Args> 479 friend void call_once(once_flag& flag, Callable&& func, Args&&... args); 480 481 private: 482 pthread_once_t _m_once = PTHREAD_ONCE_INIT; 483 }; 484 485 mutex& get_once_mutex(); 486 extern function<void()> once_functor; 487 extern void set_once_functor_lock_ptr(unique_lock<mutex>*); 488 489 extern "C" void once_proxy(); // passed into pthread_once 490 491 template <class Callable, class... Args> 492 void call_once(once_flag& flag, Callable&& func, Args&&... args) 493 { 494 // use a lock to ensure the call to the functor 495 // is exclusive to only the first calling thread 496 unique_lock<mutex> functor_lock(get_once_mutex()); 497 498 auto call_wrapper = std::bind(std::forward<Callable>(func), std::forward<Args>(args)...); 499 once_functor = [&]() { call_wrapper(); }; 500 501 set_once_functor_lock_ptr(&functor_lock); // so as to unlock when actually calling 502 503 int err = pthread_once(&flag._m_once, &once_proxy); 504 505 if (functor_lock) 506 set_once_functor_lock_ptr(nullptr); 507 if (err) 508 throw_system_error(err, "call_once failed"); 509 } 510} 511 512//#endif //(RT_USING_PTHREADS)