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