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