thread.hpp

Go to the documentation of this file.
00001 /* $Id: thread.hpp 23842 2008-02-16 08:47:16Z mordante $ */
00002 /*
00003    Copyright (C) 2003 - 2008 by David White <dave@whitevine.net>
00004    Part of the Battle for Wesnoth Project http://www.wesnoth.org/
00005 
00006    This program is free software; you can redistribute it and/or modify
00007    it under the terms of the GNU General Public License version 2
00008    or at your option any later version.
00009    This program is distributed in the hope that it will be useful,
00010    but WITHOUT ANY WARRANTY.
00011 
00012    See the COPYING file for more details.
00013 */
00014 
00015 #ifndef THREAD_HPP_INCLUDED
00016 #define THREAD_HPP_INCLUDED
00017 
00018 #include "SDL.h"
00019 #include "SDL_thread.h"
00020 
00021 // Threading primitives wrapper for SDL_Thread.
00022 //
00023 // This module defines primitives for wrapping C++ around SDL's threading
00024 // interface
00025 namespace threading
00026 {
00027 
00028 struct manager
00029 {
00030     ~manager();
00031 };
00032 
00033 // Threading object.
00034 //
00035 // This class defines threading objects. One such object represents a
00036 // thread and admits killing and joining on threads. Intended to be
00037 // used for manipulating threads instead of poking around with SDL_Thread
00038 // calls.
00039 class thread
00040 {
00041 public:
00042     // Construct a new thread to start executing the function
00043     // pointed to by f. The void* data will be passed to f, to
00044     // facilitate passing of parameters to f.
00045     //
00046     // \param f the function at which the thread should start executing
00047     // \param data passed to f
00048     //
00049     // \pre f != NULL
00050     explicit thread(int (*f)(void*), void* data=NULL);
00051 
00052     // Destroy the thread object. This is done by waiting on the
00053     // thread with the join() operation, thus blocking until the
00054     // thread object has finished its operation.
00055     ~thread();
00056 
00057     // Kill the thread. If the thread has already been killed, this
00058     // is a no-op.
00059     void kill();
00060 
00061     // Join (wait) on the thread to finish. When the thread finishes,
00062     // the function will return. calling wait() on an already killed
00063     // thread is a no-op.
00064     void join();
00065 
00066     void detach();
00067 
00068     Uint32 get_id() { return SDL_GetThreadID(thread_); }
00069 private:
00070     thread(const thread&);
00071     void operator=(const thread&);
00072 
00073     SDL_Thread* thread_;
00074 };
00075 
00076 inline Uint32 get_current_thread_id() { return SDL_ThreadID(); }
00077 // Binary mutexes.
00078 //
00079 // Implements an interface to binary mutexes. This class only defines the
00080 // mutex itself. Locking is handled through the friend class lock,
00081 // and monitor interfacing through condition variables is handled through
00082 // the friend class condition.
00083 class mutex
00084 {
00085 public:
00086     mutex();
00087     ~mutex();
00088 
00089     friend class lock;
00090     friend class condition;
00091 
00092 private:
00093     mutex(const mutex&);
00094     void operator=(const mutex&);
00095 
00096     SDL_mutex* const m_;
00097 };
00098 
00099 // Binary mutex locking.
00100 //
00101 // Implements a locking object for mutexes. The creation of a lock
00102 // object on a mutex will lock the mutex as long as this object is
00103 // not deleted.
00104 class lock
00105 {
00106 public:
00107     // Create a lock object on the mutex given as a parameter to
00108     // the constructor. The lock will be held for the duration
00109     // of the object existence.
00110     // If the mutex is already locked, the constructor will
00111     // block until the mutex lock can be acquired.
00112     //
00113     // \param m the mutex on which we should try to lock.
00114     explicit lock(mutex& m);
00115     // Delete the lock object, thus releasing the lock aquired
00116     // on the mutex which the lock object was created with.
00117     ~lock();
00118 private:
00119     lock(const lock&);
00120     void operator=(const lock&);
00121 
00122     mutex& m_;
00123 };
00124 
00125 // Condition variable locking.
00126 //
00127 // Implements condition variables for mutexes. A condition variable
00128 // allows you to free up a lock inside a critical section
00129 // of the code and regain it later. Condition classes only make
00130 // sense to do operations on, if one already aquired a mutex.
00131 class condition
00132 {
00133 public:
00134     condition();
00135     ~condition();
00136 
00137     // Wait on the condition. When the condition is met, you
00138     // have a lock on the mutex and can do work in the critical
00139     // section. When the condition is not met, wait blocks until
00140     // the condition is met and atomically frees up the lock on
00141     // the mutex. One will automatically regain the lock when the
00142     // thread unblocks.
00143     //
00144     // If wait returns false we have an error. In this case one cannot
00145     // assume that he has a lock on the mutex anymore.
00146     //
00147     // \param m the mutex you wish to free the lock for
00148     // \returns true: the wait was successful, false: an error occurred
00149     //
00150     // \pre You have already aquired a lock on mutex m
00151     //
00152     bool wait(const mutex& m);
00153 
00154     enum WAIT_TIMEOUT_RESULT { WAIT_OK, WAIT_TIMEOUT, WAIT_ERROR };
00155 
00156     // wait on the condition with a timeout. Basically the same as the
00157     // wait() function, but if the lock is not aquired before the
00158     // timeout, the function returns with an error.
00159     //
00160     // \param m the mutex you wish free the lock for.
00161     // \param timeout the allowed timeout in milliseconds (ms)
00162     // \returns result based on whether condition was met, it timed out,
00163     // or there was an error
00164     WAIT_TIMEOUT_RESULT wait_timeout(const mutex& m, unsigned int timeout);
00165     // signal the condition and wake up one thread waiting on the
00166     // condition. If no thread is waiting, notify_one() is a no-op.
00167     // Does not unlock the mutex.
00168     bool notify_one();
00169 
00170     // signal all threads waiting on the condition and let them contend
00171     // for the lock. This is often used when varying resource amounts are
00172     // involved and you do not know how many processes might continue.
00173     // The function should be used with care, especially if many threads are
00174     // waiting on the condition variable.
00175     bool notify_all();
00176 
00177 private:
00178     condition(const condition&);
00179     void operator=(const condition&);
00180 
00181     SDL_cond* const cond_;
00182 };
00183 
00184 //class which defines an interface for waiting on an asynchronous operation
00185 class waiter {
00186 public:
00187     enum ACTION { WAIT, ABORT };
00188 
00189     virtual ~waiter() {}
00190     virtual ACTION process() = 0;
00191 };
00192 
00193 //class which defines an asynchronous operation. Objects of this class are accessed from
00194 //both the worker thread and the calling thread, and so it has 'strange' allocation semantics.
00195 //It is allocated by the caller, and generally deleted by the caller. However, in some cases
00196 //the asynchronous operation is aborted, and the caller abandons it. The caller cannot still
00197 //delete the operation, since the worker thread might still access it, so in the case when the
00198 //operation is aborted, the worker thread will delete it.
00199 //
00200 //The caller should hold these objects using the async_operation_holder class below, which will
00201 //handle the delete semantics
00202 class async_operation
00203 {
00204 public:
00205 
00206     enum RESULT { COMPLETED, ABORTED };
00207 
00208     async_operation() : 
00209         aborted_(false), finished_(), finishedVar_(false), mutex_() {}
00210     virtual ~async_operation() {}
00211 
00212     RESULT execute(waiter& wait);
00213 
00214     mutex& get_mutex() { return mutex_; }
00215 
00216     virtual void run() = 0;
00217 
00218     //notify that the operation is finished. Can be called from within the thread
00219     //while holding the mutex and after checking is_aborted()
00220     //if we want to be sure that if the operation is completed, the caller is notified.
00221     //will be called in any case after the operation returns
00222     bool notify_finished();
00223 
00224     //must hold the mutex before calling this function from the worker thread
00225     bool is_aborted() const { return aborted_; }
00226 
00227 private:
00228     bool aborted_;
00229     condition finished_;
00230     bool finishedVar_;
00231     mutex mutex_;
00232 };
00233 
00234 //T should be a type derived from async_operation
00235 template<typename T>
00236 class async_operation_holder
00237 {
00238 public:
00239     explicit async_operation_holder(T* op) : op_(op)
00240     {}
00241 
00242     ~async_operation_holder() {
00243         //it's okay to call is_aborted() without the mutex here,
00244         //because we are in the calling thread, not the worker thread
00245         if(op_->is_aborted() == false) {
00246             delete op_;
00247         }
00248     }
00249 
00250     T& operation() const { return *op_; }
00251 
00252 private:
00253     T* const op_;
00254 };
00255 
00256 }
00257 
00258 #endif

Generated by doxygen 1.5.5 on 23 May 2008 for The Battle for Wesnoth
Gna! | Forum | Wiki | CIA | devdocs