4555c48dee60063a4ffb222f1802d97301f8a159
[libcds.git] / cds / gc / details / hp.h
1 //$$CDS-header$$
2
3 #ifndef __CDS_GC_DETAILS_HP_H
4 #define __CDS_GC_DETAILS_HP_H
5
6 #include <cds/algo/atomic.h>
7 #include <cds/os/thread.h>
8 #include <cds/details/bounded_array.h>
9
10 #include <cds/gc/details/hp_type.h>
11 #include <cds/gc/details/hp_alloc.h>
12
13 #if CDS_COMPILER == CDS_COMPILER_MSVC
14 #   pragma warning(push)
15     // warning C4251: 'cds::gc::hp::GarbageCollector::m_pListHead' : class 'cds::cxx11_atomic::atomic<T>'
16     // needs to have dll-interface to be used by clients of class 'cds::gc::hp::GarbageCollector'
17 #   pragma warning(disable: 4251)
18 #endif
19
20 /*
21     Editions:
22         2007.12.24  khizmax Add statistics and CDS_GATHER_HAZARDPTR_STAT macro
23         2008.03.06  khizmax Refactoring: implementation of HazardPtrMgr is moved to hazardptr.cpp
24         2008.03.08  khizmax Remove HazardPtrMgr singleton. Now you must initialize/destroy HazardPtrMgr calling
25                             HazardPtrMgr::Construct / HazardPtrMgr::Destruct before use (usually in main() function).
26         2008.12.06  khizmax Refactoring. Changes class name, namespace hierarchy, all helper defs have been moved to details namespace
27         2010.01.27  khizmax Introducing memory order constraint
28 */
29
30 //@cond
31 namespace cds {
32     /// Different safe memory reclamation schemas (garbage collectors)
33     /** @ingroup cds_garbage_collector
34
35         This namespace specifies different safe memory reclamation (SMR) algorithms.
36         See \ref cds_garbage_collector "Garbage collectors"
37     */
38     namespace gc {
39
40     /// Michael's Hazard Pointers reclamation schema
41     /**
42     \par Sources:
43         - [2002] Maged M.Michael "Safe memory reclamation for dynamic lock-freeobjects using atomic reads and writes"
44         - [2003] Maged M.Michael "Hazard Pointers: Safe memory reclamation for lock-free objects"
45         - [2004] Andrei Alexandrescy, Maged Michael "Lock-free Data Structures with Hazard Pointers"
46
47         The \p cds::gc::hp namespace and its members are internal representation of Hazard Pointer GC and should not be used directly.
48         Use \p cds::gc::HP class in your code.
49
50         Hazard Pointer garbage collector is a singleton. The main user-level part of Hazard Pointer schema is
51         GC class and its nested classes. Before use any HP-related class you must initialize HP garbage collector
52         by contructing \p cds::gc::HP object in beginning of your \p main().
53         See \p cds::gc::HP class for explanation.
54     */
55     namespace hp {
56
57         // forwards
58         class GarbageCollector;
59         class ThreadGC;
60
61         namespace details {
62
63             /// Retired pointer
64             typedef cds::gc::details::retired_ptr   retired_ptr;
65
66             /// Array of retired pointers
67             /**
68                 The vector of retired pointer ready to delete.
69
70                 The Hazard Pointer schema is build on thread-static arrays. For each HP-enabled thread the HP manager allocates
71                 array of retired pointers. The array belongs to the thread: owner thread writes to the array, other threads
72                 just read it.
73             */
74             class retired_vector {
75                 /// Underlying vector implementation
76                 typedef cds::details::bounded_array<retired_ptr>    retired_vector_impl;
77
78                 retired_vector_impl m_arr   ;   ///< the array of retired pointers
79                 size_t              m_nSize ;   ///< Current size of \p m_arr
80
81             public:
82                 /// Iterator
83                 typedef retired_vector_impl::iterator  iterator;
84
85                 /// Constructor
86                 retired_vector( const cds::gc::hp::GarbageCollector& HzpMgr ); // inline
87                 ~retired_vector()
88                 {}
89
90                 /// Vector capacity.
91                 /**
92                     The capacity is constant for any thread. It is defined by cds::gc::hp::GarbageCollector.
93                 */
94                 size_t capacity() const CDS_NOEXCEPT
95                 {
96                     return m_arr.capacity();
97                 }
98
99                 /// Current vector size (count of retired pointers in the vector)
100                 size_t size() const CDS_NOEXCEPT
101                 {
102                     return m_nSize;
103                 }
104
105                 /// Set vector size. Uses internally
106                 void size( size_t nSize )
107                 {
108                     assert( nSize <= capacity() );
109                     m_nSize = nSize;
110                 }
111
112                 /// Pushes retired pointer to the vector
113                 void push( retired_ptr const& p )
114                 {
115                     assert( m_nSize < capacity() );
116                     m_arr[ m_nSize ] = p;
117                     ++m_nSize;
118                 }
119
120                 /// Checks if the vector is full (size() == capacity() )
121                 bool isFull() const CDS_NOEXCEPT
122                 {
123                     return m_nSize >= capacity();
124                 }
125
126                 /// Begin iterator
127                 iterator    begin() CDS_NOEXCEPT
128                 {
129                     return m_arr.begin();
130                 }
131
132                 /// End iterator
133                 iterator    end() CDS_NOEXCEPT
134                 {
135                     return m_arr.begin() +  m_nSize;
136                 }
137
138                 /// Clears the vector. After clearing, size() == 0
139                 void clear() CDS_NOEXCEPT
140                 {
141                     m_nSize = 0;
142                 }
143             };
144
145             /// Hazard pointer record of the thread
146             /**
147                 The structure of type "single writer - multiple reader": only the owner thread may write to this structure
148                 other threads have read-only access.
149             */
150             struct hp_record {
151                 hp_allocator<>    m_hzp; ///< array of hazard pointers. Implicit \ref CDS_DEFAULT_ALLOCATOR dependency
152                 retired_vector    m_arrRetired ; ///< Retired pointer array
153
154                 /// Ctor
155                 hp_record( const cds::gc::hp::GarbageCollector& HzpMgr );    // inline
156                 ~hp_record()
157                 {}
158
159                 /// Clears all hazard pointers
160                 void clear()
161                 {
162                     m_hzp.clear();
163                 }
164             };
165         }    // namespace details
166
167         /// GarbageCollector::Scan phase strategy
168         /**
169             See GarbageCollector::Scan for explanation
170         */
171         enum scan_type {
172             classic,    ///< classic scan as described in Michael's works (see GarbageCollector::classic_scan)
173             inplace     ///< inplace scan without allocation (see GarbageCollector::inplace_scan)
174         };
175
176         /// Hazard Pointer singleton
177         /**
178             Safe memory reclamation schema by Michael "Hazard Pointers"
179
180         \par Sources:
181             \li [2002] Maged M.Michael "Safe memory reclamation for dynamic lock-freeobjects using atomic reads and writes"
182             \li [2003] Maged M.Michael "Hazard Pointers: Safe memory reclamation for lock-free objects"
183             \li [2004] Andrei Alexandrescy, Maged Michael "Lock-free Data Structures with Hazard Pointers"
184
185         */
186         class CDS_EXPORT_API GarbageCollector
187         {
188         public:
189             typedef cds::atomicity::event_counter  event_counter   ;   ///< event counter type
190
191             /// Internal GC statistics
192             struct InternalState {
193                 size_t              nHPCount                ;   ///< HP count per thread (const)
194                 size_t              nMaxThreadCount         ;   ///< Max thread count (const)
195                 size_t              nMaxRetiredPtrCount     ;   ///< Max retired pointer count per thread (const)
196                 size_t              nHPRecSize              ;   ///< Size of HP record, bytes (const)
197
198                 size_t              nHPRecAllocated         ;   ///< Count of HP record allocations
199                 size_t              nHPRecUsed              ;   ///< Count of HP record used
200                 size_t              nTotalRetiredPtrCount   ;   ///< Current total count of retired pointers
201                 size_t              nRetiredPtrInFreeHPRecs ;   ///< Count of retired pointer in free (unused) HP records
202
203                 event_counter::value_type   evcAllocHPRec   ;   ///< Count of \p hp_record allocations
204                 event_counter::value_type   evcRetireHPRec  ;   ///< Count of \p hp_record retire events
205                 event_counter::value_type   evcAllocNewHPRec;   ///< Count of new \p hp_record allocations from heap
206                 event_counter::value_type   evcDeleteHPRec  ;   ///< Count of \p hp_record deletions
207
208                 event_counter::value_type   evcScanCall     ;   ///< Count of Scan calling
209                 event_counter::value_type   evcHelpScanCall ;   ///< Count of HelpScan calling
210                 event_counter::value_type   evcScanFromHelpScan;///< Count of Scan calls from HelpScan
211
212                 event_counter::value_type   evcDeletedNode  ;   ///< Count of deleting of retired objects
213                 event_counter::value_type   evcDeferredNode ;   ///< Count of objects that cannot be deleted in Scan phase because of a hazard_pointer guards it
214             };
215
216             /// No GarbageCollector object is created
217             class not_initialized : public std::runtime_error
218             {
219             public:
220                 //@cond
221                 not_initialized()
222                     : std::runtime_error( "Global Hazard Pointer GarbageCollector is not initialized" )
223                 {}
224                 //@endcond
225             };
226
227             /// Not enough required Hazard Pointer count
228             class too_many_hazard_ptr : public std::length_error
229             {
230             public:
231                 //@cond
232                 too_many_hazard_ptr()
233                     : std::length_error( "Not enough required Hazard Pointer count" )
234                 {}
235                 //@endcond
236             };
237
238         private:
239             /// Internal GC statistics
240             struct Statistics {
241                 event_counter  m_AllocHPRec            ;    ///< Count of \p hp_record allocations
242                 event_counter  m_RetireHPRec            ;    ///< Count of \p hp_record retire events
243                 event_counter  m_AllocNewHPRec            ;    ///< Count of new \p hp_record allocations from heap
244                 event_counter  m_DeleteHPRec            ;    ///< Count of \p hp_record deletions
245
246                 event_counter  m_ScanCallCount            ;    ///< Count of Scan calling
247                 event_counter  m_HelpScanCallCount        ;    ///< Count of HelpScan calling
248                 event_counter  m_CallScanFromHelpScan    ;    ///< Count of Scan calls from HelpScan
249
250                 event_counter  m_DeletedNode            ;    ///< Count of retired objects deleting
251                 event_counter  m_DeferredNode            ;    ///< Count of objects that cannot be deleted in Scan phase because of a hazard_pointer guards it
252             };
253
254             /// Internal list of cds::gc::hp::details::hp_record
255             struct hplist_node : public details::hp_record
256             {
257                 hplist_node *                    m_pNextNode; ///< next hazard ptr record in list
258                 atomics::atomic<OS::ThreadId>    m_idOwner;   ///< Owner thread id; 0 - the record is free (not owned)
259                 atomics::atomic<bool>            m_bFree;     ///< true if record if free (not owned)
260
261                 //@cond
262                 hplist_node( const GarbageCollector& HzpMgr )
263                     : hp_record( HzpMgr ),
264                     m_pNextNode( nullptr ),
265                     m_idOwner( OS::c_NullThreadId ),
266                     m_bFree( true )
267                 {}
268
269                 ~hplist_node()
270                 {
271                     assert( m_idOwner.load( atomics::memory_order_relaxed ) == OS::c_NullThreadId );
272                     assert( m_bFree.load(atomics::memory_order_relaxed) );
273                 }
274                                 //@endcond
275             };
276
277             atomics::atomic<hplist_node *>   m_pListHead  ;  ///< Head of GC list
278
279             static GarbageCollector *    m_pHZPManager  ;   ///< GC instance pointer
280
281             Statistics              m_Stat              ;   ///< Internal statistics
282             bool                    m_bStatEnabled      ;   ///< true - statistics enabled
283
284             const size_t            m_nHazardPointerCount   ;   ///< max count of thread's hazard pointer
285             const size_t            m_nMaxThreadCount       ;   ///< max count of thread
286             const size_t            m_nMaxRetiredPtrCount   ;   ///< max count of retired ptr per thread
287             scan_type               m_nScanType             ;   ///< scan type (see \ref scan_type enum)
288
289
290         private:
291             /// Ctor
292             GarbageCollector(
293                 size_t nHazardPtrCount = 0,         ///< Hazard pointer count per thread
294                 size_t nMaxThreadCount = 0,         ///< Max count of thread
295                 size_t nMaxRetiredPtrCount = 0,     ///< Capacity of the array of retired objects
296                 scan_type nScanType = inplace       ///< Scan type (see \ref scan_type enum)
297             );
298
299             /// Dtor
300             ~GarbageCollector();
301
302             /// Allocate new HP record
303             hplist_node * NewHPRec();
304
305             /// Permanently deletes HPrecord \p pNode
306             /**
307                 Caveat: for performance reason this function is defined as inline and cannot be called directly
308             */
309             void                DeleteHPRec( hplist_node * pNode );
310
311             void detachAllThread();
312
313         public:
314             /// Creates GarbageCollector singleton
315             /**
316                 GC is the singleton. If GC instance is not exist then the function creates the instance.
317                 Otherwise it does nothing.
318
319                 The Michael's HP reclamation schema depends of three parameters:
320
321                 \p nHazardPtrCount - HP pointer count per thread. Usually it is small number (2-4) depending from
322                                      the data structure algorithms. By default, if \p nHazardPtrCount = 0,
323                                      the function uses maximum of HP count for CDS library.
324
325                 \p nMaxThreadCount - max count of thread with using HP GC in your application. Default is 100.
326
327                 \p nMaxRetiredPtrCount - capacity of array of retired pointers for each thread. Must be greater than
328                                     \p nHazardPtrCount * \p nMaxThreadCount.
329                                     Default is 2 * \p nHazardPtrCount * \p nMaxThreadCount.
330             */
331             static void    CDS_STDCALL Construct(
332                 size_t nHazardPtrCount = 0,     ///< Hazard pointer count per thread
333                 size_t nMaxThreadCount = 0,     ///< Max count of simultaneous working thread in your application
334                 size_t nMaxRetiredPtrCount = 0, ///< Capacity of the array of retired objects for the thread
335                 scan_type nScanType = inplace   ///< Scan type (see \ref scan_type enum)
336             );
337
338             /// Destroys global instance of GarbageCollector
339             /**
340                 The parameter \p bDetachAll should be used carefully: if its value is \p true,
341                 then the destroying GC automatically detaches all attached threads. This feature
342                 can be useful when you have no control over the thread termination, for example,
343                 when \p libcds is injected into existing external thread.
344             */
345             static void CDS_STDCALL Destruct(
346                 bool bDetachAll = false     ///< Detach all threads
347             );
348
349             /// Returns pointer to GarbageCollector instance
350             static GarbageCollector&   instance()
351             {
352                 if ( !m_pHZPManager )
353                     throw not_initialized();
354                 return *m_pHZPManager;
355             }
356
357             /// Checks if global GC object is constructed and may be used
358             static bool isUsed() CDS_NOEXCEPT
359             {
360                 return m_pHZPManager != nullptr;
361             }
362
363             /// Returns max Hazard Pointer count defined in construction time
364             size_t            getHazardPointerCount() const CDS_NOEXCEPT
365             {
366                 return m_nHazardPointerCount;
367             }
368
369             /// Returns max thread count defined in construction time
370             size_t            getMaxThreadCount() const CDS_NOEXCEPT
371             {
372                 return m_nMaxThreadCount;
373             }
374
375             /// Returns max size of retired objects array. It is defined in construction time
376             size_t            getMaxRetiredPtrCount() const CDS_NOEXCEPT
377             {
378                 return m_nMaxRetiredPtrCount;
379             }
380
381             // Internal statistics
382
383             /// Get internal statistics
384             InternalState& getInternalState(InternalState& stat) const;
385
386             /// Checks if internal statistics enabled
387             bool              isStatisticsEnabled() const { return m_bStatEnabled; }
388
389             /// Enables/disables internal statistics
390             bool              enableStatistics( bool bEnable )
391             {
392                 bool bEnabled = m_bStatEnabled;
393                 m_bStatEnabled = bEnable;
394                 return bEnabled;
395             }
396
397             /// Checks that required hazard pointer count \p nRequiredCount is less or equal then max hazard pointer count
398             /**
399                 If \p nRequiredCount > getHazardPointerCount() then the exception \p too_many_hazard_ptr is thrown
400             */
401             static void checkHPCount( unsigned int nRequiredCount )
402             {
403                 if ( instance().getHazardPointerCount() < nRequiredCount )
404                     throw too_many_hazard_ptr();
405             }
406
407             /// Get current scan strategy
408             scan_type getScanType() const
409             {
410                 return m_nScanType;
411             }
412
413             /// Set current scan strategy
414             /** @anchor hzp_gc_setScanType
415                 Scan strategy changing is allowed on the fly.
416             */
417             void setScanType(
418                 scan_type nScanType     ///< new scan strategy
419             )
420             {
421                 m_nScanType = nScanType;
422             }
423
424         public:    // Internals for threads
425
426             /// Allocates Hazard Pointer GC record. For internal use only
427             details::hp_record * alloc_hp_record();
428
429             /// Free HP record. For internal use only
430             void free_hp_record( details::hp_record * pRec );
431
432             /// The main garbage collecting function
433             /**
434                 This function is called internally by ThreadGC object when upper bound of thread's list of reclaimed pointers
435                 is reached.
436
437                 There are the following scan algorithm:
438                 - \ref hzp_gc_classic_scan "classic_scan" allocates memory for internal use
439                 - \ref hzp_gc_inplace_scan "inplace_scan" does not allocate any memory
440
441                 Use \ref hzp_gc_setScanType "setScanType" member function to setup appropriate scan algorithm.
442             */
443             void Scan( details::hp_record * pRec )
444             {
445                 switch ( m_nScanType ) {
446                     case inplace:
447                         inplace_scan( pRec );
448                         break;
449                     default:
450                         assert(false)   ;   // Forgotten something?..
451                     case classic:
452                         classic_scan( pRec );
453                         break;
454                 }
455             }
456
457             /// Helper scan routine
458             /**
459                 The function guarantees that every node that is eligible for reuse is eventually freed, barring
460                 thread failures. To do so, after executing Scan, a thread executes a HelpScan,
461                 where it checks every HP record. If an HP record is inactive, the thread moves all "lost" reclaimed pointers
462                 to thread's list of reclaimed pointers.
463
464                 The function is called internally by Scan.
465             */
466             void HelpScan( details::hp_record * pThis );
467
468         protected:
469             /// Classic scan algorithm
470             /** @anchor hzp_gc_classic_scan
471                 Classical scan algorithm as described in Michael's paper.
472
473                 A scan includes four stages. The first stage involves scanning the array HP for non-null values.
474                 Whenever a non-null value is encountered, it is inserted in a local list of currently protected pointer.
475                 Only stage 1 accesses shared variables. The following stages operate only on private variables.
476
477                 The second stage of a scan involves sorting local list of protected pointers to allow
478                 binary search in the third stage.
479
480                 The third stage of a scan involves checking each reclaimed node
481                 against the pointers in local list of protected pointers. If the binary search yields
482                 no match, the node is freed. Otherwise, it cannot be deleted now and must kept in thread's list
483                 of reclaimed pointers.
484
485                 The forth stage prepares new thread's private list of reclaimed pointers
486                 that could not be freed during the current scan, where they remain until the next scan.
487
488                 This algorithm allocates memory for internal HP array.
489
490                 This function is called internally by ThreadGC object when upper bound of thread's list of reclaimed pointers
491                 is reached.
492             */
493             void classic_scan( details::hp_record * pRec );
494
495             /// In-place scan algorithm
496             /** @anchor hzp_gc_inplace_scan
497                 Unlike the \ref hzp_gc_classic_scan "classic_scan" algorithm, \p inplace_scan does not allocate any memory.
498                 All operations are performed in-place.
499             */
500             void inplace_scan( details::hp_record * pRec );
501         };
502
503         /// Thread's hazard pointer manager
504         /**
505             To use Hazard Pointer reclamation schema each thread object must be linked with the object of ThreadGC class
506             that interacts with GarbageCollector global object. The linkage is performed by calling \ref cds_threading "cds::threading::Manager::attachThread()"
507             on the start of each thread that uses HP GC. Before terminating the thread linked to HP GC it is necessary to call
508             \ref cds_threading "cds::threading::Manager::detachThread()".
509         */
510         class ThreadGC
511         {
512             GarbageCollector&    m_HzpManager; ///< Hazard Pointer GC singleton
513             details::hp_record * m_pHzpRec;    ///< Pointer to thread's HZP record
514
515         public:
516             /// Default constructor
517             ThreadGC()
518                 : m_HzpManager( GarbageCollector::instance() ),
519                 m_pHzpRec( nullptr )
520             {}
521
522             /// The object is not copy-constructible
523             ThreadGC( ThreadGC const& ) = delete;
524
525             ~ThreadGC()
526             {
527                 fini();
528             }
529
530             /// Checks if thread GC is initialized
531             bool    isInitialized() const   { return m_pHzpRec != nullptr; }
532
533             /// Initialization. Repeat call is available
534             void init()
535             {
536                 if ( !m_pHzpRec )
537                     m_pHzpRec = m_HzpManager.alloc_hp_record();
538             }
539
540             /// Finalization. Repeat call is available
541             void fini()
542             {
543                 if ( m_pHzpRec ) {
544                     details::hp_record * pRec = m_pHzpRec;
545                     m_pHzpRec = nullptr;
546                     m_HzpManager.free_hp_record( pRec );
547                 }
548             }
549
550             /// Initializes HP guard \p guard
551             details::hp_guard& allocGuard()
552             {
553                 assert( m_pHzpRec );
554                 return m_pHzpRec->m_hzp.alloc();
555             }
556
557             /// Frees HP guard \p guard
558             void freeGuard( details::hp_guard& guard )
559             {
560                 assert( m_pHzpRec );
561                 m_pHzpRec->m_hzp.free( guard );
562             }
563
564             /// Initializes HP guard array \p arr
565             template <size_t Count>
566             void allocGuard( details::hp_array<Count>& arr )
567             {
568                 assert( m_pHzpRec );
569                 m_pHzpRec->m_hzp.alloc( arr );
570             }
571
572             /// Frees HP guard array \p arr
573             template <size_t Count>
574             void freeGuard( details::hp_array<Count>& arr )
575             {
576                 assert( m_pHzpRec );
577                 m_pHzpRec->m_hzp.free( arr );
578             }
579
580             /// Places retired pointer \p and its deleter \p pFunc into thread's array of retired pointer for deferred reclamation
581             template <typename T>
582             void retirePtr( T * p, void (* pFunc)(T *) )
583             {
584                 /*
585                 union {
586                     T * p;
587                     hazard_pointer hp;
588                 } cast_ptr;
589                 cast_ptr.p = p;
590
591                 union{
592                     void( *pFunc )(T *);
593                     free_retired_ptr_func hpFunc;
594                 } cast_func;
595                 cast_func.pFunc = pFunc;
596
597                 retirePtr( details::retired_ptr( cast_ptr.hp, cast_func.hpFunc ) );
598                 */
599                 retirePtr( details::retired_ptr( reinterpret_cast<void *>( p ), reinterpret_cast<free_retired_ptr_func>( pFunc )));
600             }
601
602             /// Places retired pointer \p into thread's array of retired pointer for deferred reclamation
603             void retirePtr( details::retired_ptr const& p )
604             {
605                 m_pHzpRec->m_arrRetired.push( p );
606
607                 if ( m_pHzpRec->m_arrRetired.isFull() ) {
608                     // Max of retired pointer count is reached. Do scan
609                     scan();
610                 }
611             }
612
613             /// Run retiring scan cycle
614             void scan()
615             {
616                 m_HzpManager.Scan( m_pHzpRec );
617                 m_HzpManager.HelpScan( m_pHzpRec );
618             }
619         };
620
621         /// Auto hp_guard.
622         /**
623             This class encapsulates Hazard Pointer guard to protect a pointer against deletion .
624             It allocates one HP from thread's HP array in constructor and free the hazard pointer allocated
625             in destructor.
626         */
627         class guard
628         {
629             details::hp_guard&  m_hp    ; ///< Hazard pointer guarded
630
631         public:
632             typedef details::hp_guard::hazard_ptr hazard_ptr ;  ///< Hazard pointer type
633
634         public:
635             /// Allocates HP guard
636             guard(); // inline in hp_impl.h
637
638             /// Allocates HP guard from \p gc and protects the pointer \p p of type \p T
639             template <typename T>
640             explicit guard( T * p ); // inline in hp_impl.h
641
642             /// Frees HP guard. The pointer guarded may be deleted after this.
643             ~guard(); // inline in hp_impl.h
644
645             /// Protects the pointer \p p against reclamation (guards the pointer).
646             template <typename T>
647             T * operator =( T * p )
648             {
649                 return m_hp = p;
650             }
651
652             //@cond
653             std::nullptr_t operator =(std::nullptr_t)
654             {
655                 return m_hp = nullptr;
656             }
657             //@endcond
658
659             /// Get raw guarded pointer
660             hazard_ptr get() const
661             {
662                 return m_hp;
663             }
664         };
665
666         /// Auto-managed array of hazard pointers
667         /**
668             This class is wrapper around cds::gc::hp::details::hp_array class.
669             \p Count is the size of HP array
670         */
671         template <size_t Count>
672         class array : public details::hp_array<Count>
673         {
674         public:
675             /// Rebind array for other size \p COUNT2
676             template <size_t Count2>
677             struct rebind {
678                 typedef array<Count2>  other;   ///< rebinding result
679             };
680
681         public:
682             /// Allocates array of HP guard
683             array(); // inline in hp_impl.h
684
685             /// Frees array of HP guard
686             ~array(); //inline in hp_impl.h
687         };
688
689     }   // namespace hp
690 }}  // namespace cds::gc
691 //@endcond
692
693 //@cond
694 // Inlines
695 namespace cds {
696     namespace gc { namespace hp { namespace details {
697
698         inline retired_vector::retired_vector( const cds::gc::hp::GarbageCollector& HzpMgr )
699             : m_arr( HzpMgr.getMaxRetiredPtrCount() ),
700             m_nSize(0)
701         {}
702
703         inline hp_record::hp_record( const cds::gc::hp::GarbageCollector& HzpMgr )
704             : m_hzp( HzpMgr.getHazardPointerCount() ),
705             m_arrRetired( HzpMgr )
706         {}
707
708     }}} // namespace gc::hp::details
709 } // namespace cds
710 //@endcond
711
712
713 #if CDS_COMPILER == CDS_COMPILER_MSVC
714 #   pragma warning(pop)
715 #endif
716
717 #endif  // #ifndef __CDS_GC_DETAILS_HP_H