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