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