1 #ifndef _THREADUTILS_H_ 2 #define _THREADUTILS_H_ 3 4 #include <stdexcept> 5 #include <sys/time.h> 6 #include <type_traits> 7 8 #include "tinycthread.h" 9 #include "timeconv.h" 10 11 class ConditionVariable; 12 13 class Mutex { 14 friend class ConditionVariable; 15 tct_mtx_t _m; 16 17 public: 18 // type must be one of: 19 // 20 // * mtx_plain for a simple non-recursive mutex 21 // * mtx_timed for a non-recursive mutex that supports timeout 22 // * mtx_try for a non-recursive mutex that supports test and return 23 // * mtx_plain | mtx_recursive (same as mtx_plain, but recursive) 24 // * mtx_timed | mtx_recursive (same as mtx_timed, but recursive) 25 // * mtx_try | mtx_recursive (same as mtx_try, but recursive) 26 // 27 // (although mtx_timed seems not to be actually implemented) Mutex(int type)28 Mutex(int type) { 29 if (tct_mtx_init(&_m, type) != tct_thrd_success) { 30 throw std::runtime_error("Mutex creation failed"); 31 } 32 } 33 34 // Make non-copyable 35 Mutex(const Mutex&) = delete; 36 Mutex& operator=(const Mutex&) = delete; 37 ~Mutex()38 virtual ~Mutex() { 39 tct_mtx_destroy(&_m); 40 } 41 lock()42 void lock() { 43 if (tct_mtx_lock(&_m) != tct_thrd_success) { 44 throw std::runtime_error("Mutex failed to lock"); 45 } 46 } 47 tryLock()48 bool tryLock() { 49 int res = tct_mtx_trylock(&_m); 50 if (res == tct_thrd_success) { 51 return true; 52 } else if (res == tct_thrd_busy) { 53 return false; 54 } else { 55 throw std::runtime_error("Mutex failed to trylock"); 56 } 57 } 58 unlock()59 void unlock() { 60 if (tct_mtx_unlock(&_m) != tct_thrd_success) { 61 throw std::runtime_error("Mutex failed to unlock"); 62 } 63 } 64 }; 65 66 class Guard { 67 Mutex* _mutex; 68 69 public: Guard(Mutex * mutex)70 Guard(Mutex* mutex) : _mutex(mutex) { 71 _mutex->lock(); 72 } 73 74 // Make non-copyable 75 Guard(const Guard&) = delete; 76 Guard& operator=(const Guard&) = delete; 77 ~Guard()78 ~Guard() { 79 _mutex->unlock(); 80 } 81 }; 82 83 class ConditionVariable { 84 tct_mtx_t* _m; 85 tct_cnd_t _c; 86 87 public: ConditionVariable(Mutex & mutex)88 ConditionVariable(Mutex& mutex) : _m(&mutex._m) { 89 // If time_t isn't integral, our addSeconds logic needs to change, 90 // as it relies on casting to time_t being a truncation. 91 if (!std::is_integral<time_t>::value) 92 throw std::runtime_error("Integral time_t type expected"); 93 // If time_t isn't signed, our addSeconds logic can't handle 94 // negative values for secs. 95 if (!std::is_signed<time_t>::value) 96 throw std::runtime_error("Signed time_t type expected"); 97 98 if (tct_cnd_init(&_c) != tct_thrd_success) 99 throw std::runtime_error("Condition variable failed to initialize"); 100 } 101 102 // Make non-copyable 103 ConditionVariable(const ConditionVariable&) = delete; 104 ConditionVariable& operator=(const ConditionVariable&) = delete; 105 ~ConditionVariable()106 virtual ~ConditionVariable() { 107 tct_cnd_destroy(&_c); 108 } 109 110 // Unblocks one thread (if any are waiting) signal()111 void signal() { 112 if (tct_cnd_signal(&_c) != tct_thrd_success) 113 throw std::runtime_error("Condition variable failed to signal"); 114 } 115 116 // Unblocks all waiting threads broadcast()117 void broadcast() { 118 if (tct_cnd_broadcast(&_c) != tct_thrd_success) 119 throw std::runtime_error("Condition variable failed to broadcast"); 120 } 121 wait()122 void wait() { 123 if (tct_cnd_wait(&_c, _m) != tct_thrd_success) 124 throw std::runtime_error("Condition variable failed to wait"); 125 } 126 timedwait(double timeoutSecs)127 bool timedwait(double timeoutSecs) { 128 timespec ts; 129 if (timespec_get(&ts, TIME_UTC) != TIME_UTC) { 130 throw std::runtime_error("timespec_get failed"); 131 } 132 133 ts = addSeconds(ts, timeoutSecs); 134 135 int res = tct_cnd_timedwait(&_c, _m, &ts); 136 if (res == tct_thrd_success) { 137 return true; 138 } else if (res == tct_thrd_timedout) { 139 return false; 140 } else { 141 throw std::runtime_error("Condition variable failed to timedwait"); 142 } 143 } 144 }; 145 146 #endif // _THREADUTILS_H_ 147