b09da7fcef266821e83eed7b5798368dc1b0b570
[awesomized/libmemcached] / libtest / thread.hpp
1 /* vim:expandtab:shiftwidth=2:tabstop=2:smarttab:
2 *
3 * Data Differential YATL (i.e. libtest) library
4 *
5 * Copyright (C) 2012 Data Differential, http://datadifferential.com/
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions are
9 * met:
10 *
11 * * Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
13 *
14 * * Redistributions in binary form must reproduce the above
15 * copyright notice, this list of conditions and the following disclaimer
16 * in the documentation and/or other materials provided with the
17 * distribution.
18 *
19 * * The names of its contributors may not be used to endorse or
20 * promote products derived from this software without specific prior
21 * written permission.
22 *
23 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
24 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
25 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
26 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
27 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
28 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
29 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
30 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
31 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
32 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
33 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
34 *
35 */
36
37 #pragma once
38
39 #include <pthread.h>
40
41 namespace libtest
42 {
43 namespace thread
44 {
45
46 class Mutex
47 {
48 public:
49 Mutex() :
50 _err(0)
51 {
52 _err= pthread_mutex_init(&_mutex, NULL);
53 }
54
55 ~Mutex()
56 {
57 if ((_err= pthread_mutex_destroy(&_mutex)))
58 {
59 throw libtest::fatal(LIBYATL_DEFAULT_PARAM, "pthread_cond_destroy: %s", strerror(_err));
60 }
61 }
62
63 pthread_mutex_t* handle()
64 {
65 if (_err != 0)
66 {
67 throw libtest::fatal(LIBYATL_DEFAULT_PARAM, "pthread_mutex_init: %s", strerror(_err));
68 }
69
70 return &_mutex;
71 }
72
73 private:
74 int _err;
75 pthread_mutex_t _mutex;
76 };
77
78 class ScopedLock
79 {
80 public:
81 ScopedLock(Mutex& mutex_) :
82 _mutex(mutex_)
83 {
84 init();
85 }
86
87 ~ScopedLock()
88 {
89 int err;
90 if ((err= pthread_mutex_unlock(_mutex.handle())))
91 {
92 throw libtest::fatal(LIBYATL_DEFAULT_PARAM, "pthread_mutex_unlock: %s", strerror(err));
93 }
94 }
95
96 Mutex* handle()
97 {
98 return &_mutex;
99 }
100
101 private:
102 void init()
103 {
104 int err;
105 if ((err= pthread_mutex_lock(_mutex.handle())))
106 {
107 throw libtest::fatal(LIBYATL_DEFAULT_PARAM, "pthread_mutex_lock: %s", strerror(err));
108 }
109 }
110
111 private:
112 Mutex& _mutex;
113 };
114
115 class Condition
116 {
117 public:
118 Condition()
119 {
120 int err;
121 if ((err= pthread_cond_init(&_cond, NULL)))
122 {
123 throw libtest::fatal(LIBYATL_DEFAULT_PARAM, "pthread_mutex_init: %s", strerror(err));
124 }
125 }
126
127 ~Condition()
128 {
129 int err;
130 if ((err= pthread_cond_destroy(&_cond)))
131 {
132 throw libtest::fatal(LIBYATL_DEFAULT_PARAM, "pthread_cond_destroy: %s", strerror(err));
133 }
134 }
135
136 void broadcast()
137 {
138 int err;
139 if ((err= pthread_cond_broadcast(&_cond)))
140 {
141 throw libtest::fatal(LIBYATL_DEFAULT_PARAM, "pthread_cond_broadcast: %s", strerror(err));
142 }
143 }
144
145 void signal()
146 {
147 int err;
148 if ((err= pthread_cond_signal(&_cond)))
149 {
150 throw libtest::fatal(LIBYATL_DEFAULT_PARAM, "pthread_cond_broadcast: %s", strerror(err));
151 }
152 }
153
154 void wait(ScopedLock& lock_)
155 {
156 int err;
157 if ((err= pthread_cond_wait(&_cond, lock_.handle()->handle())))
158 {
159 throw libtest::fatal(LIBYATL_DEFAULT_PARAM, "pthread_cond_wait: %s", strerror(err));
160 }
161 }
162
163 private:
164 pthread_cond_t _cond;
165 };
166
167 class Barrier
168 {
169 public:
170 explicit Barrier(uint32_t count):
171 _threshold(count),
172 _count(count),
173 _generation(0)
174 {
175 if (_count == 0)
176 {
177 fatal_assert("Zero is an invalid value");
178 }
179 }
180
181 ~Barrier()
182 {
183 }
184
185 bool wait()
186 {
187 ScopedLock l(_mutex);
188 uint32_t gen = _generation;
189
190 if (--_count == 0)
191 {
192 _generation++;
193 _count = _threshold;
194 _cond.broadcast();
195
196 return true;
197 }
198
199 while (gen == _generation)
200 {
201 _cond.wait(l);
202 }
203
204 return false;
205 }
206
207 private:
208 Mutex _mutex;
209 Condition _cond;
210 uint32_t _threshold;
211 uint32_t _count;
212 uint32_t _generation;
213 };
214
215 class Thread
216 {
217 private:
218 typedef void *(*start_routine_fn) (void *);
219
220 public:
221 template <class Function,class Arg1>
222 Thread(Function func, Arg1 arg):
223 _joined(false),
224 _func((start_routine_fn)func),
225 _context(arg)
226 {
227 int err;
228 if ((err= pthread_create(&_thread, NULL, entry_func, (void*)this)))
229 {
230 throw libtest::fatal(LIBYATL_DEFAULT_PARAM, "pthread_create: %s", strerror(err));
231 }
232 _owner= pthread_self();
233 }
234
235 bool running() const
236 {
237 return (pthread_kill(_thread, 0) == 0);
238 }
239
240 bool detached()
241 {
242 if (EDEADLK == pthread_join(_thread, NULL))
243 {
244 return true;
245 }
246
247 /* Result of pthread_join was EINVAL == detached thread */
248 return false;
249 }
250
251 bool join()
252 {
253 if (_thread == pthread_self())
254 {
255 throw libtest::fatal(LIBYATL_DEFAULT_PARAM, "Thread cannot join on itself");
256 }
257
258 if (_owner != pthread_self())
259 {
260 throw libtest::fatal(LIBYATL_DEFAULT_PARAM, "Attempt made by a non-owner thead to join on thread");
261 }
262
263 bool ret= false;
264 {
265 ScopedLock l(_join_mutex);
266 if (_joined == false)
267 {
268 int err;
269 if ((err= pthread_join(_thread, NULL)))
270 {
271 switch(err)
272 {
273 case EINVAL:
274 break;
275
276 case ESRCH:
277 ret= true;
278 break;
279
280 case EDEADLK:
281 default:
282 throw libtest::fatal(LIBYATL_DEFAULT_PARAM, "pthread_join: %s", strerror(err));
283 }
284 }
285 else
286 {
287 ret= true;
288 }
289
290 _joined= true;
291 }
292 }
293
294 return ret;
295 }
296
297 ~Thread()
298 {
299 join();
300 }
301
302 protected:
303 void run()
304 {
305 _func(_context);
306 }
307
308 private:
309 static void * entry_func(void* This)
310 {
311 ((Thread *)This)->run();
312 return NULL;
313 }
314
315 private:
316 bool _joined;
317 pthread_t _thread;
318 pthread_t _owner;
319 start_routine_fn _func;
320 void* _context;
321 Mutex _join_mutex;
322 };
323
324 } // namespace thread
325 } // namespace libtest