Equalizer
1.2.1
|
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 { 00034 class Thread 00035 { 00036 public: 00038 COBASE_API Thread(); 00039 00041 COBASE_API Thread( const Thread& from ); 00042 00044 COBASE_API virtual ~Thread(); 00045 00054 COBASE_API bool start(); 00055 00067 virtual bool init(){ return true; } 00068 00077 virtual void run() = 0; 00078 00087 COBASE_API virtual void exit(); 00088 00095 COBASE_API void cancel(); 00096 00103 COBASE_API bool join(); 00104 00114 bool isStopped() const { return ( _state == STATE_STOPPED ); } 00115 00125 bool isRunning() const { return ( _state == STATE_RUNNING ); } 00126 00132 COBASE_API bool isCurrent() const; 00133 00135 COBASE_API static ThreadID getSelfThreadID(); 00136 00138 COBASE_API static void yield(); 00139 00141 static void pinCurrentThread(); 00142 00144 COBASE_API static void setName( const std::string& name ); 00145 00146 private: 00147 ThreadID _id; 00148 00150 enum State 00151 { 00152 STATE_STOPPED, 00153 STATE_STARTING, // start() in progress 00154 STATE_RUNNING, 00155 STATE_STOPPING // child no longer active, join() not yet called 00156 }; 00157 00158 Monitor< State > _state; 00159 00160 static void* runChild( void* arg ); 00161 void _runChild(); 00162 00163 void _installCleanupHandler(); 00164 00165 static void _notifyStarted(); 00166 static void _notifyStopping(); 00167 friend void _notifyStopping( void* ); 00168 00169 friend std::ostream& operator << ( std::ostream& os, const Thread* ); 00170 }; 00171 00173 std::ostream& operator << ( std::ostream& os, const Thread* thread ); 00174 00175 // thread-safety checks 00176 // These checks are for development purposes, to check that certain objects are 00177 // properly used within the framework. Leaving them enabled during application 00178 // development may cause false positives, e.g., when threadsafety is ensured 00179 // outside of the objects by the application. 00180 00181 #ifndef NDEBUG 00182 # define EQ_CHECK_THREADSAFETY 00183 #endif 00184 00186 #define EQ_TS_VAR( NAME ) \ 00187 public: \ 00188 struct NAME ## Struct \ 00189 { \ 00190 NAME ## Struct () \ 00191 : extMutex( false ), inRegion( false ) \ 00192 {} \ 00193 mutable co::base::ThreadID id; \ 00194 mutable std::string name; \ 00195 bool extMutex; \ 00196 mutable bool inRegion; \ 00197 } NAME; \ 00198 private: 00199 00200 #ifdef EQ_CHECK_THREADSAFETY 00201 # define EQ_TS_RESET( NAME ) NAME.id = co::base::ThreadID::ZERO; 00202 00203 # define EQ_TS_THREAD( NAME ) \ 00204 { \ 00205 if( NAME.id == co::base::ThreadID::ZERO ) \ 00206 { \ 00207 NAME.id = co::base::Thread::getSelfThreadID(); \ 00208 NAME.name = co::base::Log::instance().getThreadName(); \ 00209 EQVERB << "Functions for " << #NAME \ 00210 << " locked to this thread" << std::endl; \ 00211 } \ 00212 if( !NAME.extMutex && NAME.id != co::base::Thread::getSelfThreadID( )) \ 00213 { \ 00214 EQERROR << "Threadsafety check for " << #NAME \ 00215 << " failed on object of type " \ 00216 << co::base::className( this ) << ", thread " \ 00217 << co::base::Thread::getSelfThreadID() << " (" \ 00218 << co::base::Log::instance().getThreadName() << ") != " \ 00219 << NAME.id << " (" << NAME.name << ")" << std::endl; \ 00220 EQABORT( "Non-threadsave code called from two threads" ); \ 00221 } \ 00222 } 00223 00224 # define EQ_TS_NOT_THREAD( NAME ) \ 00225 { \ 00226 if( !NAME.extMutex && NAME.id != co::base::ThreadID::ZERO ) \ 00227 { \ 00228 if( NAME.id == co::base::Thread::getSelfThreadID( )) \ 00229 { \ 00230 EQERROR << "Threadsafety check for not " << #NAME \ 00231 << " failed on object of type " \ 00232 << co::base::className( this ) << std::endl; \ 00233 EQABORT( "Code called from wrong thread" ); \ 00234 } \ 00235 } \ 00236 } 00237 00239 template< typename T > class ScopedThreadCheck : public NonCopyable 00240 { 00241 public: 00242 explicit ScopedThreadCheck( const T& data ) 00243 : _data( data ) 00244 { 00245 EQASSERTINFO( !data.inRegion, 00246 "Another thread already in critical region" ); 00247 data.inRegion = true; 00248 } 00249 ~ScopedThreadCheck() 00250 { 00251 EQASSERTINFO( _data.inRegion, 00252 "Another thread was in critical region" ); 00253 _data.inRegion = false; 00254 } 00255 private: 00256 const T& _data; 00257 }; 00260 # define EQ_TS_SCOPED( NAME ) \ 00261 co::base::ScopedThreadCheck< NAME ## Struct > scoped ## NAME ## Check(NAME); 00262 00263 #else 00264 # define EQ_TS_RESET( NAME ) {} 00265 # define EQ_TS_THREAD( NAME ) {} 00266 # define EQ_TS_NOT_THREAD( NAME ) {} 00267 # define EQ_TS_SCOPED( NAME ) {} 00268 #endif 00269 00270 } 00271 } 00272 #endif //COBASE_THREAD_H