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