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