]> Dogcows Code - chaz/yoink/blob - src/moof/thread.hh
prep for runloop code
[chaz/yoink] / src / moof / thread.hh
1
2 /*] Copyright (c) 2009-2010, Charles McGarvey [**************************
3 **] All rights reserved.
4 *
5 * vi:ts=4 sw=4 tw=75
6 *
7 * Distributable under the terms and conditions of the 2-clause BSD license;
8 * see the file COPYING for a complete text of the license.
9 *
10 **************************************************************************/
11
12 #ifndef _MOOF_THREAD_HH_
13 #define _MOOF_THREAD_HH_
14
15 /**
16 * \file thread.hh
17 * Light C++ wrapper around the SDL threads API.
18 */
19
20 #include "config.h"
21
22 #include <boost/bind.hpp>
23 #include <boost/function.hpp>
24 #include <SDL/SDL.h>
25
26 #include <moof/math.hh>
27
28
29 namespace moof {
30
31
32 /**
33 * Represents a thread which may be running. You cannot instantiate a
34 * thread object directly; new threads are created by detaching functions
35 * using the detach() method. Once a thread is detached, it will continue
36 * running until the function returns. You don't need to keep the thread
37 * object you want to wait() or kill() the thread later.
38 */
39 class thread
40 {
41 public:
42
43 typedef boost::function<int(void)> function;
44
45
46 /**
47 * Construct an invalid thread object which has no association with any
48 * real thread.
49 */
50 thread() :
51 thread_(0) {}
52
53 /**
54 * Execute a function in a new thread.
55 * \param function The function to execute.
56 * \return The new thread, or an invalid thread if an error occurred.
57 */
58 static thread detach(const function& function)
59 {
60 thread::function* fcopy = new thread::function(function);
61 SDL_Thread* thread = SDL_CreateThread(&thread::run, (void*)fcopy);
62 if (thread == 0) delete fcopy;
63 return moof::thread(thread);
64 }
65
66
67 /**
68 * Wait for the thread to terminate, getting its return value. The
69 * thread will be invalidated.
70 * \return The integer value returned by the detached function.
71 */
72 int wait()
73 {
74 int i;
75 SDL_WaitThread(thread_, &i);
76 thread_ = 0;
77 return i;
78 }
79
80 /**
81 * Forcefully kill the thread without giving it a chance to clean up
82 * after itself. The thread will be invalidated. Don't use this.
83 */
84 void kill()
85 {
86 SDL_KillThread(thread_);
87 thread_ = 0;
88 }
89
90 /**
91 * Get whether or not the thread object is associated with a real
92 * thread.
93 * \return True if the thread is valid, false otherwise.
94 */
95 bool is_valid() const
96 {
97 return thread_ != 0;
98 }
99
100
101 /**
102 * Get a unique identifier for this thread, if it is valid.
103 * \return The identifier.
104 */
105 uint32_t identifier() const
106 {
107 return SDL_GetThreadID(thread_);
108 }
109
110 /**
111 * Get the unique identifier of the calling thread.
112 * \return The identifier.
113 */
114 static uint32_t current_identifier()
115 {
116 return SDL_ThreadID();
117 }
118
119
120 private:
121
122 thread(SDL_Thread* thread) :
123 thread_(thread) {}
124
125 static int run(void* arg)
126 {
127 int code = (*(function*)arg)();
128 delete (function*)arg;
129 return code;
130 }
131
132
133 SDL_Thread* thread_;
134 };
135
136
137 /**
138 * An abstract class representing some task that is to be run
139 * asynchronously.
140 */
141 class async_task
142 {
143 public:
144
145 /**
146 * Deconstruct the task.
147 */
148 virtual ~async_task() {}
149
150 /**
151 * Get whether or not the task is done.
152 * \return True if the task is done, false otherwise.
153 */
154 virtual bool is_done() const = 0;
155
156 /**
157 * Begin the task.
158 */
159 virtual void run() = 0;
160
161 /**
162 * Block the current thread until the task is finished.
163 * \return A value representing the state of the finished task.
164 */
165 virtual int wait() = 0;
166 };
167
168 /**
169 * An asynchronous task that is run to be executed in a separated thread.
170 */
171 class threaded_task
172 {
173 public:
174
175 /**
176 * Get the thread object the task is executing in.
177 * \return The thread.
178 */
179 const class thread& thread() const { return thread_; }
180
181 /**
182 * Block the current thread until the task thread is finished.
183 * \return The integer value returned by the task function.
184 */
185 int wait()
186 {
187 return thread_.wait();
188 }
189
190
191 protected:
192
193 class thread thread_;
194 };
195
196
197 /**
198 * A mutex to protect sensitive sections of code from threads which might
199 * otherwise cause unpredictable results.
200 */
201 class mutex
202 {
203 public:
204
205 /**
206 * Construct a mutex.
207 */
208 mutex() :
209 mutex_(SDL_CreateMutex()) {}
210
211 /**
212 * Deconstruct a mutex.
213 */
214 ~mutex()
215 {
216 SDL_DestroyMutex(mutex_);
217 }
218
219
220 /**
221 * Block until the calling thread can secure exclusive access to the
222 * code protected by the mutex.
223 * \return True if the lock was acquired, false otherwise.
224 * \see lock
225 */
226 bool acquire_lock()
227 {
228 return (SDL_LockMutex(mutex_) == 0);
229 }
230
231 /**
232 * Unlock the mutex. Call this after the sensitive block of code to
233 * allow another thread to acquire the lock.
234 * \return True if the mutex was unlocked, false otherwise.
235 * \see lock
236 */
237 bool release_lock()
238 {
239 return (SDL_UnlockMutex(mutex_) == 0);
240 }
241
242
243 /**
244 * As an alternative method for locking, objects of this class will
245 * automagically release the lock if it is still locked at
246 * deconstruction. Therefore, it's generally safer to use this method
247 * since it makes it much more difficult to forget to unlock a mutex.
248 */
249 class lock
250 {
251 public:
252
253 /**
254 * Construct a lock.
255 * \param mutex The mutex.
256 */
257 explicit lock(mutex& mutex, bool lock = true) :
258 mutex_(mutex),
259 is_locked_(false)
260 {
261 if (lock) if (!acquire()) throw "mutex lock not acquired";
262 }
263
264 /**
265 * Deconstruct a lock. The lock is automagically released if it is
266 * still locked.
267 */
268 ~lock()
269 {
270 if (is_locked_) release();
271 }
272
273
274 /**
275 * Try to acquire a lock on the mutex.
276 * \return True if the mutex was locked, false otherwise.
277 */
278 bool acquire()
279 {
280 return (is_locked_ = mutex_.acquire_lock());
281 }
282
283 /**
284 * Release the lock.
285 * \return True if the mutex was unlocked, false otherwise.
286 */
287 bool release()
288 {
289 bool result = mutex_.release_lock();
290 is_locked_ = false;
291 return result;
292 }
293
294
295 /**
296 * Get whether or not the mutex is locked.
297 * \return True if the mutex is locked, false otherwise.
298 */
299 bool is_locked() const
300 {
301 return is_locked_;
302 }
303
304
305 protected:
306
307 mutex& mutex_;
308 bool is_locked_;
309
310 friend class condition;
311 };
312
313
314 private:
315
316 SDL_mutex* mutex_;
317
318 friend class condition;
319 };
320
321
322 /**
323 * A class representing a condition variable.
324 */
325 class condition
326 {
327 public:
328
329 /**
330 * Construct a condition.
331 */
332 condition()
333 {
334 condition_ = SDL_CreateCond();
335 }
336
337 /**
338 * Deconstruct a condition.
339 */
340 ~condition()
341 {
342 SDL_DestroyCond(condition_);
343 }
344
345
346 /**
347 * Unlock the mutex and wait for another thread to notify the thread,
348 * at which point the mutex will be re-locked and the method will
349 * return.
350 * \param mutex The mutex.
351 * \return True if the thread was notified, false otherwise.
352 */
353 bool wait(mutex& mutex)
354 {
355 return (SDL_CondWait(condition_, mutex.mutex_) == 0);
356 }
357
358 /**
359 * Unlock the mutex associated with a lock and wait for another thread
360 * to notify the thread, at which point the lock will be re-locked and
361 * the method will return.
362 * \param lock The lock.
363 * \return True if the thread was notified, false otherwise.
364 */
365 bool wait(mutex::lock& lock)
366 {
367 return (SDL_CondWait(condition_, lock.mutex_.mutex_) == 0);
368 }
369
370 /**
371 * Unlock the mutex and wait for another thread to notify the thread,
372 * at which point the mutex will be re-locked and the method will
373 * return. If the thread was not notified before a certain number of
374 * seconds, the method will return anyway.
375 * \param mutex The mutex.
376 * \param timeout Number of seconds to wait.
377 * \return True if the thread was notified, false otherwise.
378 */
379 bool wait(mutex& mutex, scalar timeout)
380 {
381 Uint32 ms = timeout * SCALAR(1000.0);
382 return (SDL_CondWaitTimeout(condition_, mutex.mutex_, ms) == 0);
383 }
384
385 /**
386 * Unlock the mutex associated with a lock and wait for another thread
387 * to notify the thread, at which point the lock will be re-locked and
388 * the method will return. If the thread was not notified before a
389 * certain number of seconds, the method will return anyway.
390 * \param lock The lock.
391 * \param timeout Number of seconds to wait.
392 * \return True if the thread was notified, false otherwise.
393 */
394 bool wait(mutex::lock& lock, scalar timeout)
395 {
396 Uint32 ms = timeout * SCALAR(1000.0);
397 return (SDL_CondWaitTimeout(condition_,
398 lock.mutex_.mutex_, ms) == 0);
399 }
400
401
402 /**
403 * Notify one other thread that is waiting on the condition.
404 * \return True on success, false otherwise.
405 */
406 bool notify()
407 {
408 return (SDL_CondSignal(condition_) == 0);
409 }
410
411 /**
412 * Notify all other threads that are waiting on the condition.
413 * \return True on success, false otherwise.
414 */
415 bool notify_all()
416 {
417 return (SDL_CondBroadcast(condition_) == 0);
418 }
419
420
421 private:
422
423 SDL_cond* condition_;
424 };
425
426
427 /**
428 * A semaphore class.
429 */
430 class semaphore
431 {
432 public:
433
434 /**
435 * Construct a semaphore.
436 * \param value The initial value of the semaphore.
437 */
438 explicit semaphore(uint32_t value)
439 {
440 semaphore_ = SDL_CreateSemaphore(value);
441 }
442
443 /**
444 * Deconstruct a semaphore.
445 */
446 ~semaphore()
447 {
448 SDL_DestroySemaphore(semaphore_);
449 }
450
451
452 /**
453 * Block until the calling thread can secure exclusive access to the
454 * code protected by the semaphore.
455 * \return True if the lock was acquired, false otherwise.
456 * \see lock
457 */
458 bool acquire_lock()
459 {
460 return (SDL_SemWait(semaphore_) == 0);
461 }
462
463 /**
464 * Block until the calling thread can secure exclusive access to the
465 * code protected by the semaphore, or until the timeout expires.
466 * \param timeout Number of seconds to try.
467 * \return True if the lock was acquired, false otherwise.
468 */
469 bool acquire_lock(scalar timeout)
470 {
471 Uint32 ms = timeout * SCALAR(1000.0);
472 return (SDL_SemWaitTimeout(semaphore_, ms) == 0);
473 }
474
475 /**
476 * Unlock the semaphore. Call this after the sensitive block of code
477 * to allow another thread to acquire the lock.
478 * \return True if the semaphore was unlocked, false otherwise.
479 * \see lock
480 */
481 bool release_lock()
482 {
483 return (SDL_SemPost(semaphore_) == 0);
484 }
485
486 /**
487 * Try to lock the semaphore, but don't block if the lock is not
488 * immediately available.
489 * \return True if the semaphore was locked, false otherwise.
490 */
491 bool try_lock()
492 {
493 return (SDL_SemTryWait(semaphore_) == 0);
494 }
495
496 /**
497 * As an alternative method for locking, objects of this class will
498 * automagically release the lock if it is still locked at
499 * deconstruction. Therefore, it's generally safer to use this method
500 * since it makes it much more difficult to forget to unlock a
501 * semaphore.
502 */
503 class lock
504 {
505 public:
506
507 /**
508 * Construct a lock.
509 * \param semaphore The semaphore.
510 */
511 explicit lock(semaphore& semaphore, bool lock = true) :
512 semaphore_(semaphore),
513 is_locked_(false)
514 {
515 if (lock) if (!acquire()) throw "semaphore lock not acquired";
516 }
517
518 /**
519 * Deconstruct a lock. The lock is automagically released if it is
520 * still locked.
521 */
522 ~lock()
523 {
524 if (is_locked_) release();
525 }
526
527
528 /**
529 * Try to acquire a lock on the semaphore.
530 * \return True if the semaphore was locked, false otherwise.
531 */
532 bool acquire()
533 {
534 return (is_locked_ = semaphore_.acquire_lock());
535 }
536
537 /**
538 * Release the lock.
539 * \return True if the semaphore was unlocked, false otherwise.
540 */
541 bool release()
542 {
543 bool result = semaphore_.release_lock();
544 is_locked_ = false;
545 return result;
546 }
547
548 /**
549 * Get whether or not the semaphore is locked.
550 * \return True if the semaphore is locked, false otherwise.
551 */
552 bool is_locked() const
553 {
554 return is_locked_;
555 }
556
557
558 protected:
559
560 semaphore& semaphore_;
561 bool is_locked_;
562 };
563
564
565 private:
566
567 SDL_sem* semaphore_;
568 };
569
570
571 #if ENABLE_THREADS
572 #define MOOF_DECLARE_MUTEX(M) moof::mutex M
573 #define MOOF_MUTEX_LOCK(M) moof::mutex::lock lock_##M(M)
574 #else
575 #define MOOF_DECLARE_MUTEX(M)
576 #define MOOF_MUTEX_LOCK(M)
577 #endif
578
579
580 } // namespace moof
581
582 #endif // _MOOF_THREAD_HH_
583
This page took 0.053129 seconds and 4 git commands to generate.