1 #ifndef CORELIB___NCBITHR__HPP
2 #define CORELIB___NCBITHR__HPP
3 
4 /*  $Id: ncbithr.hpp 606787 2020-04-27 16:15:35Z lavr $
5  * ===========================================================================
6  *
7  *                            PUBLIC DOMAIN NOTICE
8  *               National Center for Biotechnology Information
9  *
10  *  This software/database is a "United States Government Work" under the
11  *  terms of the United States Copyright Act.  It was written as part of
12  *  the author's official duties as a United States Government employee and
13  *  thus cannot be copyrighted.  This software/database is freely available
14  *  to the public for use. The National Library of Medicine and the U.S.
15  *  Government have not placed any restriction on its use or reproduction.
16  *
17  *  Although all reasonable efforts have been taken to ensure the accuracy
18  *  and reliability of the software and data, the NLM and the U.S.
19  *  Government do not and cannot warrant the performance or results that
20  *  may be obtained by using this software or data. The NLM and the U.S.
21  *  Government disclaim all warranties, express or implied, including
22  *  warranties of performance, merchantability or fitness for any particular
23  *  purpose.
24  *
25  *  Please cite the author in any work or product based on this material.
26  *
27  * ===========================================================================
28  *
29  * Author:  Denis Vakatov, Aleksey Grichenko
30  *
31  *
32  */
33 
34 /// @file ncbithr.hpp
35 /// Multi-threading -- classes, functions, and features.
36 ///
37 ///   TLS:
38 ///   -   CTlsBase         -- TLS implementation (base class for CTls<>)
39 ///   -   CTls<>           -- thread local storage template
40 ///
41 ///   THREAD:
42 ///   -   CThread          -- thread wrapper class
43 ///
44 
45 
46 #include <corelib/ncbi_process.hpp>
47 #include <corelib/ncbi_safe_static.hpp>
48 #include <list>
49 
50 
51 BEGIN_NCBI_SCOPE
52 
53 /** @addtogroup Threads
54  *
55  * @{
56  */
57 
58 
59 /////////////////////////////////////////////////////////////////////////////
60 ///
61 /// CTlBase --
62 ///
63 /// Base class for CTls<> for storing thread-specific data.
64 
65 class NCBI_XNCBI_EXPORT CTlsBase : public CObject
66 {
67     friend class CRef<CTlsBase>;
68     friend class CUsedTlsBases;
69     friend class CStaticTlsHelper;
70 
71 public:
72     typedef void (*FCleanupBase)(void* value, void* cleanup_data);
73 
74     /// Flag indicating if cleanup function should be called when using native threads
75     /// rather than CThread. Native threads may perform cleanup later than CThread,
76     /// so that some resources (like static variables) may be already destroyed.
77     /// To prevent failures the default mode is eSkipCleanup.
78     enum ENativeThreadCleanup {
79         eDoCleanup,
80         eSkipCleanup
81     };
82 
83     /// Flag telling which code has called TLS cleanup.
84     enum ECleanupMode {
85         eCleanup_Toolkit, ///< Cleanup is performed by CThread.
86         eCleanup_Native   ///< Cleanup is performed by thread_local destructor.
87     };
88 
89 protected:
90     /// Constructor.
CTlsBase(bool auto_destroy)91     CTlsBase(bool auto_destroy)
92         : m_AutoDestroy(auto_destroy)
93     {}
94 
95     /// Destructor.
96     ///
97     /// Cleanup data and delete TLS key.
~CTlsBase(void)98     ~CTlsBase(void)
99     {
100         if (m_AutoDestroy) {
101             x_Destroy();
102         }
103     }
104 
105     /// Helper method to get stored thread data.
106     void* x_GetValue(void) const;
107 
108     /// Helper method to set thread data.
109     void x_SetValue(void* value, FCleanupBase cleanup, void* cleanup_data, ENativeThreadCleanup native);
110 
111     /// Helper method to reset thread data.
112     void x_Reset(void);
113 
114 protected:
115     /// Initialize thread data
116     void x_Init(void);
117 
118     /// Destroy thread data
119     void x_Destroy(void);
120 
121 private:
122     TTlsKey m_Key;              ///<
123     bool    m_Initialized;      ///< Indicates if thread data initialized.
124     bool    m_AutoDestroy;      ///< Indicates if object should be destroyed
125                                 ///< in destructor
126 
127     /// Internal structure to store all three pointers in the same TLS.
128     struct STlsData {
129         void*        m_Value;
130         FCleanupBase m_CleanupFunc;
131         void*        m_CleanupData;
132         ENativeThreadCleanup m_Native;
133     };
134 
135     /// Helper method to get the STlsData*
136     STlsData* x_GetTlsData(void) const;
137     /// Deletes STlsData* structure and managed pointer
138     /// Returns true if CTlsBase must be deregistered from current thread
139     bool x_DeleteTlsData(ECleanupMode mode = eCleanup_Toolkit);
140 
141 public:
142     static void CleanupTlsData(void *data, ECleanupMode mode = eCleanup_Toolkit);
143 
144     template<class TValue>
DefaultCleanup(TValue * value,void *)145     static void DefaultCleanup(TValue *value, void*) {
146         if (value) {
147             delete value;
148         }
149 
150     }
151 };
152 
153 
154 
155 /////////////////////////////////////////////////////////////////////////////
156 ///
157 /// CTls --
158 ///
159 /// Define template class for thread local storage.
160 
161 template <class TValue>
162 class CTls : public CTlsBase
163 {
164 public:
CTls(void)165     CTls(void) : CTlsBase(true)
166     {
167         DoDeleteThisObject();
168         x_Init();
169     }
170 
171     /// Get the pointer previously stored by SetValue().
172     ///
173     /// Return 0 if no value has been stored, or if Reset() was last called.
174     /// @sa
175     ///   SetValue()
GetValue(void) const176     TValue* GetValue(void) const
177     {
178         return reinterpret_cast<TValue*> (x_GetValue());
179     }
180 
181     /// Define cleanup function type, FCleanup.
182     typedef void (*FCleanup)(TValue* value, void* cleanup_data);
183 
184     /// Set value.
185     ///
186     /// Cleanup previously stored value, and set the new value.
187     /// The "cleanup" function and "cleanup_data" will be used to
188     /// destroy the new "value" in the next call to SetValue() or Reset().
189     /// Do not cleanup if the new value is equal to the old one.
190     /// @param value
191     ///   New value to set.
192     /// @param cleanup
193     ///   Cleanup function.
194     ///   Do not cleanup if default of 0 is specified or if new value is the
195     ///   same as old value.
196     /// @param cleanup_data
197     ///   One of the parameters to the cleanup function.
198     /// @sa
199     ///   GetValue()
SetValue(TValue * value,FCleanup cleanup=0,void * cleanup_data=0,ENativeThreadCleanup native=eSkipCleanup)200     void SetValue(TValue* value, FCleanup cleanup = 0, void* cleanup_data = 0, ENativeThreadCleanup native = eSkipCleanup)
201     {
202         x_SetValue(value,
203                    reinterpret_cast<FCleanupBase> (cleanup), cleanup_data, native);
204     }
205 
206     /// Reset thread local storage.
207     ///
208     /// Reset thread local storage to its initial value (as it was before the
209     /// first call to SetValue()). Do cleanup if the cleanup function was
210     /// specified in the previous call to SetValue().
211     ///
212     /// Reset() will always be called automatically on the thread termination,
213     /// or when the TLS is destroyed.
Reset(void)214     void Reset(void) { x_Reset(); }
215 
216     /// Discard thread local storage.
217     ///
218     /// Schedule the TLS to be destroyed as soon as there are no CRef to it
219     /// left.
Discard(void)220     void Discard(void) { x_Reset(); }
221 };
222 
223 
224 // TLS static variable support in case there is no compiler support
225 #ifdef NCBI_TLS_VAR
226 # define DECLARE_TLS_VAR(type, var) NCBI_TLS_VAR type var
227 #else
228 /////////////////////////////////////////////////////////////////////////////
229 ///
230 /// CSimpleStaticTls
231 ///
232 /// Define template class for simple data (POD) in thread local storage.
233 /// The data type must fit in the same memory as pointer type.
234 /// Use compiler support if possible, and direct pthread calls otherwise.
235 /// The variable of this type is MT-safe to be declared statically.
236 /// Initial value of the variable is zero or equivalent.
237 template<class V> class CSimpleStaticTls {
238 private:
239     typedef pthread_key_t key_type;
240     mutable key_type m_Key;
241     template<class A> struct SCaster {
FromVoidPCSimpleStaticTls::SCaster242         static A FromVoidP(void* p) {
243             return A(reinterpret_cast<intptr_t>(p));
244         }
ToVoidPCSimpleStaticTls::SCaster245         static const void* ToVoidP(A v) {
246             return reinterpret_cast<const void*>(intptr_t(v));
247         }
248     };
249     template<class A> struct SCaster<A*> {
FromVoidPCSimpleStaticTls::SCaster250         static A* FromVoidP(void* p) {
251             return reinterpret_cast<A*>(p);
252         }
ToVoidPCSimpleStaticTls::SCaster253         static const void* ToVoidP(A* v) {
254             return reinterpret_cast<const void*>(v);
255         }
256     };
x_GetKey(void) const257     key_type x_GetKey(void) const {
258         return m_Key? m_Key: x_GetKeyLong();
259     }
x_GetKeyLong(void) const260     key_type x_GetKeyLong(void) const {
261         DEFINE_STATIC_FAST_MUTEX(s_InitMutex);
262         NCBI_NS_NCBI::CFastMutexGuard guard(s_InitMutex);
263         if ( !m_Key ) {
264             _ASSERT(sizeof(value_type) <= sizeof(void*));
265             key_type new_key = 0;
266             do {
267                 _VERIFY(pthread_key_create(&new_key, 0) == 0);
268             } while ( !new_key );
269             pthread_setspecific(new_key, 0);
270             m_Key = new_key;
271         }
272         return m_Key;
273     }
274 public:
275     typedef V value_type;
276     /// Getter - returns value stored in TLS.
operator value_type() const277     operator value_type() const {
278         return SCaster<value_type>::FromVoidP(pthread_getspecific(x_GetKey()));
279     }
280     /// Setter - changes value stored in TLS.
operator =(const value_type & v)281     void operator=(const value_type& v) {
282         pthread_setspecific(x_GetKey(), SCaster<value_type>::ToVoidP(v));
283     }
284 };
285 # define DECLARE_TLS_VAR(type, var) CSimpleStaticTls<type> var
286 #endif
287 
288 
289 #define NCBI_STATIC_TLS_VIA_SAFE_STATIC_REF 1
290 
291 #if NCBI_STATIC_TLS_VIA_SAFE_STATIC_REF
292 
293 template <class TValue>
294 class CStaticTls_Callbacks
295 {
296 public:
297     typedef void (*FUserCleanup)(void*  ptr);
CStaticTls_Callbacks(FUserCleanup cleanup)298     CStaticTls_Callbacks(FUserCleanup cleanup) : m_Cleanup(cleanup) {}
299 
Create()300     CTls<TValue>* Create() {
301         return new CTls<TValue>;
302     }
Cleanup(CTls<TValue> & value)303     void Cleanup(CTls<TValue>& value) {
304         if ( m_Cleanup ) {
305             m_Cleanup(&value);
306         }
307     }
308 
309 private:
310     FUserCleanup m_Cleanup;
311 };
312 
313 template<class TValue>
314 class CStaticTls : private CSafeStatic<CTls<TValue>, CStaticTls_Callbacks<TValue> >
315 {
316 private:
317     typedef CSafeStatic<CTls<TValue>, CStaticTls_Callbacks<TValue> > TParent;
318 
319 public:
320     typedef CSafeStaticLifeSpan TLifeSpan;
321     /// User cleanup function type
322     typedef void (*FUserCleanup)(void*  ptr);
323     /// Define cleanup function type, FCleanup.
324     typedef void (*FCleanup)(TValue* value, void* cleanup_data);
325 
CStaticTls(FUserCleanup user_cleanup=0,TLifeSpan life_span=TLifeSpan::GetDefault ())326     CStaticTls(FUserCleanup user_cleanup = 0,
327                TLifeSpan life_span = TLifeSpan::GetDefault())
328         : TParent(CStaticTls_Callbacks<TValue>(user_cleanup), life_span)
329     {
330     }
331 
GetValue(void)332     TValue* GetValue(void) {
333         return TParent::Get().GetValue();
334     }
SetValue(TValue * value,FCleanup cleanup=0,void * cleanup_data=0,CTlsBase::ENativeThreadCleanup native=CTlsBase::eSkipCleanup)335     void SetValue(TValue* value, FCleanup cleanup = 0, void* cleanup_data = 0,
336         CTlsBase::ENativeThreadCleanup native = CTlsBase::eSkipCleanup){
337         TParent::Get().SetValue(value, cleanup, cleanup_data, native);
338     }
339 
340     friend class CUsedTlsBases;
341 };
342 
343 #else // !NCBI_STATIC_TLS_VIA_SAFE_STATIC_REF
344 template <class TValue> class CStaticTls;
345 
346 /// Helper class to control life time of CStaticTls object
347 class CStaticTlsHelper : public CSafeStaticPtr_Base
348 {
349 private:
350     template <class TValue> friend class CStaticTls;
351 
CStaticTlsHelper(FUserCleanup user_cleanup,TLifeSpan life_span)352     CStaticTlsHelper(FUserCleanup user_cleanup,
353                      TLifeSpan    life_span)
354         : CSafeStaticPtr_Base(sx_SelfCleanup, user_cleanup, life_span)
355     {}
356 
Set(CTlsBase * object)357     void Set(CTlsBase* object)
358     {
359         CMutexGuard guard(CSafeStaticPtr_Base::sm_Mutex);
360         if ( m_Ptr == 0 ) {
361             // Set the new object and register for cleanup
362             if ( object ) {
363                 m_Ptr = object;
364                 CSafeStaticGuard::Register(this);
365             }
366         }
367     }
368 
sx_SelfCleanup(CSafeStaticPtr_Base * safe_static,CMutexGuard & guard)369     static void sx_SelfCleanup(CSafeStaticPtr_Base* safe_static,
370                                CMutexGuard& guard)
371     {
372         CStaticTlsHelper* this_ptr = static_cast<CStaticTlsHelper*>(safe_static);
373         if ( CTlsBase* ptr = static_cast<CTlsBase*>(this_ptr->m_Ptr) ) {
374             FUserCleanup user_cleanup = this_ptr->m_UserCleanup;
375             this_ptr->m_Ptr = 0;
376             guard.Release();
377             if ( user_cleanup ) {
378                 user_cleanup(ptr);
379             }
380             ptr->x_Destroy();
381         }
382     }
383 };
384 
385 
386 /////////////////////////////////////////////////////////////////////////////
387 ///
388 /// CStaticTls --
389 ///
390 /// Define template class for thread local storage in static variable
391 /// (as thread local storage objects are meaningful only in static content).
392 /// Class can be used only as static variable type.
393 
394 template <class TValue>
395 class CStaticTls : public CTlsBase
396 {
397 public:
398     /// Life span
399     typedef CSafeStaticLifeSpan TLifeSpan;
400     /// User cleanup function type
401     typedef void (*FUserCleanup)(void*  ptr);
402 
403     // Set user-provided cleanup function to be executed on destruction.
404     // Life span allows to control destruction of objects. Objects with
405     // the same life span are destroyed in the order reverse to their
406     // creation order.
CStaticTls(FUserCleanup user_cleanup=0,TLifeSpan life_span=TLifeSpan::GetDefault ())407     CStaticTls(FUserCleanup user_cleanup = 0,
408                TLifeSpan life_span = TLifeSpan::GetDefault())
409         : CTlsBase(false),
410           m_SafeHelper(user_cleanup, life_span)
411     {}
412 
413     /// Get the pointer previously stored by SetValue().
414     ///
415     /// Return 0 if no value has been stored, or if Reset() was last called.
416     /// @sa
417     ///   SetValue()
GetValue(void)418     TValue* GetValue(void)
419     {
420         if (!m_SafeHelper.m_Ptr) {
421             x_SafeInit();
422         }
423         return reinterpret_cast<TValue*> (x_GetValue());
424     }
425 
426     /// Define cleanup function type, FCleanup.
427     typedef void (*FCleanup)(TValue* value, void* cleanup_data);
428 
429     /// Set value.
430     ///
431     /// Cleanup previously stored value, and set the new value.
432     /// The "cleanup" function and "cleanup_data" will be used to
433     /// destroy the new "value" in the next call to SetValue() or Reset().
434     /// Do not cleanup if the new value is equal to the old one.
435     /// @param value
436     ///   New value to set.
437     /// @param cleanup
438     ///   Cleanup function.
439     ///   Do not cleanup if default of 0 is specified or if new value is the
440     ///   same as old value.
441     /// @param cleanup_data
442     ///   One of the parameters to the cleanup function.
443     /// @sa
444     ///   GetValue()
SetValue(TValue * value,FCleanup cleanup=0,void * cleanup_data=0,ENativeThreadCleanup native=eSkipCleanup)445     void SetValue(TValue* value, FCleanup cleanup = 0, void* cleanup_data = 0, ENativeThreadCleanup native = eSkipCleanup)
446     {
447         if (!m_SafeHelper.m_Ptr) {
448             x_SafeInit();
449         }
450         x_SetValue(value,
451                    reinterpret_cast<FCleanupBase> (cleanup), cleanup_data, native);
452     }
453 
454     /// Reset thread local storage.
455     ///
456     /// Reset thread local storage to its initial value (as it was before the
457     /// first call to SetValue()). Do cleanup if the cleanup function was
458     /// specified in the previous call to SetValue().
459     ///
460     /// Reset() will always be called automatically on the thread termination,
461     /// or when the TLS is destroyed.
Reset(void)462     void Reset(void)
463     {
464         if (!m_SafeHelper.m_Ptr) {
465             x_SafeInit();
466         }
467         x_Reset();
468     }
469 
470 private:
471     /// Object derived from CSafeStaticPtr_Base to help manage life time
472     /// of the object
473     CStaticTlsHelper m_SafeHelper;
474 
475     /// Initialize the object in SafeStaticRef-ish manner
476     void x_SafeInit(void);
477 };
478 #endif // NCBI_STATIC_TLS_VIA_SAFE_STATIC_REF
479 
480 class NCBI_XNCBI_EXPORT CUsedTlsBases
481 {
482 public:
483     CUsedTlsBases(void);
484     ~CUsedTlsBases(void);
485 
486     /// The function is called before thread termination to cleanup data
487     /// stored in the TLS.
488     void ClearAll(CTlsBase::ECleanupMode mode = CTlsBase::eCleanup_Toolkit);
489 
490     void Register(CTlsBase* tls);
491     void Deregister(CTlsBase* tls);
492 
493     /// Get the list of used TLS-es for the current thread
494     static CUsedTlsBases& GetUsedTlsBases(void);
495 
496     /// Clear used TLS-es for the current thread
497     static void ClearAllCurrentThread(CTlsBase::ECleanupMode mode = CTlsBase::eCleanup_Toolkit);
498 
499     /// Init TLS, call before creating thread
500     static void Init(void);
501 
502 private:
503     typedef set<CTlsBase*> TTlsSet;
504     TTlsSet m_UsedTls;
505 
506     static CStaticTls<CUsedTlsBases> sm_UsedTlsBases;
507 
508 private:
509     CUsedTlsBases(const CUsedTlsBases&);
510     void operator=(const CUsedTlsBases&);
511 };
512 
513 
514 /////////////////////////////////////////////////////////////////////////////
515 ///
516 /// CThread --
517 ///
518 /// Thread wrapper class.
519 ///
520 ///  Base class for user-defined threads. Creates the new thread, then
521 ///  calls user-provided Main() function. The thread then can be detached
522 ///  or joined. In any case, explicit destruction of the thread is prohibited.
523 
524 class NCBI_XNCBI_EXPORT CThread : public CObject
525 {
526     friend class CRef<CThread>;
527     friend class CTlsBase;
528 
529 public:
530     /// Constructor.
531     ///
532     /// Must be allocated in the heap only!.
533     CThread(void);
534 
535     /// Which mode should the thread run in.
536     enum ERunMode {
537         fRunDefault  = 0x00,    ///< Default mode
538         fRunDetached = 0x01,    ///< Run the thread detached (non-joinable)
539         fRunBound    = 0x10,    ///< Run thread in a 1:1 thread:LPW mode
540                                 ///< - may not be supported and will be
541                                 ///< ignored on some platforms
542         fRunUnbound  = 0x20,    ///< Run thread in a N:1 thread:LPW mode
543                                 ///< - may not be supported and will be
544                                 ///< ignored on some platforms
545         fRunNice     = 0x40,    ///< Run thread with low priority (MS-Win only)
546         fRunAllowST  = 0x100,   ///< Allow threads to run in single thread
547                                 ///< builds
548 
549         fRunCloneRequestContext = 0x200  ///< Clone parent's request context and pass it to the
550                                          ///< new thread. The flag can be used when processing
551                                          ///< the same request in multiple child threads.
552     };
553 
554     /// Bitwise OR'd flags for thread creation passed to Run().
555     typedef int TRunMode;
556 
557     /// Run the thread.
558     ///
559     /// Create a new thread, initialize it, and call user-provided Main()
560     /// method.
561     bool Run(TRunMode flags = fRunDefault);
562 
563     /// Inform the thread that user does not need to wait for its termination.
564     /// The thread object will be destroyed by Exit().
565     /// If the thread has already been terminated by Exit, Detach() will
566     /// also schedule the thread object for destruction.
567     /// NOTE:  it is no more safe to use this thread object after Detach(),
568     ///        unless there are still CRef<> based references to it!
569     void Detach(void);
570 
571     /// Wait for the thread termination.
572     /// The thread object will be scheduled for destruction right here,
573     /// inside Join(). Only one call to Join() is allowed.
574     void Join(void** exit_data = 0);
575 
576     /// Cancel current thread. If the thread is detached, then schedule
577     /// the thread object for destruction.
578     /// Cancellation is performed by throwing an exception of type
579     /// CExitThreadException to allow destruction of all objects in
580     /// thread's stack, so Exit() method shell not be called from any
581     /// destructor.
582     static void Exit(void* exit_data);
583 
584     /// If the thread has not been Run() yet, then schedule the thread object
585     /// for destruction, and return TRUE.
586     /// Otherwise, do nothing, and return FALSE.
587     bool Discard(void);
588 
589     /// Check if the thread has been terminated.
IsTerminated(void) const590     bool IsTerminated(void) const { return m_IsTerminated; }
591 
592     /// Get ID of current thread. When not using native threads, but CThread only,
593     /// the main thread is guaranteed to have zero id. With native threads the
594     /// main thread may have a non-zero id and it's more reliable to use IsMain().
595     typedef unsigned int TID;
596     static TID GetSelf(void);
597 
598     static bool IsMain(void);
599 
600     /// Get current CThread object (or NULL, if main thread)
601     static CThread* GetCurrentThread(void);
602 
603     /// Get system ID of the current thread - for internal use only.
604     /// The ID is unique only while the thread is running and may be
605     /// re-used by another thread later.
606     static void GetSystemID(TThreadSystemID* id);
607 
608     /// Get total amount of threads
609     /// This amount does not contain main thread.
610     static unsigned int GetThreadsCount();
611 
612     /// Set name for the current thread.
613     /// This works only on Linux, no-op on other platforms
614     static void SetCurrentThreadName(const CTempString&);
615 
616     /// Initialize main thread's TID.
617     /// The function must be called from the main thread if the application
618     /// is using non-toolkit threads. Otherwise getting thread id of a
619     /// native thread will return zero.
620     static void InitializeMainThreadId(void);
621 
622     /// Check if the application is exiting (entered the destructor).
623     /// Recommended to be used as while() condition by infinite threads
624     /// to stop them properly on exit.
625     /// @sa SetWaitForAllThreadsTimeout
IsAppExiting(void)626     static bool IsAppExiting(void) { return sm_IsExiting; }
627 
628     /// Set timeout for stopping all threads on application exit.
629     static void SetWaitForAllThreadsTimeout(const CTimeout& timeout);
630 
631 protected:
632     /// Derived (user-created) class must provide a real thread function.
633     virtual void* Main(void) = 0;
634 
635     /// Override this to execute finalization code.
636     /// Unlike destructor, this code will be executed before
637     /// thread termination and as a part of the thread.
638     virtual void OnExit(void);
639 
640     /// To be called only internally!
641     /// NOTE:  destructor of the derived (user-provided) class should be
642     ///        declared "protected", too!
643     virtual ~CThread(void);
644 
645     TThreadHandle GetThreadHandle();
646 
647 private:
648     TThreadHandle m_Handle;        ///< platform-dependent thread handle
649     bool          m_IsRun;         ///< if Run() was called for the thread
650     bool          m_IsDetached;    ///< if the thread is detached
651     bool          m_IsJoined;      ///< if Join() was called for the thread
652     bool          m_IsTerminated;  ///< if Exit() was called for the thread
653     CRef<CThread> m_SelfRef;       ///< "this" -- to avoid premature destruction
654     void*         m_ExitData;      ///< as returned by Main() or passed to Exit()
655     CRef<CRequestContext> m_ParentRequestContext;
656 
657     static bool     sm_IsExiting;
658     static CTimeout sm_WaitForThreadsTimeout;
659 
660 #if defined NCBI_THREAD_PID_WORKAROUND
661     friend class CProcess;
662     TPid          m_ThreadPID;     ///< Cache thread PID to detect forks
663 
664     static TPid sx_GetThreadPid(void);
665     static void sx_SetThreadPid(TPid pid);
666 #endif
667 
668     static atomic<unsigned int> sm_ThreadsCount;  ///< Total amount of threads
669 
670     /// initalize new thread id, must be called from Wrapper().
671     void x_InitializeThreadId(void);
672 
673     /// Function to use (internally) as the thread's startup function
674     static TWrapperRes Wrapper(TWrapperArg arg);
675     friend TWrapperRes ThreadWrapperCaller(TWrapperArg arg);
676 
677     // Wait for all threads to terminate. Note that the method does not request threads
678     // to stop, it just waits for the number of running threads to become zero.
679     // Return true if all threads have been stopped, false on timeout.
680     static bool WaitForAllThreads(void);
681     friend class CNcbiApplicationAPI;
682 
683     /// Prohibit copying and assigning
684     CThread(const CThread&);
685     CThread& operator= (const CThread&);
686 };
687 
688 
689 class NCBI_XNCBI_EXPORT CThreadException : EXCEPTION_VIRTUAL_BASE public CException
690 {
691 public:
692     enum EErrCode {
693         eRunError,          ///< Failed to run thread
694         eControlError,      ///< Failed to control thread's state
695         eOther              ///< Other thread errors
696     };
697 
698     /// Translate from the error code value to its string representation.
699     virtual const char* GetErrCodeString(void) const override;
700 
701     // Standard exception boilerplate code.
702     NCBI_EXCEPTION_DEFAULT(CThreadException, CException);
703 };
704 
705 
706 /* @} */
707 
708 
709 /////////////////////////////////////////////////////////////////////////////
710 
711 /////////////////////////////////////////////////////////////////////////////
712 //  IMPLEMENTATION of INLINE functions
713 /////////////////////////////////////////////////////////////////////////////
714 
715 
716 
717 /////////////////////////////////////////////////////////////////////////////
718 //  CTlsBase::
719 //
720 
721 inline
x_GetTlsData(void) const722 CTlsBase::STlsData* CTlsBase::x_GetTlsData(void)
723 const
724 {
725     if ( !m_Initialized ) {
726         return 0;
727     }
728 
729     void* tls_data;
730 
731 #if defined(NCBI_WIN32_THREADS)
732     tls_data = TlsGetValue(m_Key);
733 #elif defined(NCBI_POSIX_THREADS)
734     tls_data = pthread_getspecific(m_Key);
735 #else
736     tls_data = m_Key;
737 #endif
738 
739     return static_cast<STlsData*> (tls_data);
740 }
741 
742 
743 inline
x_GetValue(void) const744 void* CTlsBase::x_GetValue(void)
745 const
746 {
747     // Get TLS-stored structure
748     STlsData* tls_data = x_GetTlsData();
749 
750     // If assigned, extract and return user data
751     return tls_data ? tls_data->m_Value : 0;
752 }
753 
754 
755 
756 /////////////////////////////////////////////////////////////////////////////
757 //  CThread::
758 //
759 
760 #if !NCBI_STATIC_TLS_VIA_SAFE_STATIC_REF
761 template <class TValue>
762 inline
x_SafeInit(void)763 void CStaticTls<TValue>::x_SafeInit(void)
764 {
765     m_SafeHelper.Set(this);
766 }
767 #endif
768 
769 
770 /////////////////////////////////////////////////////////////////////////////
771 //  CThread::
772 //
773 
774 inline
GetThreadHandle()775 TThreadHandle CThread::GetThreadHandle()
776 {
777     return m_Handle;
778 }
779 
780 
781 inline
GetThreadsCount()782 unsigned int CThread::GetThreadsCount() {
783     return sm_ThreadsCount;
784 }
785 
786 
787 // Special value, stands for "no thread" thread ID
788 const CThread::TID kThreadID_None = 0xFFFFFFFF;
789 
790 
791 END_NCBI_SCOPE
792 
793 #endif  /* NCBITHR__HPP */
794