4888ab6408be05225dc9fe880730f9bd4fe474ba
[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 Mf {
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 mThread(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 Function* fcopy = new Function(function);
59 SDL_Thread* thread = SDL_CreateThread(&Thread::run, (void*)fcopy);
60 if (thread == 0) delete fcopy;
61 return 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(mThread, &i);
74 mThread = 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(mThread);
85 mThread = 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 isValid() const
94 {
95 return mThread != 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(mThread);
106 }
107
108 /**
109 * Get the unique identifier of the calling thread.
110 * \return The identifier.
111 */
112 static uint32_t currentIdentifier()
113 {
114 return SDL_ThreadID();
115 }
116
117
118 private:
119
120 Thread(SDL_Thread* thread) :
121 mThread(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* mThread;
132 };
133
134
135 /**
136 * An abstract class representing some task that is to be run
137 * asynchronously.
138 */
139 class AsyncTask
140 {
141 public:
142
143 /**
144 * Deconstruct the task.
145 */
146 virtual ~AsyncTask() {}
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 isDone() 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 ThreadedTask
170 {
171 public:
172
173 /**
174 * Get the thread object the task is executing in.
175 * \return The thread.
176 */
177 const Thread& thread() const { return mThread; }
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 mThread.wait();
186 }
187
188
189 protected:
190
191 Thread mThread;
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 mMutex(SDL_CreateMutex()) {}
208
209 /**
210 * Deconstruct a mutex.
211 */
212 ~Mutex()
213 {
214 SDL_DestroyMutex(mMutex);
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 acquireLock()
225 {
226 return (SDL_LockMutex(mMutex) == 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 releaseLock()
236 {
237 return (SDL_UnlockMutex(mMutex) == 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 Lock(Mutex& mutex) :
256 mMutex(mutex),
257 mIsLocked(false) {}
258
259 /**
260 * Deconstruct a lock. The lock is automagically released if it is
261 * still locked.
262 */
263 ~Lock()
264 {
265 if (mIsLocked) 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 (mIsLocked = mMutex.acquireLock());
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 = mMutex.releaseLock();
285 mIsLocked = 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 isLocked() const
295 {
296 return mIsLocked;
297 }
298
299
300 protected:
301
302 Mutex& mMutex;
303 bool mIsLocked;
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 ScopedLock : private Lock
313 {
314 public:
315
316 /**
317 * Construct a lock.
318 * \param mutex The mutex.
319 */
320 ScopedLock(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 isLocked() const
331 {
332 return Lock::isLocked();
333 }
334 };
335
336
337 private:
338
339 SDL_mutex* mMutex;
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 mCondition = SDL_CreateCond();
358 }
359
360 /**
361 * Deconstruct a condition.
362 */
363 ~Condition()
364 {
365 SDL_DestroyCond(mCondition);
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(mCondition, mutex.mMutex) == 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(mCondition, lock.mMutex.mMutex) == 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(mCondition, mutex.mMutex, 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(mCondition,
421 lock.mMutex.mMutex, 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(mCondition) == 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 notifyAll()
439 {
440 return (SDL_CondBroadcast(mCondition) == 0);
441 }
442
443
444 private:
445
446 SDL_cond* mCondition;
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 Semaphore(uint32_t value)
462 {
463 mSemaphore = SDL_CreateSemaphore(value);
464 }
465
466 /**
467 * Deconstruct a semaphore.
468 */
469 ~Semaphore()
470 {
471 SDL_DestroySemaphore(mSemaphore);
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 acquireLock()
482 {
483 return (SDL_SemWait(mSemaphore) == 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 acquireLock(Scalar timeout)
493 {
494 Uint32 ms = timeout * SCALAR(1000.0);
495 return (SDL_SemWaitTimeout(mSemaphore, 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 releaseLock()
505 {
506 return (SDL_SemPost(mSemaphore) == 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 tryLock()
515 {
516 return (SDL_SemTryWait(mSemaphore) == 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 Lock(Semaphore& semaphore) :
535 mSemaphore(semaphore),
536 mIsLocked(false) {}
537
538 /**
539 * Deconstruct a lock. The lock is automagically released if it is
540 * still locked.
541 */
542 ~Lock()
543 {
544 if (mIsLocked) 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 (mIsLocked = mSemaphore.acquireLock());
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 = mSemaphore.releaseLock();
564 mIsLocked = 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 isLocked() const
573 {
574 return mIsLocked;
575 }
576
577
578 protected:
579
580 Semaphore& mSemaphore;
581 bool mIsLocked;
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 ScopedLock : private Lock
589 {
590 public:
591
592 /**
593 * Construct a lock.
594 * \param semaphore The semaphore.
595 */
596 ScopedLock(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 isLocked() const
607 {
608 return Lock::isLocked();
609 }
610 };
611
612
613 private:
614
615 SDL_sem* mSemaphore;
616 };
617
618
619 } // namespace Mf
620
621 #endif // _MOOF_THREAD_HH_
622
This page took 0.053134 seconds and 3 git commands to generate.