|
NCBI C++ ToolKit
|
00001 #ifndef CORELIB___NCBITHR__HPP 00002 #define CORELIB___NCBITHR__HPP 00003 00004 /* $Id: ncbithr.hpp 53291 2012-03-06 20:39:08Z lavr $ 00005 * =========================================================================== 00006 * 00007 * PUBLIC DOMAIN NOTICE 00008 * National Center for Biotechnology Information 00009 * 00010 * This software/database is a "United States Government Work" under the 00011 * terms of the United States Copyright Act. It was written as part of 00012 * the author's official duties as a United States Government employee and 00013 * thus cannot be copyrighted. This software/database is freely available 00014 * to the public for use. The National Library of Medicine and the U.S. 00015 * Government have not placed any restriction on its use or reproduction. 00016 * 00017 * Although all reasonable efforts have been taken to ensure the accuracy 00018 * and reliability of the software and data, the NLM and the U.S. 00019 * Government do not and cannot warrant the performance or results that 00020 * may be obtained by using this software or data. The NLM and the U.S. 00021 * Government disclaim all warranties, express or implied, including 00022 * warranties of performance, merchantability or fitness for any particular 00023 * purpose. 00024 * 00025 * Please cite the author in any work or product based on this material. 00026 * 00027 * =========================================================================== 00028 * 00029 * Author: Denis Vakatov, Aleksey Grichenko 00030 * 00031 * 00032 */ 00033 00034 /// @file ncbithr.hpp 00035 /// Multi-threading -- classes, functions, and features. 00036 /// 00037 /// TLS: 00038 /// - CTlsBase -- TLS implementation (base class for CTls<>) 00039 /// - CTls<> -- thread local storage template 00040 /// 00041 /// THREAD: 00042 /// - CThread -- thread wrapper class 00043 /// 00044 00045 00046 #include <corelib/ncbimtx.hpp> 00047 #include <corelib/ncbi_process.hpp> 00048 #include <corelib/ncbi_safe_static.hpp> 00049 #include <list> 00050 00051 00052 BEGIN_NCBI_SCOPE 00053 00054 /** @addtogroup Threads 00055 * 00056 * @{ 00057 */ 00058 00059 00060 ///////////////////////////////////////////////////////////////////////////// 00061 /// 00062 /// CTlBase -- 00063 /// 00064 /// Base class for CTls<> for storing thread-specific data. 00065 00066 class NCBI_XNCBI_EXPORT CTlsBase : public CObject 00067 { 00068 friend class CRef<CTlsBase>; 00069 friend class CUsedTlsBases; 00070 friend class CStaticTlsHelper; 00071 00072 public: 00073 typedef void (*FCleanupBase)(void* value, void* cleanup_data); 00074 00075 protected: 00076 /// Constructor. 00077 CTlsBase(bool auto_destroy) 00078 : m_AutoDestroy(auto_destroy) 00079 {} 00080 00081 /// Destructor. 00082 /// 00083 /// Cleanup data and delete TLS key. 00084 ~CTlsBase(void) 00085 { 00086 if (m_AutoDestroy) { 00087 x_Destroy(); 00088 } 00089 } 00090 00091 /// Helper method to get stored thread data. 00092 void* x_GetValue(void) const; 00093 00094 /// Helper method to set thread data. 00095 void x_SetValue(void* value, FCleanupBase cleanup=0, void* cleanup_data=0); 00096 00097 /// Helper method to reset thread data. 00098 void x_Reset(void); 00099 00100 protected: 00101 /// Initialize thread data 00102 void x_Init(void); 00103 00104 /// Destroy thread data 00105 void x_Destroy(void); 00106 00107 private: 00108 TTlsKey m_Key; ///< 00109 bool m_Initialized; ///< Indicates if thread data initialized. 00110 bool m_AutoDestroy; ///< Indicates if object should be destroyed 00111 ///< in destructor 00112 00113 /// Internal structure to store all three pointers in the same TLS. 00114 struct STlsData { 00115 void* m_Value; 00116 FCleanupBase m_CleanupFunc; 00117 void* m_CleanupData; 00118 }; 00119 00120 /// Helper method to get the STlsData* 00121 STlsData* x_GetTlsData(void) const; 00122 /// Deletes STlsData* structure and managed pointer 00123 /// Returns true if CTlsBase must be deregistered from current thread 00124 bool x_DeleteTlsData(void); 00125 00126 public: 00127 static void CleanupTlsData(void *data); 00128 }; 00129 00130 00131 00132 ///////////////////////////////////////////////////////////////////////////// 00133 /// 00134 /// CTls -- 00135 /// 00136 /// Define template class for thread local storage. 00137 00138 template <class TValue> 00139 class CTls : public CTlsBase 00140 { 00141 public: 00142 CTls(void) : CTlsBase(true) 00143 { 00144 DoDeleteThisObject(); 00145 x_Init(); 00146 } 00147 00148 /// Get the pointer previously stored by SetValue(). 00149 /// 00150 /// Return 0 if no value has been stored, or if Reset() was last called. 00151 /// @sa 00152 /// SetValue() 00153 TValue* GetValue(void) const 00154 { 00155 return reinterpret_cast<TValue*> (x_GetValue()); 00156 } 00157 00158 /// Define cleanup function type, FCleanup. 00159 typedef void (*FCleanup)(TValue* value, void* cleanup_data); 00160 00161 /// Set value. 00162 /// 00163 /// Cleanup previously stored value, and set the new value. 00164 /// The "cleanup" function and "cleanup_data" will be used to 00165 /// destroy the new "value" in the next call to SetValue() or Reset(). 00166 /// Do not cleanup if the new value is equal to the old one. 00167 /// @param value 00168 /// New value to set. 00169 /// @param cleanup 00170 /// Cleanup function. 00171 /// Do not cleanup if default of 0 is specified or if new value is the 00172 /// same as old value. 00173 /// @param cleanup_data 00174 /// One of the parameters to the cleanup function. 00175 /// @sa 00176 /// GetValue() 00177 void SetValue(TValue* value, FCleanup cleanup = 0, void* cleanup_data = 0) 00178 { 00179 x_SetValue(value, 00180 reinterpret_cast<FCleanupBase> (cleanup), cleanup_data); 00181 } 00182 00183 /// Reset thread local storage. 00184 /// 00185 /// Reset thread local storage to its initial value (as it was before the 00186 /// first call to SetValue()). Do cleanup if the cleanup function was 00187 /// specified in the previous call to SetValue(). 00188 /// 00189 /// Reset() will always be called automatically on the thread termination, 00190 /// or when the TLS is destroyed. 00191 void Reset(void) { x_Reset(); } 00192 00193 /// Discard thread local storage. 00194 /// 00195 /// Schedule the TLS to be destroyed as soon as there are no CRef to it 00196 /// left. 00197 void Discard(void) { x_Reset(); } 00198 }; 00199 00200 00201 #define NCBI_STATIC_TLS_VIA_SAFE_STATIC_REF 1 00202 00203 #if NCBI_STATIC_TLS_VIA_SAFE_STATIC_REF 00204 template<class TValue> 00205 class CStaticTls : private CSafeStaticRef< CTls<TValue> > 00206 { 00207 private: 00208 typedef CSafeStaticRef< CTls<TValue> > TParent; 00209 00210 public: 00211 typedef CSafeStaticLifeSpan TLifeSpan; 00212 /// User cleanup function type 00213 typedef void (*FUserCleanup)(void* ptr); 00214 /// Define cleanup function type, FCleanup. 00215 typedef void (*FCleanup)(TValue* value, void* cleanup_data); 00216 00217 CStaticTls(FUserCleanup user_cleanup = 0, 00218 TLifeSpan life_span = TLifeSpan::GetDefault()) 00219 : TParent(user_cleanup, life_span) 00220 { 00221 } 00222 00223 TValue* GetValue(void) { 00224 return TParent::Get().GetValue(); 00225 } 00226 void SetValue(TValue* value, FCleanup cleanup = 0, void* cleanup_data = 0){ 00227 TParent::Get().SetValue(value, cleanup, cleanup_data); 00228 } 00229 00230 friend class CUsedTlsBases; 00231 }; 00232 00233 #else // !NCBI_STATIC_TLS_VIA_SAFE_STATIC_REF 00234 template <class TValue> class CStaticTls; 00235 00236 /// Helper class to control life time of CStaticTls object 00237 class CStaticTlsHelper : public CSafeStaticPtr_Base 00238 { 00239 private: 00240 template <class TValue> friend class CStaticTls; 00241 00242 CStaticTlsHelper(FUserCleanup user_cleanup, 00243 TLifeSpan life_span) 00244 : CSafeStaticPtr_Base(SelfCleanup, user_cleanup, life_span) 00245 {} 00246 00247 static void SelfCleanup(void** ptr) 00248 { 00249 CTlsBase* tmp = static_cast<CTlsBase*>(*ptr); 00250 if (tmp) { 00251 tmp->x_Destroy(); 00252 *ptr = NULL; 00253 } 00254 } 00255 }; 00256 00257 00258 ///////////////////////////////////////////////////////////////////////////// 00259 /// 00260 /// CStaticTls -- 00261 /// 00262 /// Define template class for thread local storage in static variable 00263 /// (as thread local storage objects are meaningful only in static content). 00264 /// Class can be used only as static variable type. 00265 00266 template <class TValue> 00267 class CStaticTls : public CTlsBase 00268 { 00269 public: 00270 /// Life span 00271 typedef CSafeStaticLifeSpan TLifeSpan; 00272 /// User cleanup function type 00273 typedef void (*FUserCleanup)(void* ptr); 00274 00275 // Set user-provided cleanup function to be executed on destruction. 00276 // Life span allows to control destruction of objects. Objects with 00277 // the same life span are destroyed in the order reverse to their 00278 // creation order. 00279 CStaticTls(FUserCleanup user_cleanup = 0, 00280 TLifeSpan life_span = TLifeSpan::GetDefault()) 00281 : CTlsBase(false), 00282 m_SafeHelper(user_cleanup, life_span) 00283 {} 00284 00285 /// Get the pointer previously stored by SetValue(). 00286 /// 00287 /// Return 0 if no value has been stored, or if Reset() was last called. 00288 /// @sa 00289 /// SetValue() 00290 TValue* GetValue(void) 00291 { 00292 if (!m_SafeHelper.m_Ptr) { 00293 x_SafeInit(); 00294 } 00295 return reinterpret_cast<TValue*> (x_GetValue()); 00296 } 00297 00298 /// Define cleanup function type, FCleanup. 00299 typedef void (*FCleanup)(TValue* value, void* cleanup_data); 00300 00301 /// Set value. 00302 /// 00303 /// Cleanup previously stored value, and set the new value. 00304 /// The "cleanup" function and "cleanup_data" will be used to 00305 /// destroy the new "value" in the next call to SetValue() or Reset(). 00306 /// Do not cleanup if the new value is equal to the old one. 00307 /// @param value 00308 /// New value to set. 00309 /// @param cleanup 00310 /// Cleanup function. 00311 /// Do not cleanup if default of 0 is specified or if new value is the 00312 /// same as old value. 00313 /// @param cleanup_data 00314 /// One of the parameters to the cleanup function. 00315 /// @sa 00316 /// GetValue() 00317 void SetValue(TValue* value, FCleanup cleanup = 0, void* cleanup_data = 0) 00318 { 00319 if (!m_SafeHelper.m_Ptr) { 00320 x_SafeInit(); 00321 } 00322 x_SetValue(value, 00323 reinterpret_cast<FCleanupBase> (cleanup), cleanup_data); 00324 } 00325 00326 /// Reset thread local storage. 00327 /// 00328 /// Reset thread local storage to its initial value (as it was before the 00329 /// first call to SetValue()). Do cleanup if the cleanup function was 00330 /// specified in the previous call to SetValue(). 00331 /// 00332 /// Reset() will always be called automatically on the thread termination, 00333 /// or when the TLS is destroyed. 00334 void Reset(void) 00335 { 00336 if (!m_SafeHelper.m_Ptr) { 00337 x_SafeInit(); 00338 } 00339 x_Reset(); 00340 } 00341 00342 private: 00343 /// Object derived from CSafeStaticPtr_Base to help manage life time 00344 /// of the object 00345 CStaticTlsHelper m_SafeHelper; 00346 00347 /// Initialize the object in SafeStaticRef-ish manner 00348 void x_SafeInit(void); 00349 }; 00350 #endif // NCBI_STATIC_TLS_VIA_SAFE_STATIC_REF 00351 00352 class NCBI_XNCBI_EXPORT CUsedTlsBases 00353 { 00354 public: 00355 CUsedTlsBases(void); 00356 ~CUsedTlsBases(void); 00357 00358 /// The function must be called before thread termination when 00359 /// using native threads instead of CThread. Otherwise any data 00360 /// allocated by the thread and put into TLS will not be destroyed 00361 /// and will cause memory leaks. 00362 void ClearAll(void); 00363 00364 void Register(CTlsBase* tls); 00365 void Deregister(CTlsBase* tls); 00366 00367 /// Get the list of used TLS-es for the current thread 00368 static CUsedTlsBases& GetUsedTlsBases(void); 00369 00370 private: 00371 typedef set<CTlsBase*> TTlsSet; 00372 TTlsSet m_UsedTls; 00373 00374 static CStaticTls<CUsedTlsBases> sm_UsedTlsBases; 00375 00376 private: 00377 CUsedTlsBases(const CUsedTlsBases&); 00378 void operator=(const CUsedTlsBases&); 00379 }; 00380 00381 00382 ///////////////////////////////////////////////////////////////////////////// 00383 /// 00384 /// CThread -- 00385 /// 00386 /// Thread wrapper class. 00387 /// 00388 /// Base class for user-defined threads. Creates the new thread, then 00389 /// calls user-provided Main() function. The thread then can be detached 00390 /// or joined. In any case, explicit destruction of the thread is prohibited. 00391 00392 class NCBI_XNCBI_EXPORT CThread : public CObject 00393 { 00394 friend class CRef<CThread>; 00395 friend class CTlsBase; 00396 00397 public: 00398 /// Constructor. 00399 /// 00400 /// Must be allocated in the heap only!. 00401 CThread(void); 00402 00403 /// Which mode should the thread run in. 00404 enum ERunMode { 00405 fRunDefault = 0x00, ///< Default mode 00406 fRunDetached = 0x01, ///< Run the thread detached (non-joinable) 00407 fRunBound = 0x10, ///< Run thread in a 1:1 thread:LPW mode 00408 ///< - may not be supported and will be 00409 ///< ignored on some platforms 00410 fRunUnbound = 0x20, ///< Run thread in a N:1 thread:LPW mode 00411 ///< - may not be supported and will be 00412 ///< ignored on some platforms 00413 fRunNice = 0x40, ///< Run thread with low priority (MS-Win only) 00414 fRunAllowST = 0x100 ///< Allow threads to run in single thread 00415 ///< builds 00416 }; 00417 00418 /// Bitwise OR'd flags for thread creation passed to Run(). 00419 typedef int TRunMode; 00420 00421 /// Run the thread. 00422 /// 00423 /// Create a new thread, initialize it, and call user-provided Main() 00424 /// method. 00425 bool Run(TRunMode flags = fRunDefault); 00426 00427 /// Inform the thread that user does not need to wait for its termination. 00428 /// The thread object will be destroyed by Exit(). 00429 /// If the thread has already been terminated by Exit, Detach() will 00430 /// also schedule the thread object for destruction. 00431 /// NOTE: it is no more safe to use this thread object after Detach(), 00432 /// unless there are still CRef<> based references to it! 00433 void Detach(void); 00434 00435 /// Wait for the thread termination. 00436 /// The thread object will be scheduled for destruction right here, 00437 /// inside Join(). Only one call to Join() is allowed. 00438 void Join(void** exit_data = 0); 00439 00440 /// Cancel current thread. If the thread is detached, then schedule 00441 /// the thread object for destruction. 00442 /// Cancellation is performed by throwing an exception of type 00443 /// CExitThreadException to allow destruction of all objects in 00444 /// thread's stack, so Exit() method shell not be called from any 00445 /// destructor. 00446 static void Exit(void* exit_data); 00447 00448 /// If the thread has not been Run() yet, then schedule the thread object 00449 /// for destruction, and return TRUE. 00450 /// Otherwise, do nothing, and return FALSE. 00451 bool Discard(void); 00452 00453 /// Get ID of current thread (for main thread it is always zero). 00454 typedef unsigned int TID; 00455 static TID GetSelf(void); 00456 00457 /// Get current CThread object (or NULL, if main thread) 00458 static CThread* GetCurrentThread(void); 00459 00460 /// Get system ID of the current thread - for internal use only. 00461 /// The ID is unique only while the thread is running and may be 00462 /// re-used by another thread later. 00463 static void GetSystemID(TThreadSystemID* id); 00464 00465 /// Get total amount of threads 00466 /// This amount does not contain main thread. 00467 static unsigned int GetThreadsCount(); 00468 00469 /// Initialize main thread's TID. 00470 /// The function must be called from the main thread if the application 00471 /// is using non-toolkit threads. Otherwise getting thread id of a 00472 /// native thread will return zero. 00473 static void InitializeMainThreadId(void); 00474 00475 protected: 00476 /// Derived (user-created) class must provide a real thread function. 00477 virtual void* Main(void) = 0; 00478 00479 /// Override this to execute finalization code. 00480 /// Unlike destructor, this code will be executed before 00481 /// thread termination and as a part of the thread. 00482 virtual void OnExit(void); 00483 00484 /// To be called only internally! 00485 /// NOTE: destructor of the derived (user-provided) class should be 00486 /// declared "protected", too! 00487 virtual ~CThread(void); 00488 00489 TThreadHandle GetThreadHandle(); 00490 00491 private: 00492 TThreadHandle m_Handle; ///< platform-dependent thread handle 00493 bool m_IsRun; ///< if Run() was called for the thread 00494 bool m_IsDetached; ///< if the thread is detached 00495 bool m_IsJoined; ///< if Join() was called for the thread 00496 bool m_IsTerminated; ///< if Exit() was called for the thread 00497 CRef<CThread> m_SelfRef; ///< "this" -- to avoid premature destruction 00498 void* m_ExitData; ///< as returned by Main() or passed to Exit() 00499 00500 #if defined NCBI_THREAD_PID_WORKAROUND 00501 friend class CProcess; 00502 TPid m_ThreadPID; ///< Cache thread PID to detect forks 00503 00504 static TPid sx_GetThreadPid(void); 00505 static void sx_SetThreadPid(TPid pid); 00506 #endif 00507 00508 static unsigned int sm_ThreadsCount; ///< Total amount of threads 00509 00510 /// Function to use (internally) as the thread's startup function 00511 static TWrapperRes Wrapper(TWrapperArg arg); 00512 friend TWrapperRes ThreadWrapperCaller(TWrapperArg arg); 00513 00514 struct SThreadInfo { 00515 CThread* thread_ptr; 00516 TID thread_id; 00517 }; 00518 00519 /// To store "CThread" object related to the current (running) thread 00520 static CStaticTls<SThreadInfo>* sm_ThreadsTls; 00521 static bool sm_MainThreadIdInitialized; 00522 00523 /// Safe access to "sm_ThreadsTls" 00524 static CStaticTls<SThreadInfo>& GetThreadsTls(void) 00525 { 00526 if ( !sm_ThreadsTls ) { 00527 CreateThreadsTls(); 00528 } 00529 return *sm_ThreadsTls; 00530 } 00531 00532 static void sx_CleanupThreadInfo(SThreadInfo* info, void* cleanup_data); 00533 static SThreadInfo* sx_InitThreadInfo(CThread* thread_obj); 00534 static int sx_GetNextThreadId(void); 00535 00536 /// sm_ThreadsTls initialization and cleanup functions 00537 static void CreateThreadsTls(void); 00538 friend void s_CleanupThreadsTls(void* /* ptr */); 00539 00540 /// Prohibit copying and assigning 00541 CThread(const CThread&); 00542 CThread& operator= (const CThread&); 00543 }; 00544 00545 00546 class NCBI_XNCBI_EXPORT CThreadException : EXCEPTION_VIRTUAL_BASE public CException 00547 { 00548 public: 00549 enum EErrCode { 00550 eRunError, ///< Failed to run thread 00551 eControlError, ///< Failed to control thread's state 00552 eOther ///< Other thread errors 00553 }; 00554 00555 /// Translate from the error code value to its string representation. 00556 virtual const char* GetErrCodeString(void) const; 00557 00558 // Standard exception boilerplate code. 00559 NCBI_EXCEPTION_DEFAULT(CThreadException, CException); 00560 }; 00561 00562 00563 /* @} */ 00564 00565 00566 ///////////////////////////////////////////////////////////////////////////// 00567 00568 ///////////////////////////////////////////////////////////////////////////// 00569 // IMPLEMENTATION of INLINE functions 00570 ///////////////////////////////////////////////////////////////////////////// 00571 00572 00573 00574 ///////////////////////////////////////////////////////////////////////////// 00575 // CTlsBase:: 00576 // 00577 00578 inline 00579 CTlsBase::STlsData* CTlsBase::x_GetTlsData(void) 00580 const 00581 { 00582 if ( !m_Initialized ) { 00583 return 0; 00584 } 00585 00586 void* tls_data; 00587 00588 #if defined(NCBI_WIN32_THREADS) 00589 tls_data = TlsGetValue(m_Key); 00590 #elif defined(NCBI_POSIX_THREADS) 00591 tls_data = pthread_getspecific(m_Key); 00592 #else 00593 tls_data = m_Key; 00594 #endif 00595 00596 return static_cast<STlsData*> (tls_data); 00597 } 00598 00599 00600 inline 00601 void* CTlsBase::x_GetValue(void) 00602 const 00603 { 00604 // Get TLS-stored structure 00605 STlsData* tls_data = x_GetTlsData(); 00606 00607 // If assigned, extract and return user data 00608 return tls_data ? tls_data->m_Value : 0; 00609 } 00610 00611 00612 00613 ///////////////////////////////////////////////////////////////////////////// 00614 // CThread:: 00615 // 00616 00617 #if !NCBI_STATIC_TLS_VIA_SAFE_STATIC_REF 00618 template <class TValue> 00619 inline 00620 void CStaticTls<TValue>::x_SafeInit(void) 00621 { 00622 bool mutex_locked = false; 00623 if ( m_SafeHelper.Init_Lock(&mutex_locked) ) { 00624 // Init the object and register for cleanup 00625 try { 00626 x_Init(); 00627 m_SafeHelper.m_Ptr = this; 00628 CSafeStaticGuard::Register(&m_SafeHelper); 00629 } 00630 catch (CException& e) { 00631 m_SafeHelper.Init_Unlock(mutex_locked); 00632 NCBI_RETHROW_SAME(e, 00633 "CStaticTls::x_CheckInit: Register() failed"); 00634 } 00635 catch (...) { 00636 m_SafeHelper.Init_Unlock(mutex_locked); 00637 NCBI_THROW(CCoreException, eCore, 00638 "CStaticTls::x_CheckInit: Register() failed"); 00639 } 00640 } 00641 m_SafeHelper.Init_Unlock(mutex_locked); 00642 } 00643 #endif 00644 00645 00646 ///////////////////////////////////////////////////////////////////////////// 00647 // CThread:: 00648 // 00649 00650 inline 00651 CThread::TID CThread::GetSelf(void) 00652 { 00653 SThreadInfo* info = GetThreadsTls().GetValue(); 00654 if (!info && sm_MainThreadIdInitialized) { 00655 // Info has not been set - this is a native thread, 00656 // need to assign an ID. 00657 info = sx_InitThreadInfo(0); 00658 } 00659 // If zero, it is main thread which has no CThread object 00660 return info ? info->thread_id : 0; 00661 } 00662 00663 00664 inline 00665 CThread* CThread::GetCurrentThread(void) 00666 { 00667 // Get pointer to the current thread object 00668 SThreadInfo* info = GetThreadsTls().GetValue(); 00669 return info ? info->thread_ptr : 0; 00670 } 00671 00672 00673 inline 00674 TThreadHandle CThread::GetThreadHandle() 00675 { 00676 return m_Handle; 00677 } 00678 00679 00680 inline 00681 unsigned int CThread::GetThreadsCount() { 00682 return sm_ThreadsCount; 00683 } 00684 00685 00686 // Special value, stands for "no thread" thread ID 00687 const CThread::TID kThreadID_None = 0xFFFFFFFF; 00688 00689 00690 END_NCBI_SCOPE 00691 00692 #endif /* NCBITHR__HPP */
1.7.5.1
Modified on Wed May 23 13:17:54 2012 by modify_doxy.py rev. 337098