Equalizer 1.0
|
00001 00002 /* Copyright (c) 2005-2011, Stefan Eilemann <eile@equalizergraphics.com> 00003 * 00004 * This library is free software; you can redistribute it and/or modify it under 00005 * the terms of the GNU Lesser General Public License version 2.1 as published 00006 * by the Free Software Foundation. 00007 * 00008 * This library is distributed in the hope that it will be useful, but WITHOUT 00009 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS 00010 * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more 00011 * details. 00012 * 00013 * You should have received a copy of the GNU Lesser General Public License 00014 * along with this library; if not, write to the Free Software Foundation, Inc., 00015 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 00016 */ 00017 00018 #ifndef COBASE_THREAD_H 00019 #define COBASE_THREAD_H 00020 00021 #include <co/base/api.h> // COBASE_API definition 00022 #include <co/base/debug.h> // debug macros in thread-safety checks 00023 #include <co/base/lock.h> // member 00024 #include <co/base/monitor.h> // member 00025 #include <co/base/threadID.h> // member 00026 00027 #include <ostream> 00028 00029 namespace co 00030 { 00031 namespace base 00032 { 00033 class ExecutionListener; 00034 00036 class Thread 00037 { 00038 public: 00040 COBASE_API Thread(); 00041 00043 COBASE_API virtual ~Thread(); 00044 00056 COBASE_API bool start(); 00057 00069 virtual bool init(){ return true; } 00070 00079 virtual void run() = 0; 00080 00089 COBASE_API virtual void exit(); 00090 00097 COBASE_API void cancel(); 00098 00105 COBASE_API bool join(); 00106 00116 bool isStopped() const { return ( _state == STATE_STOPPED ); } 00117 00127 bool isRunning() const { return ( _state == STATE_RUNNING ); } 00128 00136 COBASE_API bool isCurrent() const; 00137 00144 COBASE_API static void addListener( ExecutionListener* listener ); 00145 00152 COBASE_API static bool removeListener( ExecutionListener* listener ); 00153 00155 COBASE_API static void removeAllListeners(); 00156 00158 COBASE_API static ThreadID getSelfThreadID(); 00159 00161 COBASE_API void untrack(); 00162 00164 COBASE_API static void yield(); 00165 00167 static void pinCurrentThread(); 00168 00170 COBASE_API static void setName( const std::string& name ); 00171 00172 private: 00173 ThreadID _id; 00174 00176 enum State 00177 { 00178 STATE_STOPPED, 00179 STATE_STARTING, // start() in progress 00180 STATE_RUNNING, 00181 STATE_STOPPING // child no longer active, join() not yet called 00182 }; 00183 00184 Monitor< State > _state; 00185 00186 static void* runChild( void* arg ); 00187 void _runChild(); 00188 00189 void _installCleanupHandler(); 00190 00191 static void _notifyStarted(); 00192 static void _notifyStopping(); 00193 friend void _notifyStopping( void* ); 00194 00195 friend std::ostream& operator << ( std::ostream& os, const Thread* ); 00196 }; 00197 00199 std::ostream& operator << ( std::ostream& os, const Thread* thread ); 00200 00201 // thread-safety checks 00202 // These checks are for development purposes, to check that certain objects are 00203 // properly used within the framework. Leaving them enabled during application 00204 // development may cause false positives, e.g., when threadsafety is ensured 00205 // outside of the objects by the application. 00206 00207 #ifndef NDEBUG 00208 # define EQ_CHECK_THREADSAFETY 00209 #endif 00210 00212 #define EQ_TS_VAR( NAME ) \ 00213 public: \ 00214 struct NAME ## Struct \ 00215 { \ 00216 NAME ## Struct () \ 00217 : extMutex( false ), inRegion( false ) \ 00218 {} \ 00219 mutable co::base::ThreadID id; \ 00220 mutable std::string name; \ 00221 bool extMutex; \ 00222 mutable bool inRegion; \ 00223 } NAME; \ 00224 private: 00225 00226 #ifdef EQ_CHECK_THREADSAFETY 00227 # define EQ_TS_RESET( NAME ) NAME.id = co::base::ThreadID::ZERO; 00228 00229 # define EQ_TS_THREAD( NAME ) \ 00230 { \ 00231 if( NAME.id == co::base::ThreadID::ZERO ) \ 00232 { \ 00233 NAME.id = co::base::Thread::getSelfThreadID(); \ 00234 NAME.name = co::base::Log::instance().getThreadName(); \ 00235 EQVERB << "Functions for " << #NAME \ 00236 << " locked to this thread" << std::endl; \ 00237 } \ 00238 if( !NAME.extMutex && NAME.id != co::base::Thread::getSelfThreadID( )) \ 00239 { \ 00240 EQERROR << "Threadsafety check for " << #NAME \ 00241 << " failed on object of type " \ 00242 << co::base::className( this ) << ", thread " \ 00243 << co::base::Thread::getSelfThreadID() << " (" \ 00244 << co::base::Log::instance().getThreadName() << ") != " \ 00245 << NAME.id << " (" << NAME.name << ")" << std::endl; \ 00246 EQABORT( "Non-threadsave code called from two threads" ); \ 00247 } \ 00248 } 00249 00250 # define EQ_TS_NOT_THREAD( NAME ) \ 00251 { \ 00252 if( !NAME.extMutex && NAME.id != co::base::ThreadID::ZERO ) \ 00253 { \ 00254 if( NAME.id == co::base::Thread::getSelfThreadID( )) \ 00255 { \ 00256 EQERROR << "Threadsafety check for not " << #NAME \ 00257 << " failed on object of type " \ 00258 << co::base::className( this ) << std::endl; \ 00259 EQABORT( "Code called from wrong thread" ); \ 00260 } \ 00261 } \ 00262 } 00263 00265 template< typename T > class ScopedThreadCheck : public NonCopyable 00266 { 00267 public: 00268 explicit ScopedThreadCheck( const T& data ) 00269 : _data( data ) 00270 { 00271 EQASSERTINFO( !data.inRegion, 00272 "Another thread already in critical region" ); 00273 data.inRegion = true; 00274 } 00275 ~ScopedThreadCheck() 00276 { 00277 EQASSERTINFO( _data.inRegion, 00278 "Another thread was in critical region" ); 00279 _data.inRegion = false; 00280 } 00281 private: 00282 const T& _data; 00283 }; 00286 # define EQ_TS_SCOPED( NAME ) \ 00287 co::base::ScopedThreadCheck< NAME ## Struct > scoped ## NAME ## Check(NAME); 00288 00289 #else 00290 # define EQ_TS_RESET( NAME ) {} 00291 # define EQ_TS_THREAD( NAME ) {} 00292 # define EQ_TS_NOT_THREAD( NAME ) {} 00293 # define EQ_TS_SCOPED( NAME ) {} 00294 #endif 00295 00296 } 00297 } 00298 #endif //COBASE_THREAD_H