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 |