1 #ifndef NCBI_SAFE_STATIC__HPP
2 #define NCBI_SAFE_STATIC__HPP
3
4 /* $Id: ncbi_safe_static.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: Aleksey Grichenko
30 *
31 * File Description:
32 * Static variables safety - create on demand, destroy on termination
33 *
34 * CSafeStaticPtr_Base:: -- base class for CSafePtr<> and CSafeRef<>
35 * CSafeStaticPtr<>:: -- create variable on demand, destroy on program
36 * termination (see NCBIOBJ for CSafeRef<> class)
37 * CSafeStaticRef<>:: -- create variable on demand, destroy on program
38 * termination (see NCBIOBJ for CSafeRef<> class)
39 * CSafeStaticGuard:: -- guarantee for CSafePtr<> and CSafeRef<>
40 * destruction and cleanup
41 *
42 */
43
44 /// @file ncbi_safe_static.hpp
45 /// Static variables safety - create on demand, destroy on application
46 /// termination.
47
48 #include <corelib/ncbiobj.hpp>
49 #include <corelib/ncbimtx.hpp>
50 #include <corelib/ncbi_limits.h>
51 #include <array>
52
53
54 BEGIN_NCBI_SCOPE
55
56
57 /////////////////////////////////////////////////////////////////////////////
58 ///
59 /// CSafeStaticLifeSpan::
60 ///
61 /// Class for specifying safe static object life span.
62 ///
63
64 class NCBI_XNCBI_EXPORT CSafeStaticLifeSpan
65 {
66 public:
67 /// Predefined life levels for the safe static objects
68 enum ELifeLevel {
69 eLifeLevel_Default,
70 eLifeLevel_AppMain ///< Destroyed in CNcbiApplication::AppMain, if possible
71 };
72 /// Predefined life spans for the safe static objects
73 enum ELifeSpan {
74 eLifeSpan_Min = kMin_Int, ///< std static, not adjustable
75 eLifeSpan_Shortest = -20000,
76 eLifeSpan_Short = -10000,
77 eLifeSpan_Normal = 0,
78 eLifeSpan_Long = 10000,
79 eLifeSpan_Longest = 20000
80 };
81 /// Constructs a life span object from basic level and adjustment.
82 /// Generates warning (and assertion in debug mode) if the adjustment
83 /// argument is too big (<= -5000 or >= 5000). If span is eLifeSpan_Min
84 /// "adjust" is ignored.
85 CSafeStaticLifeSpan(ELifeSpan span, int adjust = 0);
86 /// Same as above but allows to specify life level.
87 /// E.g. eLifeLevel_AppMain allows to destroy such objects on AppMain exit
88 /// (useful for static objects that join threads in their destructors,
89 /// otherwise threads might disappear before such destructors are called).
90 CSafeStaticLifeSpan(ELifeLevel level, ELifeSpan span, int adjust = 0);
91
92 /// Get life level value.
GetLifeLevel() const93 ELifeLevel GetLifeLevel() const { return m_LifeLevel; }
94
95 /// Get life span value.
GetLifeSpan(void) const96 int GetLifeSpan(void) const { return m_LifeSpan; }
97
98 /// Get default life span (set to eLifeSpan_Min).
99 static CSafeStaticLifeSpan& GetDefault(void);
100
101 private:
102 ELifeLevel m_LifeLevel;
103 int m_LifeSpan;
104 };
105
106
107 /////////////////////////////////////////////////////////////////////////////
108 ///
109 /// CSafeStaticPtr_Base::
110 ///
111 /// Base class for CSafeStaticPtr<> and CSafeStaticRef<> templates.
112 ///
113
114 class NCBI_XNCBI_EXPORT CSafeStaticPtr_Base
115 {
116 public:
117 /// User cleanup function type
118 typedef void (*FUserCleanup)(void* ptr);
119
120 /// Life span
121 typedef CSafeStaticLifeSpan TLifeSpan;
122
123 ~CSafeStaticPtr_Base(void);
124
125 protected:
126 typedef CGuard<CSafeStaticPtr_Base> TInstanceMutexGuard;
127
128 /// Cleanup function type used by derived classes
129 typedef void (*FSelfCleanup)(CSafeStaticPtr_Base* safe_static,
130 TInstanceMutexGuard& guard);
131
132 /// Constructor.
133 ///
134 /// @param self_cleanup
135 /// Cleanup function to be executed on destruction,
136 /// provided by a derived class.
137 /// @param user_cleanup
138 /// User-provided cleanup function to be executed on destruction.
139 /// @param life_span
140 /// Life span allows to control destruction of objects. Objects with
141 /// the same life span are destroyed in the order reverse to their
142 /// creation order.
143 /// @sa CSafeStaticLifeSpan
CSafeStaticPtr_Base(FSelfCleanup self_cleanup,FUserCleanup user_cleanup=0,TLifeSpan life_span=TLifeSpan::GetDefault ())144 CSafeStaticPtr_Base(FSelfCleanup self_cleanup,
145 FUserCleanup user_cleanup = 0,
146 TLifeSpan life_span = TLifeSpan::GetDefault())
147 : m_SelfCleanup(self_cleanup),
148 m_UserCleanup(user_cleanup),
149 m_LifeSpan(life_span),
150 m_CreationOrder(x_GetCreationOrder())
151 {}
152
153 /// Pointer to the data
154 const void* m_Ptr;
155
156 DECLARE_CLASS_STATIC_MUTEX(sm_ClassMutex);
157
158 private:
159 friend struct SSimpleLock<CSafeStaticPtr_Base>;
160 friend struct SSimpleUnlock<CSafeStaticPtr_Base>;
161
Lock(void)162 void Lock(void)
163 {
164 CMutexGuard guard(sm_ClassMutex);
165 if (!m_InstanceMutex || !m_MutexRefCount) {
166 m_InstanceMutex = new CMutex;
167 m_MutexRefCount = 1;
168 }
169 ++m_MutexRefCount;
170 guard.Release();
171 m_InstanceMutex->Lock();
172 }
173
Unlock(void)174 void Unlock(void)
175 {
176 m_InstanceMutex->Unlock();
177 x_ReleaseInstanceMutex();
178 }
179
x_ReleaseInstanceMutex(void)180 void x_ReleaseInstanceMutex(void)
181 {
182 CMutexGuard guard(sm_ClassMutex);
183 if (--m_MutexRefCount > 0) return;
184 // Workaround for over-optimization - member assignments done immediately
185 // before exiting destructor can be dropped, so mutex and counter should
186 // be set to 0 before deleting the mutex.
187 CMutex* tmp = m_InstanceMutex;
188 m_InstanceMutex = 0;
189 m_MutexRefCount = 0;
190 delete tmp;
191 }
192
193 protected:
194 friend class CSafeStatic_Less;
195
196 FSelfCleanup m_SelfCleanup; // Derived class' cleanup function
197 FUserCleanup m_UserCleanup; // User-provided cleanup function
198 TLifeSpan m_LifeSpan; // Life span of the object
199 int m_CreationOrder; // Creation order of the object
200 int m_MutexRefCount; // Mutex reference counter.
201 CMutex* m_InstanceMutex; // Mutex used to create/destroy value.
202
203 static int x_GetCreationOrder(void);
204
205 // Return true if the object should behave like regular static
206 // (no delayed destruction).
x_IsStdStatic(void) const207 bool x_IsStdStatic(void) const
208 {
209 return (m_LifeSpan.GetLifeLevel() == CSafeStaticLifeSpan::eLifeLevel_Default) &&
210 (m_LifeSpan.GetLifeSpan() == int(CSafeStaticLifeSpan::eLifeSpan_Min));
211 }
212
213 // To be called by CSafeStaticGuard on the program termination
214 friend class CSafeStaticGuard;
x_Cleanup(void)215 void x_Cleanup(void)
216 {
217 // Note: x_Cleanup should always be called with sm_ClassMutex locked.
218 if ( m_SelfCleanup ) {
219 TInstanceMutexGuard guard(*this);
220 m_SelfCleanup(this, guard);
221 }
222 // Delete instance mutex if it's not used by other threads.
223 x_ReleaseInstanceMutex();
224 }
225 };
226
227
228 /// Comparison for safe static ptrs. Defines order of objects' destruction:
229 /// short living objects go first; if life span of several objects is the same,
230 /// the order of destruction is reverse to the order of their creation.
231 class CSafeStatic_Less
232 {
233 public:
234 typedef CSafeStaticPtr_Base* TPtr;
operator ()(const TPtr & ptr1,const TPtr & ptr2) const235 bool operator()(const TPtr& ptr1, const TPtr& ptr2) const
236 {
237 if (ptr1->m_LifeSpan.GetLifeSpan() == ptr2->m_LifeSpan.GetLifeSpan()) {
238 return ptr1->m_CreationOrder > ptr2->m_CreationOrder;
239 }
240 return ptr1->m_LifeSpan.GetLifeSpan() < ptr2->m_LifeSpan.GetLifeSpan();
241 }
242 };
243
244
245 /////////////////////////////////////////////////////////////////////////////
246 ///
247 /// CSafeStaticGuard::
248 ///
249 /// Register all on-demand variables,
250 /// destroy them on the program termination.
251
252 class NCBI_XNCBI_EXPORT CSafeStaticGuard
253 {
254 public:
255 /// Check if already initialized. If not - create the stack,
256 /// otherwise just increment the reference count.
257 CSafeStaticGuard(void);
258
259 /// Check reference count, and if it is zero, then destroy
260 /// all registered variables.
261 ~CSafeStaticGuard(void);
262
263 /// Add new on-demand variable to the cleanup stack.
Register(CSafeStaticPtr_Base * ptr)264 static void Register(CSafeStaticPtr_Base* ptr)
265 {
266 if ( sm_RefCount > 0 && ptr->x_IsStdStatic() ) {
267 // Do not add the object to the stack
268 return;
269 }
270
271 auto& stack = x_GetStack(ptr->m_LifeSpan.GetLifeLevel());
272
273 if ( !stack ) {
274 x_Get();
275 }
276 stack->insert(ptr);
277 }
278
279 /// Disable checking on child thread(s) running during destruction
280 static void DisableChildThreadsCheck();
281
282 /// Explicitly destroy all on-demand variables up to a specified level
283 static void Destroy(CSafeStaticLifeSpan::ELifeLevel level);
284
285 private:
286 // Initialize the guard, return pointer to it.
287 static CSafeStaticGuard* x_Get(void);
288
289 // Stack to keep registered variables.
290 typedef multiset<CSafeStaticPtr_Base*, CSafeStatic_Less> TStack;
291
292 // Get a stack based on a level
x_GetStack(CSafeStaticLifeSpan::ELifeLevel level)293 static TStack*& x_GetStack(CSafeStaticLifeSpan::ELifeLevel level)
294 {
295 static array<TStack*, 2> stacks;
296
297 const size_t index = level;
298 _ASSERT(level >= 0);
299 _ASSERT(index < stacks.size());
300 return stacks[index];
301 }
302
303 // Cleanup a stack
304 static void x_Cleanup(CMutexGuard& guard, TStack*& stack);
305
306 // Reference counter. The stack is destroyed when
307 // the last reference is removed.
308 static int sm_RefCount;
309
310 // Whether to check on child thread(s) running during destruction
311 static bool sm_ChildThreadsCheck;
312 };
313
314
315 /// Helper class for object allocation/deallocation.
316 /// Required to simplify friend declarations for classes with
317 /// private constructors/destructors and to support CObject
318 /// reference counting.
319
320 template <class T>
321 class CSafeStatic_Allocator
322 {
323 public:
324 /// Create a new class instance.
s_Create(void)325 static T* s_Create(void) { return new T; }
326
327 // CSafeStatic must use AddReference/RemoveReference for CObject derived
328 // classes. To do this there are two different versions of the following
329 // methods.
s_AddReference(void *)330 static void s_AddReference(void*) {}
s_AddReference(const void *)331 static void s_AddReference(const void*) {}
s_AddReference(CObject * ptr)332 static void s_AddReference(CObject* ptr)
333 {
334 if (ptr) ptr->AddReference();
335 }
s_RemoveReference(void * ptr)336 static void s_RemoveReference(void* ptr)
337 {
338 if (ptr) delete static_cast<T*>(ptr);
339 }
s_RemoveReference(const void * ptr)340 static void s_RemoveReference(const void* ptr)
341 {
342 if (ptr) delete const_cast<T*>(static_cast<const T*>(ptr));
343 }
s_RemoveReference(CObject * ptr)344 static void s_RemoveReference(CObject* ptr)
345 {
346 if (ptr) ptr->RemoveReference();
347 }
s_RemoveReference(const CObject * ptr)348 static void s_RemoveReference(const CObject* ptr)
349 {
350 if (ptr) ptr->RemoveReference();
351 }
352 };
353
354
355 /// Initialization and cleanup of a safe-static object.
356 /// Must implement at least Create() and Cleanup() methods.
357 /// Create() must create a new object and return the initialized
358 /// pointer. Cleanup() can be a no-op.
359 /// The default implementation allows to use it as a wrapper for
360 /// static callback functions.
361 template <class T>
362 class CSafeStatic_Callbacks
363 {
364 public:
365 /// The default implementation allows to use callback functions
366 /// rather than a new class.
367 typedef T* (*FCreate)(void);
368 typedef void (*FCleanup)(T& value);
369 typedef CSafeStatic_Allocator<T> TAllocator;
370
371 /// The constructor allows to use CSafeStatic_Callbacks as a simple
372 /// wrapper for static functions.
373 /// @param create
374 /// Initialization function which must create a new object. If null,
375 /// no special initialization is performed.
376 /// @param cleanup
377 /// Cleanup function. If null, no special cleanup is performed.
CSafeStatic_Callbacks(FCreate create=0,FCleanup cleanup=0)378 CSafeStatic_Callbacks(FCreate create = 0, FCleanup cleanup = 0)
379 : m_Create(create), m_Cleanup(cleanup) {}
380
381 /// Create new object.
382 /// @return
383 /// The allocated object.
Create(void)384 T* Create(void) {
385 return (m_Create) ? m_Create() : TAllocator::s_Create();
386 }
387
388 /// Perform cleanup before destruction.
389 /// @param ptr
390 /// Object to be destroyed using the selected allocator. The cleanup
391 /// method should not destroy the object itself, just perform any
392 /// additional actions (e.g. setting some external pointers to null).
Cleanup(T & value)393 void Cleanup(T& value) {
394 if ( m_Cleanup ) {
395 m_Cleanup(value);
396 }
397 }
398
399 private:
400 FCreate m_Create;
401 FCleanup m_Cleanup;
402 };
403
404
405 /////////////////////////////////////////////////////////////////////////////
406 ///
407 /// CSafeStatic<>::
408 ///
409 /// For on-demand object access, both non-CObject and CObject based.
410 /// Create the variable of type "T" on the first access. The default
411 /// implementation of allocator uses reference counter of CObject
412 /// class to prevent premature destruction.
413 /// Should be used only as static object. Otherwise the correct
414 /// initialization is not guaranteed.
415 /// @param T
416 /// Type of the variable to be stored.
417 /// @param Callbacks
418 /// A class implementing two methods to perform additional initialization
419 /// and cleanup:
420 /// void Create(T* ptr)
421 /// void Cleanup(T* ptr)
422 /// NOTE: The Cleanup callback must not destroy the object itself, just
423 /// perform any additional actions.
424 template <class T,
425 class Callbacks = CSafeStatic_Callbacks<T> >
426 class CSafeStatic : public CSafeStaticPtr_Base
427 {
428 public:
429 typedef Callbacks TCallbacks;
430 typedef CSafeStatic<T, TCallbacks> TThisType;
431 typedef CSafeStaticLifeSpan TLifeSpan;
432 typedef CSafeStatic_Allocator<T> TAllocator;
433
434 /// Callback function types. The default callback class can be used
435 /// as a simple wrapper for static functions.
436 /// @sa CSafeStatic_Callbacks
437 typedef typename CSafeStatic_Callbacks<T>::FCreate FCreate;
438 typedef typename CSafeStatic_Callbacks<T>::FCleanup FCleanup;
439
440 /// Constructor.
441 /// @param life_span
442 /// Life span allows to control destruction of objects. Objects with
443 /// the same life span are destroyed in the order reverse to their
444 /// creation order.
445 /// @sa CSafeStaticLifeSpan
CSafeStatic(TLifeSpan life_span=TLifeSpan::GetDefault ())446 CSafeStatic(TLifeSpan life_span = TLifeSpan::GetDefault())
447 : CSafeStaticPtr_Base(sx_SelfCleanup, 0, life_span)
448 {}
449
450 /// Constructor.
451 /// @param init
452 /// Callback function to be used to create the stored object.
453 /// @param cleanup
454 /// Callback function to be used for additional cleanup.
455 /// @param life_span
456 /// Life span allows to control destruction of objects. Objects with
457 /// the same life span are destroyed in the order reverse to their
458 /// creation order.
459 /// @sa CSafeStaticLifeSpan
CSafeStatic(FCreate create,FCleanup cleanup,TLifeSpan life_span=TLifeSpan::GetDefault ())460 CSafeStatic(FCreate create,
461 FCleanup cleanup,
462 TLifeSpan life_span = TLifeSpan::GetDefault())
463 : CSafeStaticPtr_Base(sx_SelfCleanup, 0, life_span),
464 m_Callbacks(create, cleanup)
465 {}
466
467 /// Constructor.
468 /// @param callbacks
469 /// Callbacks class instance.
470 /// @param life_span
471 /// Life span allows to control destruction of objects. Objects with
472 /// the same life span are destroyed in the order reverse to their
473 /// creation order.
474 /// @sa CSafeStaticLifeSpan
CSafeStatic(TCallbacks callbacks,TLifeSpan life_span=TLifeSpan::GetDefault ())475 CSafeStatic(TCallbacks callbacks,
476 TLifeSpan life_span = TLifeSpan::GetDefault())
477 : CSafeStaticPtr_Base(sx_SelfCleanup, 0, life_span),
478 m_Callbacks(callbacks)
479 {}
480
481 /// Create the variable if not created yet, return the reference.
Get(void)482 T& Get(void)
483 {
484 if ( !m_Ptr ) {
485 x_Init();
486 }
487 return *static_cast<T*>(const_cast<void*>(m_Ptr));
488 }
489
operator ->(void)490 T* operator-> (void) { return &Get(); }
operator *(void)491 T& operator* (void) { return Get(); }
492
493 private:
494 CSafeStatic(const CSafeStatic&);
495 CSafeStatic& operator=(const CSafeStatic&);
496
x_Init(void)497 void x_Init(void) {
498 TInstanceMutexGuard guard(*this);
499 if ( m_Ptr == 0 ) {
500 // Create the object and register for cleanup
501 T* ptr = 0;
502 try {
503 ptr = m_Callbacks.Create();
504 TAllocator::s_AddReference(ptr);
505 CSafeStaticGuard::Register(this);
506 m_Ptr = ptr;
507 }
508 catch (CException& e) {
509 TAllocator::s_RemoveReference(ptr);
510 NCBI_RETHROW_SAME(e, "CSafeStatic::Init: Register() failed");
511 }
512 catch (...) {
513 TAllocator::s_RemoveReference(ptr);
514 NCBI_THROW(CCoreException,eCore,
515 "CSafeStatic::Init: Register() failed");
516 }
517 }
518 }
519
520 // "virtual" cleanup function
sx_SelfCleanup(CSafeStaticPtr_Base * safe_static,TInstanceMutexGuard & guard)521 static void sx_SelfCleanup(CSafeStaticPtr_Base* safe_static,
522 TInstanceMutexGuard& guard)
523 {
524 TThisType* this_ptr = static_cast<TThisType*>(safe_static);
525 if ( T* ptr = static_cast<T*>(const_cast<void*>(this_ptr->m_Ptr)) ) {
526 TCallbacks callbacks = this_ptr->m_Callbacks;
527 this_ptr->m_Ptr = 0;
528 guard.Release();
529 callbacks.Cleanup(*ptr);
530 TAllocator::s_RemoveReference(ptr);
531 }
532 }
533
534 TCallbacks m_Callbacks;
535 };
536
537
538 /// Safe static callbacks version allowing initial value of type V
539 // to be set through template argument.
540 template<class T, class V, V value>
541 class CSafeStaticInit_Callbacks : public CSafeStatic_Callbacks<T>
542 {
543 public:
Create(void)544 T* Create(void) {
545 return new T(value);
546 }
547 };
548
549
550 /// Declare CSafeStatic<const type>, initialize it with 'init_value' of the same type.
551 #define SAFE_CONST_STATIC(type, init_value) \
552 SAFE_CONST_STATIC_EX(type, type, init_value)
553
554 /// Declare CSafeStatic<const type>, initialize it with 'init_value' of type
555 /// 'init_value_type'.
556 #define SAFE_CONST_STATIC_EX(type, init_value_type, init_value) \
557 CSafeStatic< const type, \
558 CSafeStaticInit_Callbacks<const type, init_value_type, init_value> >
559
560 /// Declare CSafeStatic<const string>, initialize it with 'const char* value'.
561 #define SAFE_CONST_STATIC_STRING(var, value) \
562 char SAFE_CONST_STATIC_STRING_##var[] = value; \
563 SAFE_CONST_STATIC_EX(std::string, const char*, \
564 SAFE_CONST_STATIC_STRING_##var) var
565
566
567
568 /////////////////////////////////////////////////////////////////////////////
569 ///
570 /// CSafeStaticPtr<>::
571 ///
572 /// For simple on-demand variables.
573 /// Create the variable of type "T" on demand,
574 /// destroy it on the program termination.
575 /// Should be used only as static object. Otherwise
576 /// the correct initialization is not guaranteed.
577 /// @deprecated Use CSafeStatic<> instead.
578
579 template <class T>
580 class CSafeStaticPtr : public CSafeStaticPtr_Base
581 {
582 public:
583 typedef CSafeStaticLifeSpan TLifeSpan;
584
585 /// Constructor.
586 ///
587 /// @param user_cleanup
588 /// User-provided cleanup function to be executed on destruction.
589 /// @param life_span
590 /// Life span allows to control destruction of objects.
591 /// @sa CSafeStaticPtr_Base
CSafeStaticPtr(FUserCleanup user_cleanup=0,TLifeSpan life_span=TLifeSpan::GetDefault ())592 CSafeStaticPtr(FUserCleanup user_cleanup = 0,
593 TLifeSpan life_span = TLifeSpan::GetDefault())
594 : CSafeStaticPtr_Base(sx_SelfCleanup, user_cleanup, life_span)
595 {}
596
597 /// Create the variable if not created yet, return the reference.
Get(void)598 T& Get(void)
599 {
600 if ( !m_Ptr ) {
601 x_Init();
602 }
603 return *static_cast<T*>(const_cast<void*>(m_Ptr));
604 }
605 /// Get the existing object or create a new one using the provided
606 /// FUserCreate object.
607 /// @deprecated Use CSafeStatic class instead.
608 template <class FUserCreate>
609 NCBI_DEPRECATED
Get(FUserCreate user_create)610 T& Get(FUserCreate user_create)
611 {
612 if ( !m_Ptr ) {
613 x_Init(user_create);
614 }
615 return *static_cast<T*>(const_cast<void*>(m_Ptr));
616 }
617
operator ->(void)618 T* operator -> (void) { return &Get(); }
operator *(void)619 T& operator * (void) { return Get(); }
620
621 /// Initialize with an existing object. The object MUST be
622 /// allocated with "new T" -- it will be destroyed with
623 /// "delete object" in the end. Set() works only for
624 /// not yet initialized safe-static variables.
625 void Set(T* object);
626
627 private:
628 // Initialize the object
629 void x_Init(void);
630
631 template <class FUserCreate>
632 void x_Init(FUserCreate user_create);
633
634 // "virtual" cleanup function
sx_SelfCleanup(CSafeStaticPtr_Base * safe_static,TInstanceMutexGuard & guard)635 static void sx_SelfCleanup(CSafeStaticPtr_Base* safe_static,
636 TInstanceMutexGuard& guard)
637 {
638 CSafeStaticPtr<T>* this_ptr = static_cast<CSafeStaticPtr<T>*>(safe_static);
639 if ( T* ptr = static_cast<T*>(const_cast<void*>(this_ptr->m_Ptr)) ) {
640 CSafeStaticPtr_Base::FUserCleanup user_cleanup = this_ptr->m_UserCleanup;
641 this_ptr->m_Ptr = 0;
642 guard.Release();
643 if ( user_cleanup ) {
644 user_cleanup(ptr);
645 }
646 delete ptr;
647 }
648 }
649 };
650
651
652
653 /////////////////////////////////////////////////////////////////////////////
654 ///
655 /// CSafeStaticRef<>::
656 ///
657 /// For on-demand CObject-derived object.
658 /// Create the variable of type "T" using CRef<>
659 /// (to avoid premature destruction).
660 /// Should be used only as static object. Otherwise
661 /// the correct initialization is not guaranteed.
662 /// @deprecated Use CSafeStatic<> instead.
663 template <class T>
664 class CSafeStaticRef : public CSafeStaticPtr_Base
665 {
666 public:
667 typedef CSafeStaticLifeSpan TLifeSpan;
668
669 /// Constructor.
670 ///
671 /// @param user_cleanup
672 /// User-provided cleanup function to be executed on destruction.
673 /// @param life_span
674 /// Life span allows to control destruction of objects.
675 /// @sa CSafeStaticPtr_Base
CSafeStaticRef(FUserCleanup user_cleanup=0,TLifeSpan life_span=TLifeSpan::GetDefault ())676 CSafeStaticRef(FUserCleanup user_cleanup = 0,
677 TLifeSpan life_span = TLifeSpan::GetDefault())
678 : CSafeStaticPtr_Base(sx_SelfCleanup, user_cleanup, life_span)
679 {}
680
681 /// Create the variable if not created yet, return the reference.
Get(void)682 T& Get(void)
683 {
684 if ( !m_Ptr ) {
685 x_Init();
686 }
687 return *static_cast<T*>(const_cast<void*>(m_Ptr));
688 }
689 /// Get the existing object or create a new one using the provided
690 /// FUserCreate object.
691 /// @deprecated Use CSafeStatic class instead.
692 template <class FUserCreate>
693 NCBI_DEPRECATED
Get(FUserCreate user_create)694 T& Get(FUserCreate user_create)
695 {
696 if ( !m_Ptr ) {
697 x_Init(user_create);
698 }
699 return *static_cast<T*>(const_cast<void*>(m_Ptr));
700 }
701
operator ->(void)702 T* operator -> (void) { return &Get(); }
operator *(void)703 T& operator * (void) { return Get(); }
704
705 /// Initialize with an existing object. The object MUST be
706 /// allocated with "new T" to avoid premature destruction.
707 /// Set() works only for un-initialized safe-static variables.
708 void Set(T* object);
709
710 private:
711 // Initialize the object and the reference
712 void x_Init(void);
713
714 template <class FUserCreate>
715 void x_Init(FUserCreate user_create);
716
717 // "virtual" cleanup function
sx_SelfCleanup(CSafeStaticPtr_Base * safe_static,TInstanceMutexGuard & guard)718 static void sx_SelfCleanup(CSafeStaticPtr_Base* safe_static,
719 TInstanceMutexGuard& guard)
720 {
721 CSafeStaticRef<T>* this_ptr = static_cast<CSafeStaticRef<T>*>(safe_static);
722 if ( T* ptr = static_cast<T*>(const_cast<void*>(this_ptr->m_Ptr)) ) {
723 CSafeStaticPtr_Base::FUserCleanup user_cleanup = this_ptr->m_UserCleanup;
724 this_ptr->m_Ptr = 0;
725 guard.Release();
726 if ( user_cleanup ) {
727 user_cleanup(ptr);
728 }
729 ptr->RemoveReference();
730 }
731 }
732 };
733
734
735 /////////////////////////////////////////////////////////////////////////////
736 ///
737 /// This static variable must be present in all modules using
738 /// on-demand static variables. The guard must be created first
739 /// regardless of the modules initialization order.
740
741 static CSafeStaticGuard s_CleanupGuard;
742
743
744 /////////////////////////////////////////////////////////////////////////////
745 //
746 // Large inline methods
747
748 template <class T>
749 inline
Set(T * object)750 void CSafeStaticPtr<T>::Set(T* object)
751 {
752 TInstanceMutexGuard guard(*this);
753 if ( m_Ptr == 0 ) {
754 // Set the new object and register for cleanup
755 if ( object ) {
756 m_Ptr = object;
757 CSafeStaticGuard::Register(this);
758 }
759 }
760 }
761
762
763 template <class T>
764 inline
x_Init(void)765 void CSafeStaticPtr<T>::x_Init(void)
766 {
767 TInstanceMutexGuard guard(*this);
768 if ( m_Ptr == 0 ) {
769 // Create the object and register for cleanup
770 m_Ptr = new T;
771 CSafeStaticGuard::Register(this);
772 }
773 }
774
775
776 template <class T>
777 template <class FUserCreate>
778 inline
x_Init(FUserCreate user_create)779 void CSafeStaticPtr<T>::x_Init(FUserCreate user_create)
780 {
781 TInstanceMutexGuard guard(*this);
782 if ( m_Ptr == 0 ) {
783 // Create the object and register for cleanup
784 m_Ptr = user_create();
785 if ( m_Ptr ) {
786 CSafeStaticGuard::Register(this);
787 }
788 }
789 }
790
791
792 template <class T>
793 inline
Set(T * object)794 void CSafeStaticRef<T>::Set(T* object)
795 {
796 TInstanceMutexGuard guard(*this);
797 if ( m_Ptr == 0 ) {
798 // Set the new object and register for cleanup
799 if ( object ) {
800 object->AddReference();
801 m_Ptr = object;
802 CSafeStaticGuard::Register(this);
803 }
804 }
805 }
806
807
808 template <class T>
809 inline
x_Init(void)810 void CSafeStaticRef<T>::x_Init(void)
811 {
812 TInstanceMutexGuard guard(*this);
813 if ( m_Ptr == 0 ) {
814 // Create the object and register for cleanup
815 T* ptr = new T;
816 ptr->AddReference();
817 m_Ptr = ptr;
818 CSafeStaticGuard::Register(this);
819 }
820 }
821
822
823 template <class T>
824 template <class FUserCreate>
825 inline
x_Init(FUserCreate user_create)826 void CSafeStaticRef<T>::x_Init(FUserCreate user_create)
827 {
828 TInstanceMutexGuard guard(*this);
829 if ( m_Ptr == 0 ) {
830 // Create the object and register for cleanup
831 T* ptr = user_create();
832 if ( ptr ) {
833 ptr->AddReference();
834 m_Ptr = ptr;
835 CSafeStaticGuard::Register(this);
836 }
837 }
838 }
839
840
841
842 END_NCBI_SCOPE
843
844 #endif /* NCBI_SAFE_STATIC__HPP */
845