Replace NULL with nullptr
[libcds.git] / cds / gc / hrc / hrc.h
1 //$$CDS-header$$
2
3 #ifndef __CDS_GC_HRC_HRC_H
4 #define __CDS_GC_HRC_HRC_H
5
6 /*
7     Editions:
8         2008.03.08    Maxim.Khiszinsky    Created
9 */
10
11 #include <cds/refcounter.h>
12 #include <cds/lock/spinlock.h>
13 #include <cds/gc/exception.h>
14
15 #include <cds/gc/hrc/details/hrc_fwd.h>
16 #include <cds/gc/hrc/details/hrc_retired.h>
17
18 #include <cds/gc/hzp/details/hp_alloc.h>
19
20 #include <cds/details/noncopyable.h>
21
22 #if CDS_COMPILER == CDS_COMPILER_MSVC
23 #   pragma warning(push)
24 // warning C4251: 'cds::gc::hzp::GarbageCollector::m_pListHead' : class 'cds::cxx11_atomics::atomic<T>'
25 // needs to have dll-interface to be used by clients of class 'cds::gc::hzp::GarbageCollector'
26 #   pragma warning(disable: 4251)
27 #endif
28
29
30 namespace cds { namespace gc {
31
32     // forwards
33     class HRC;
34
35     /// Gidenstam's memory reclamation schema (HRC)
36     /**
37
38     \par Sources:
39         - [2006] A.Gidenstam "Algorithms for synchronization and consistency
40                 in concurrent system services", Chapter 5 "Lock-Free Memory Reclamation"
41                 Thesis for the degree of Doctor    of Philosophy
42         - [2005] Anders Gidenstam, Marina Papatriantafilou and Philippas Tsigas "Allocating
43                 memory in a lock-free manner", Proceedings of the 13th Annual European
44                 Symposium on Algorithms (ESA 2005), Lecture Notes in Computer
45                 Science Vol. 3669, pages 229 \96 242, Springer-Verlag, 2005
46
47
48         The \p %cds::gc::hrc namespace and its members are internal representation of the GC and should not be used directly.
49         Use \p cds::gc::HRC class in your code.
50
51         This reclamation schema combines Michael's Hazard Pointer schema (see \p cds::gc::hzp)
52         for deferred safe reclamation of unused objects and the reference counting
53         for controlling lifetime of the objects.
54
55         HRC garbage collector is a singleton. The main user-level part of HRC schema is
56         GC class and its nested classes. Before use any HRC-related class you must initialize HRC garbage collector
57         by contructing \p %cds::gc::HRC object in beginning of your <tt>main()</tt>.
58         See \p cds::gc::HRC class for explanation.
59     */
60     namespace hrc {
61
62         /// Base class for all HRC-based container's node
63         /**
64             This interface is placed to the separate class since in the presence of a garbage collector
65             the lifetime of the node is greater than lifetime of its container.
66             Reclaimed node may be physically deleted later than its container.
67             So, the ContainerNode must be smarter than the usual.
68         */
69         class ContainerNode
70         {
71         protected:
72
73             friend class GarbageCollector;
74             friend class ThreadGC;
75             friend class gc::HRC;
76
77             unsigned_ref_counter        m_RC        ;    ///< reference counter
78             CDS_ATOMIC::atomic<bool>    m_bTrace    ;    ///< \p true - node is tracing by Scan
79             CDS_ATOMIC::atomic<bool>    m_bDeleted  ;    ///< \p true - node is deleted
80
81         protected:
82             //@cond
83             ContainerNode() ;               // inline, see hrc_inline.h
84             virtual ~ContainerNode()    ;   // inline, see hrc_inline.h
85             //@endcond
86
87         public:
88             /// Returns count of reference for the node
89             unsigned_ref_counter::ref_counter_type  getRefCount() const CDS_NOEXCEPT
90             {
91                 return m_RC.value();
92             }
93
94             /// Increments the reference counter of the node
95             void            incRefCount() CDS_NOEXCEPT
96             {
97                 m_RC.inc();
98             }
99
100             /// Decrements the reference counter of the node. Returns \p true if ref counter is 0.
101             bool            decRefCount() CDS_NOEXCEPT
102             {
103                 return m_RC.dec();
104             }
105
106             /// Returns the mark whether the node is deleted
107             bool            isDeleted() const CDS_NOEXCEPT
108             {
109                 return m_bDeleted.load( CDS_ATOMIC::memory_order_acquire );
110             }
111
112         protected:
113             //@cond
114             void clean( CDS_ATOMIC::memory_order order ) CDS_NOEXCEPT
115             {
116                 m_bDeleted.store( false, order );
117                 m_bTrace.store( false, order );
118             }
119             //@endcond
120
121         protected:    // GC interface
122             /**
123                 [Gidenstam 2006]: "The procedure \p CleanUpNode will make sure that all claimed references from
124                 the links of the given node will only point to active nodes, thus removing redundant
125                 passages through an arbitrary number of deleted nodes"
126
127                 The pseudocode of this method must be like following:
128                 \code
129                 void cleanUp( ThreadGC * pGC )
130                     for all x where link[x] of node is reference-counted do
131                 retry:
132                         node1 := link[x];
133                         if node1 != nullptr and node1.m_bDeleted then
134                             node2 := node1->link[x];
135                             pGC->CASRef( this->link[x], node1, node2 );
136                             pGC->releaseRef( node2 );
137                             pGC->releaseRef( node1 );
138                             goto retry;
139                         pGC->releaseRef(node1);
140                 \endcode
141
142                 Be aware to use hazard pointers inside implementation of this method. cleanUp is called from
143                 the container's method when deleting the nodes. However, some hazard pointers may be occupied
144                 by container's method. You should allocate new hazard pointers inside \p cleanUp method, for example:
145                 \code
146                     gc::hrc::AutoHPArray<2> hpArr( *pGC );
147                 \endcode
148             */
149             virtual void    cleanUp( ThreadGC * pGC ) = 0;
150
151             /**
152                 [Gidenstam 2006]: "The procedure \p TerminateNode will make sure that none of the links in the
153                 given node will have any claim on any other node. TerminateNode is called on
154                 a deleted node when there are no claims from any other node or thread to the
155                 node"
156
157                 The pseudocode of this method must be like following:
158                 \code
159                 void terminate( ThreadGC * pGC, bool bConcurrent)
160                     if !bConcurrent
161                         for all this->link where link is reference-counted do
162                             link := nullptr;
163                     else
164                         for all this->link where link is reference-counted do
165                             repeat node1 := link;
166                             until pGC->CASRef(link,node1,nullptr);
167                 \endcode
168             */
169             virtual void    terminate( ThreadGC * pGC, bool bConcurrent ) = 0;
170
171         public:
172             /// Method to destroy (deallocate) node. Depends on node's allocator
173             //virtual void    destroy() = 0;
174         };
175
176         //@cond
177         /// HRC GC implementation details
178         namespace details {
179
180             /// Hazard pointer guard
181             typedef gc::hzp::details::HPGuardT<ContainerNode *>    HPGuard;
182
183             /// Array of hazard pointers.
184             /**
185                 This is wrapper for cds::gc::hzp::details::HPArray class
186             */
187 #ifdef CDS_CXX11_TEMPLATE_ALIAS_SUPPORT
188             template <size_t Count> using HPArray = gc::hzp::details::HPArrayT<ContainerNode *, Count >;
189 #else
190             template <size_t Count>
191             class HPArray: public gc::hzp::details::HPArrayT<ContainerNode *, Count>
192             {};
193 #endif
194
195             /// HP record of the thread
196             /**
197                 This structure is single writer - multiple reader type. The writer is the thread owned the record
198             */
199             struct thread_descriptor {
200                 typedef ContainerNode * hazard_ptr       ; ///< base type of hazard pointer
201
202                 hzp::details::HPAllocator<hazard_ptr>   m_hzp           ;   ///< array of hazard pointers. Implicit \ref CDS_DEFAULT_ALLOCATOR dependence
203                 details::retired_vector                 m_arrRetired    ;   ///< array of retired pointers
204
205                 //@cond
206                 thread_descriptor( const GarbageCollector& HzpMgr ) ;    // inline
207                 ~thread_descriptor()
208                 {}
209                 //@endcond
210
211                 /// clear all hazard pointers
212                 void clear()
213                 {
214                     m_hzp.clear();
215                 }
216             };
217         }    // namespace details
218         //@endcond
219
220         /// Gidenstam's Garbage Collector
221         /**
222             This GC combines Hazard Pointers (HP) reclamation method by Michael's and the well-known reference counting
223             reclamation schema. The HP method is light-weight algorithm guarding local references only. Reference counting
224             schema is harder than HP with relation to the performance but can guard global references too.
225             Using Gidenstam's GC it can be possible to safely introduce to the lock-free data structures
226             very useful concepts like iterators.
227
228             GarbageCollector is the singleton.
229         */
230         class CDS_EXPORT_API GarbageCollector
231         {
232         public:
233             typedef cds::atomicity::event_counter  event_counter   ;   ///< event counter type
234
235             /// GC internal statistics
236             struct internal_state {
237                 size_t              nHPCount                ;   ///< HP count per thread (const)
238                 size_t              nMaxThreadCount         ;   ///< Max thread count (const)
239                 size_t              nMaxRetiredPtrCount     ;   ///< Max retired pointer count per thread (const)
240                 size_t              nHRCRecSize             ;   ///< Size of HRC record, bytes (const)
241
242                 size_t              nHRCRecAllocated        ;   ///< Count of HRC record allocations
243                 size_t              nHRCRecUsed             ;   ///< Count of HRC record used
244                 size_t              nTotalRetiredPtrCount   ;   ///< Current total count of retired pointers
245                 size_t              nRetiredPtrInFreeHRCRecs;   ///< Count of retired pointer in free (unused) HP records
246
247
248                 event_counter::value_type   evcAllocHRCRec        ; ///< Event count of thread descriptor allocation
249                 event_counter::value_type   evcRetireHRCRec        ; ///< Event count of thread descriptor reclamation
250                 event_counter::value_type   evcAllocNewHRCRec    ; ///< Event count of new thread descriptor allocation
251                 event_counter::value_type   evcDeleteHRCRec        ; ///< Event count of thread descriptor deletion
252                 event_counter::value_type   evcScanCall            ; ///< Number of calls Scan
253                 event_counter::value_type   evcHelpScanCalls    ; ///< Number of calls HelpScan
254                 event_counter::value_type   evcCleanUpAllCalls  ; ///< Number of calls CleanUpAll
255                 event_counter::value_type   evcDeletedNode        ; ///< Node deletion event counter
256                 event_counter::value_type   evcScanGuarded      ; ///< Count of retired nodes that could not be deleted on Scan phase
257                 event_counter::value_type   evcScanClaimGuarded ; ///< Count of retired node that could not be deleted on Scan phase because of m_nClaim != 0
258
259 #ifdef CDS_DEBUG
260                 event_counter::value_type   evcNodeConstruct    ; ///< Count of constructed ContainerNode
261                 event_counter::value_type   evcNodeDestruct     ; ///< Count of destructed ContainerNode
262 #endif
263             };
264
265             /// "Global GC object is nullptr" exception
266             CDS_DECLARE_EXCEPTION( HRCGarbageCollectorEmpty, "Global cds::gc::hrc::GarbageCollector is NULL" );
267
268             /// Not enough required Hazard Pointer count
269             CDS_DECLARE_EXCEPTION( HRCTooMany, "Not enough required Hazard Pointer count" );
270
271         private:
272             /// Internal statistics by events
273             struct statistics {
274                 event_counter  m_AllocHRCThreadDesc        ; ///< Event count of thread descriptor allocation
275                 event_counter  m_RetireHRCThreadDesc        ; ///< Event count of thread descriptor reclamation
276                 event_counter  m_AllocNewHRCThreadDesc        ; ///< Event count of new thread descriptor allocation
277                 event_counter  m_DeleteHRCThreadDesc        ; ///< Event count of deletion of thread descriptor
278                 event_counter  m_ScanCalls                    ; ///< Number of calls Scan
279                 event_counter  m_HelpScanCalls             ; ///< Number of calls HelpScan
280                 event_counter  m_CleanUpAllCalls           ; ///< Number of calls CleanUpAll
281
282                 event_counter  m_DeletedNode                ; ///< Node deletion event counter
283                 event_counter  m_ScanGuarded               ; ///< Count of retired nodes that could not be deleted on Scan phase
284                 event_counter  m_ScanClaimGuarded          ; ///< Count of retired node that could not be deleted on Scan phase because of m_nClaim != 0
285
286 #           ifdef CDS_DEBUG
287                 event_counter  m_NodeConstructed           ; ///< Count of ContainerNode constructed
288                 event_counter  m_NodeDestructed            ; ///< Count of ContainerNode destructed
289 #           endif
290             };
291
292             /// HRC control structure of global thread list
293             struct thread_list_node: public details::thread_descriptor
294             {
295                 thread_list_node *  m_pNext     ; ///< next list record
296                 ThreadGC *          m_pOwner    ; ///< Owner of record
297                 CDS_ATOMIC::atomic<cds::OS::ThreadId>   m_idOwner   ; ///< Id of thread owned; 0 - record is free
298                 bool                m_bFree        ; ///< Node is help-scanned
299
300                 //@cond
301                 thread_list_node( const GarbageCollector& HzpMgr )
302                     : thread_descriptor( HzpMgr ),
303                     m_pNext( nullptr ),
304                     m_pOwner( nullptr ),
305                     m_idOwner(cds::OS::c_NullThreadId),
306                     m_bFree( false )
307                 {}
308
309                 ~thread_list_node()
310                 {
311                     assert( m_pOwner == nullptr );
312                     assert( m_idOwner.load( CDS_ATOMIC::memory_order_relaxed ) == cds::OS::c_NullThreadId );
313                 }
314                 //@endcond
315             };
316
317         private:
318             CDS_ATOMIC::atomic<thread_list_node *> m_pListHead  ;  ///< Head of thread list
319
320             static GarbageCollector *    m_pGC    ;    ///< HRC garbage collector instance
321
322             statistics              m_Stat                  ;    ///< Internal statistics
323             bool                    m_bStatEnabled          ;    ///< @a true - accumulate internal statistics
324
325             const size_t            m_nHazardPointerCount   ;    ///< max count of thread's hazard pointer
326             const size_t            m_nMaxThreadCount       ;    ///< max count of thread
327             const size_t            m_nMaxRetiredPtrCount   ;    ///< max count of retired ptr per thread
328
329         private:
330             //@cond
331             GarbageCollector(
332                 size_t nHazardPtrCount,            ///< number of hazard pointers
333                 size_t nMaxThreadCount,            ///< max number of threads
334                 size_t nRetiredNodeArraySize    ///< size of array of retired node
335             );
336             ~GarbageCollector();
337             //@endcond
338
339             /// Allocates new HRC control structure from the heap (using operator new)
340             thread_list_node *    newHRCThreadDesc();
341
342             /// Deletes \p pNode control structure
343             void                deleteHRCThreadDesc( thread_list_node * pNode );
344
345             /// Clears retired nodes of \p pNode control structure
346             void                clearHRCThreadDesc( thread_list_node * pNode );
347
348             /// Finds HRC control structure for current thread
349             thread_list_node *    getHRCThreadDescForCurrentThread() const;
350
351         public:
352             /// Create global instance of GarbageCollector
353             static void    CDS_STDCALL    Construct(
354                 size_t nHazardPtrCount = 0,        ///< number of hazard pointers
355                 size_t nMaxThreadCount = 0,        ///< max threads count
356                 size_t nMaxNodeLinkCount = 0,    ///< max number of links a @ref ContainerNode can contain
357                 size_t nMaxTransientLinks = 0    ///< max number of links in live nodes that may transiently point to a deleted node
358                 );
359
360             /// Destroy global instance of GarbageCollector
361             static void    CDS_STDCALL        Destruct();
362
363             /// Get global instance of GarbageCollector
364             static GarbageCollector&   instance()
365             {
366                 if ( !m_pGC )
367                     throw HRCGarbageCollectorEmpty();
368                 return *m_pGC;
369             }
370
371             /// Checks if global GC object is constructed and may be used
372             static bool isUsed()
373             {
374                 return m_pGC != nullptr;
375             }
376
377             /// Get max count of hazard pointers as defined in @ref Construct call
378             size_t            getHazardPointerCount() const
379             {
380                 return m_nHazardPointerCount;
381             }
382
383             /// Get max thread count as defined in @ref Construct call
384             size_t            getMaxThreadCount() const
385             {
386                 return m_nMaxThreadCount;
387             }
388
389             /// Get max retired pointers count. It is calculated by the parameters of @ref Construct call
390             size_t            getMaxRetiredPtrCount() const
391             {
392                 return m_nMaxRetiredPtrCount;
393             }
394
395             /// Get internal statistics
396             internal_state& getInternalState( internal_state& stat) const;
397
398             /// Check if statistics enabled
399             bool              isStatisticsEnabled() const
400             {
401                 return m_bStatEnabled;
402             }
403
404             /// Enable internal statistics
405             bool              enableStatistics( bool bEnable )
406             {
407                 bool bCurEnabled = m_bStatEnabled;
408                 m_bStatEnabled = bEnable;
409                 return bCurEnabled;
410             }
411
412             /// Checks that required hazard pointer count \p nRequiredCount is less or equal then max hazard pointer count
413             /**
414                 If \p nRequiredCount > getHazardPointerCount() then the exception HZPTooMany is thrown
415             */
416             static void checkHPCount( unsigned int nRequiredCount )
417             {
418                 if ( instance().getHazardPointerCount() < nRequiredCount )
419                     throw HRCTooMany();
420             }
421
422         public:    // Internals for threads
423
424             /// Allocates HRC thread descriptor (thread interface)
425             details::thread_descriptor * allocateHRCThreadDesc( ThreadGC * pThreadGC );
426
427             /// Retires HRC thread descriptor (thread interface)
428             void retireHRCThreadDesc( details::thread_descriptor * pRec );
429
430             /// The main method of GC
431             /**
432                 The procedure searches through all not yet reclaimed nodes deleted by this thread
433                 and reclaim only those that does not have any matching hazard pointers and do not have any
434                 counted references from any links inside of nodes.
435                 @a Scan is called in context of thread owned \p pRec.
436             */
437             void Scan( ThreadGC * pThreadGC );
438
439             /// Manage free thread_descriptor records and move all retired pointers to \p pThreadGC
440             void HelpScan( ThreadGC * pThreadGC );
441
442             /// Global clean up
443             /**
444                 The procedure try to remove redundant claimed references from links in deleted nodes
445                 that has been deleted by any thread. \p pThreadGC - ThreadGC of calling thread
446             */
447             void CleanUpAll( ThreadGC * pThreadGC );
448
449             //@cond
450             void try_retire( ThreadGC * pThreadGC ) ;   // inline in hrc_inline.h
451             //@endcond
452
453 #   ifdef CDS_DEBUG
454         public:
455             //@cond
456             void dbgNodeConstructed() { ++m_Stat.m_NodeConstructed; }
457             void dbgNodeDestructed()  { ++m_Stat.m_NodeDestructed;  }
458             //@endcond
459 #   endif
460
461         };
462
463         class AutoHPGuard;
464
465         /// Thread's Garbage collector
466         /**
467             To use HRC reclamation schema each thread object must be linked with the object of ThreadGC class
468             that interacts with GarbageCollector global object. The linkage is performed by calling cds::threading \p Manager::attachThread()
469             on the start of each thread that uses HRC GC. Before terminating the thread linked to HRC GC it is necessary to call
470             cds::threading \p Manager::detachThread().
471         */
472         class ThreadGC: cds::details::noncopyable
473         {
474             GarbageCollector&               m_gc    ; ///< master garbage collector
475             details::thread_descriptor *    m_pDesc ; ///< descriptor of GC data for the thread
476
477             friend class GarbageCollector;
478
479         public:
480             //@cond
481             ThreadGC()
482                 : m_gc( GarbageCollector::instance() )
483                 , m_pDesc( nullptr )
484             {}
485             ~ThreadGC()
486             {
487                 fini();
488             }
489             //@endcond
490
491             /// Checks if thread GC is initialized
492             bool    isInitialized() const { return m_pDesc != nullptr; }
493
494             /// Initialization. Multiple calls is allowed
495             void init()
496             {
497                 if ( !m_pDesc )
498                     m_pDesc = m_gc.allocateHRCThreadDesc( this );
499             }
500
501             /// Finalization. Multiple calls is allowed
502             void fini()
503             {
504                 if ( m_pDesc ) {
505                     cleanUpLocal();
506                     m_gc.Scan( this );
507                     details::thread_descriptor * pRec = m_pDesc;
508                     m_pDesc = nullptr;
509                     if  ( pRec )
510                         m_gc.retireHRCThreadDesc( pRec );
511                 }
512             }
513         public:    // HRC garbage collector methods
514
515             /// Initializes HP guard \p guard
516             details::HPGuard& allocGuard()
517             {
518                 assert( m_pDesc != nullptr );
519                 return m_pDesc->m_hzp.alloc();
520             }
521
522             /// Frees HP guard \p guard
523             void freeGuard( details::HPGuard& guard )
524             {
525                 assert( m_pDesc != nullptr );
526                 m_pDesc->m_hzp.free( guard );
527             }
528
529             /// Initializes HP guard array \p arr
530             template <size_t Count>
531             void allocGuard( details::HPArray<Count>& arr )
532             {
533                 assert( m_pDesc != nullptr );
534                 m_pDesc->m_hzp.alloc( arr );
535             }
536
537             /// Frees HP guard array \p arr
538             template <size_t Count>
539             void freeGuard( details::HPArray<Count>& arr )
540             {
541                 assert( m_pDesc != nullptr );
542                 m_pDesc->m_hzp.free( arr );
543             }
544
545             /// Retire (deferred delete) node \p pNode guarded by \p hp hazard pointer
546             void retireNode( ContainerNode * pNode, details::HPGuard& hp, details::free_retired_ptr_func pFunc )
547             {
548                 assert( !pNode->m_bDeleted.load( CDS_ATOMIC::memory_order_relaxed ) );
549                 assert( pNode == hp );
550
551                 retireNode( pNode, pFunc );
552                 hp.clear();
553             }
554
555             /// Retire (deferred delete) node \p pNode. Do not use this function directly!
556             void retireNode( ContainerNode * pNode, details::free_retired_ptr_func pFunc )
557             {
558                 assert( !pNode->m_bDeleted.load( CDS_ATOMIC::memory_order_relaxed ) );
559
560                 pNode->m_bDeleted.store( true, CDS_ATOMIC::memory_order_release );
561                 pNode->m_bTrace.store( false, CDS_ATOMIC::memory_order_release );
562
563                 m_pDesc->m_arrRetired.push( pNode, pFunc );
564
565                 if ( m_pDesc->m_arrRetired.isFull() )
566                     m_gc.try_retire( this );
567             }
568
569             //@cond
570             void scan()
571             {
572                 m_gc.try_retire( this );
573             }
574             //@endcond
575
576         protected:
577             /// The procedure will try to remove redundant claimed references from link in deleted nodes that has been deleted by this thread
578             void cleanUpLocal()
579             {
580                 details::retired_vector::iterator itEnd = m_pDesc->m_arrRetired.end();
581                 for ( details::retired_vector::iterator it = m_pDesc->m_arrRetired.begin(); it != itEnd; ++it ) {
582                     details::retired_node& node = *it;
583                     ContainerNode * pNode = node.m_pNode.load(CDS_ATOMIC::memory_order_acquire);
584                     if ( pNode && !node.m_bDone.load(CDS_ATOMIC::memory_order_acquire) )
585                         pNode->cleanUp( this );
586                 }
587             }
588         };
589
590         /// Auto HPGuard.
591         class AutoHPGuard
592         {
593             //@cond
594             details::HPGuard&   m_hp  ; ///< hazard pointer
595             ThreadGC&           m_mgr ; ///< Thread GC.
596             //@endcond
597         public:
598             typedef details::HPGuard::hazard_ptr hazard_ptr ;  ///< Hazard pointer type
599
600         public:
601             /// Allocates HP guard from \p mgr
602             AutoHPGuard( ThreadGC& mgr )
603                 : m_hp( mgr.allocGuard() )
604                 , m_mgr( mgr )
605             {}
606
607             /// Allocates HP guard from \p mgr and protects the pointer \p p of type \p T
608             template <typename T>
609             AutoHPGuard( ThreadGC& mgr, T * p  )
610                 : m_hp( mgr.allocGuard() )
611                 , m_mgr( mgr )
612             {
613                 m_hp = p;
614             }
615
616             /// Frees HP guard
617             ~AutoHPGuard()
618             {
619                 m_mgr.freeGuard( m_hp );
620             }
621
622             /// Returns thread GC
623             ThreadGC&    getGC() const CDS_NOEXCEPT
624             {
625                 return m_mgr;
626             }
627
628             //@cond
629             template <typename T>
630             T * operator =( T * p ) CDS_NOEXCEPT
631             {
632                 return m_hp = p;
633             }
634             //@endcond
635
636             //@cond
637             hazard_ptr get() const CDS_NOEXCEPT
638             {
639                 return m_hp;
640             }
641             //@endcond
642
643             /// Clears the hazard pointer
644             void clear() CDS_NOEXCEPT
645             {
646                 m_hp.clear();
647             }
648         };
649
650         /// Auto-managed array of hazard pointers
651         /**
652             This class is wrapper around gc::hzp::details::HPArray class.
653         */
654         template <size_t Count>
655         class AutoHPArray: public details::HPArray<Count>
656         {
657             ThreadGC&    m_mgr    ;    ///< Thread GC
658
659         public:
660             /// Allocates array of HP guard from \p mgr
661             AutoHPArray( ThreadGC& mgr )
662                 : m_mgr( mgr )
663             {
664                 mgr.allocGuard( *this );
665             }
666
667             /// Frees array of HP guard
668             ~AutoHPArray()
669             {
670                 m_mgr.freeGuard( *this );
671             }
672
673             /// Returns thread GC
674             ThreadGC&    getGC() const
675             {
676                 return m_mgr;
677             }
678         };
679
680
681     }    // namespace hrc
682 }} // namespace cds::gc
683
684 #include <cds/gc/hrc/details/hrc_inline.h>
685
686 #if CDS_COMPILER == CDS_COMPILER_MSVC
687 #   pragma warning(pop)
688 #endif
689
690 #endif // #ifndef __CDS_GC_HRC_HRC_H