Improving gc:HP and gc::DHP guards
[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/cxx11_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             CDS_DECLARE_EXCEPTION( HZPManagerEmpty, "Global Hazard Pointer GarbageCollector is NULL" );
218
219             /// Not enough required Hazard Pointer count
220             CDS_DECLARE_EXCEPTION( HZPTooMany, "Not enough required Hazard Pointer count" );
221
222         private:
223             /// Internal GC statistics
224             struct Statistics {
225                 event_counter  m_AllocHPRec            ;    ///< Count of \p hp_record allocations
226                 event_counter  m_RetireHPRec            ;    ///< Count of \p hp_record retire events
227                 event_counter  m_AllocNewHPRec            ;    ///< Count of new \p hp_record allocations from heap
228                 event_counter  m_DeleteHPRec            ;    ///< Count of \p hp_record deletions
229
230                 event_counter  m_ScanCallCount            ;    ///< Count of Scan calling
231                 event_counter  m_HelpScanCallCount        ;    ///< Count of HelpScan calling
232                 event_counter  m_CallScanFromHelpScan    ;    ///< Count of Scan calls from HelpScan
233
234                 event_counter  m_DeletedNode            ;    ///< Count of retired objects deleting
235                 event_counter  m_DeferredNode            ;    ///< Count of objects that cannot be deleted in Scan phase because of a hazard_pointer guards it
236             };
237
238             /// Internal list of cds::gc::hp::details::hp_record
239             struct hplist_node : public details::hp_record
240             {
241                 hplist_node *                    m_pNextNode; ///< next hazard ptr record in list
242                 atomics::atomic<OS::ThreadId>    m_idOwner;   ///< Owner thread id; 0 - the record is free (not owned)
243                 atomics::atomic<bool>            m_bFree;     ///< true if record if free (not owned)
244
245                 hplist_node( const GarbageCollector& HzpMgr )
246                     : hp_record( HzpMgr ),
247                     m_pNextNode( nullptr ),
248                     m_idOwner( OS::c_NullThreadId ),
249                     m_bFree( true )
250                 {}
251
252                 ~hplist_node()
253                 {
254                     assert( m_idOwner.load( atomics::memory_order_relaxed ) == OS::c_NullThreadId );
255                     assert( m_bFree.load(atomics::memory_order_relaxed) );
256                 }
257             };
258
259             atomics::atomic<hplist_node *>   m_pListHead  ;  ///< Head of GC list
260
261             static GarbageCollector *    m_pHZPManager  ;   ///< GC instance pointer
262
263             Statistics              m_Stat              ;   ///< Internal statistics
264             bool                    m_bStatEnabled      ;   ///< true - statistics enabled
265
266             const size_t            m_nHazardPointerCount   ;   ///< max count of thread's hazard pointer
267             const size_t            m_nMaxThreadCount       ;   ///< max count of thread
268             const size_t            m_nMaxRetiredPtrCount   ;   ///< max count of retired ptr per thread
269             scan_type               m_nScanType             ;   ///< scan type (see \ref scan_type enum)
270
271
272         private:
273             /// Ctor
274             GarbageCollector(
275                 size_t nHazardPtrCount = 0,         ///< Hazard pointer count per thread
276                 size_t nMaxThreadCount = 0,         ///< Max count of thread
277                 size_t nMaxRetiredPtrCount = 0,     ///< Capacity of the array of retired objects
278                 scan_type nScanType = inplace       ///< Scan type (see \ref scan_type enum)
279             );
280
281             /// Dtor
282             ~GarbageCollector();
283
284             /// Allocate new HP record
285             hplist_node * NewHPRec();
286
287             /// Permanently deletes HPrecord \p pNode
288             /**
289                 Caveat: for performance reason this function is defined as inline and cannot be called directly
290             */
291             void                DeleteHPRec( hplist_node * pNode );
292
293             void detachAllThread();
294
295         public:
296             /// Creates GarbageCollector singleton
297             /**
298                 GC is the singleton. If GC instance is not exist then the function creates the instance.
299                 Otherwise it does nothing.
300
301                 The Michael's HP reclamation schema depends of three parameters:
302
303                 \p nHazardPtrCount - HP pointer count per thread. Usually it is small number (2-4) depending from
304                                      the data structure algorithms. By default, if \p nHazardPtrCount = 0,
305                                      the function uses maximum of HP count for CDS library.
306
307                 \p nMaxThreadCount - max count of thread with using HP GC in your application. Default is 100.
308
309                 \p nMaxRetiredPtrCount - capacity of array of retired pointers for each thread. Must be greater than
310                                     \p nHazardPtrCount * \p nMaxThreadCount.
311                                     Default is 2 * \p nHazardPtrCount * \p nMaxThreadCount.
312             */
313             static void    CDS_STDCALL Construct(
314                 size_t nHazardPtrCount = 0,     ///< Hazard pointer count per thread
315                 size_t nMaxThreadCount = 0,     ///< Max count of simultaneous working thread in your application
316                 size_t nMaxRetiredPtrCount = 0, ///< Capacity of the array of retired objects for the thread
317                 scan_type nScanType = inplace   ///< Scan type (see \ref scan_type enum)
318             );
319
320             /// Destroys global instance of GarbageCollector
321             /**
322                 The parameter \p bDetachAll should be used carefully: if its value is \p true,
323                 then the destroying GC automatically detaches all attached threads. This feature
324                 can be useful when you have no control over the thread termination, for example,
325                 when \p libcds is injected into existing external thread.
326             */
327             static void CDS_STDCALL Destruct(
328                 bool bDetachAll = false     ///< Detach all threads
329             );
330
331             /// Returns pointer to GarbageCollector instance
332             static GarbageCollector&   instance()
333             {
334                 if ( !m_pHZPManager )
335                     throw HZPManagerEmpty();
336                 return *m_pHZPManager;
337             }
338
339             /// Checks if global GC object is constructed and may be used
340             static bool isUsed() CDS_NOEXCEPT
341             {
342                 return m_pHZPManager != nullptr;
343             }
344
345             /// Returns max Hazard Pointer count defined in construction time
346             size_t            getHazardPointerCount() const CDS_NOEXCEPT
347             { 
348                 return m_nHazardPointerCount; 
349             }
350
351             /// Returns max thread count defined in construction time
352             size_t            getMaxThreadCount() const CDS_NOEXCEPT
353             { 
354                 return m_nMaxThreadCount; 
355             }
356
357             /// Returns max size of retired objects array. It is defined in construction time
358             size_t            getMaxRetiredPtrCount() const CDS_NOEXCEPT
359             { 
360                 return m_nMaxRetiredPtrCount; 
361             }
362
363             // Internal statistics
364
365             /// Get internal statistics
366             InternalState& getInternalState(InternalState& stat) const;
367
368             /// Checks if internal statistics enabled
369             bool              isStatisticsEnabled() const { return m_bStatEnabled; }
370
371             /// Enables/disables internal statistics
372             bool              enableStatistics( bool bEnable )
373             {
374                 bool bEnabled = m_bStatEnabled;
375                 m_bStatEnabled = bEnable;
376                 return bEnabled;
377             }
378
379             /// Checks that required hazard pointer count \p nRequiredCount is less or equal then max hazard pointer count
380             /**
381                 If \p nRequiredCount > getHazardPointerCount() then the exception HZPTooMany is thrown
382             */
383             static void checkHPCount( unsigned int nRequiredCount )
384             {
385                 if ( instance().getHazardPointerCount() < nRequiredCount )
386                     throw HZPTooMany();
387             }
388
389             /// Get current scan strategy
390             scan_type getScanType() const
391             {
392                 return m_nScanType;
393             }
394
395             /// Set current scan strategy
396             /** @anchor hzp_gc_setScanType
397                 Scan strategy changing is allowed on the fly.
398             */
399             void setScanType(
400                 scan_type nScanType     ///< new scan strategy
401             )
402             {
403                 m_nScanType = nScanType;
404             }
405
406         public:    // Internals for threads
407
408             /// Allocates Hazard Pointer GC record. For internal use only
409             details::hp_record * alloc_hp_record();
410
411             /// Free HP record. For internal use only
412             void free_hp_record( details::hp_record * pRec );
413
414             /// The main garbage collecting function
415             /**
416                 This function is called internally by ThreadGC object when upper bound of thread's list of reclaimed pointers
417                 is reached.
418
419                 There are the following scan algorithm:
420                 - \ref hzp_gc_classic_scan "classic_scan" allocates memory for internal use
421                 - \ref hzp_gc_inplace_scan "inplace_scan" does not allocate any memory
422
423                 Use \ref hzp_gc_setScanType "setScanType" member function to setup appropriate scan algorithm.
424             */
425             void Scan( details::hp_record * pRec )
426             {
427                 switch ( m_nScanType ) {
428                     case inplace:
429                         inplace_scan( pRec );
430                         break;
431                     default:
432                         assert(false)   ;   // Forgotten something?..
433                     case classic:
434                         classic_scan( pRec );
435                         break;
436                 }
437             }
438
439             /// Helper scan routine
440             /**
441                 The function guarantees that every node that is eligible for reuse is eventually freed, barring
442                 thread failures. To do so, after executing Scan, a thread executes a HelpScan,
443                 where it checks every HP record. If an HP record is inactive, the thread moves all "lost" reclaimed pointers
444                 to thread's list of reclaimed pointers.
445
446                 The function is called internally by Scan.
447             */
448             void HelpScan( details::hp_record * pThis );
449
450         protected:
451             /// Classic scan algorithm
452             /** @anchor hzp_gc_classic_scan
453                 Classical scan algorithm as described in Michael's paper.
454
455                 A scan includes four stages. The first stage involves scanning the array HP for non-null values.
456                 Whenever a non-null value is encountered, it is inserted in a local list of currently protected pointer.
457                 Only stage 1 accesses shared variables. The following stages operate only on private variables.
458
459                 The second stage of a scan involves sorting local list of protected pointers to allow
460                 binary search in the third stage.
461
462                 The third stage of a scan involves checking each reclaimed node
463                 against the pointers in local list of protected pointers. If the binary search yields
464                 no match, the node is freed. Otherwise, it cannot be deleted now and must kept in thread's list
465                 of reclaimed pointers.
466
467                 The forth stage prepares new thread's private list of reclaimed pointers
468                 that could not be freed during the current scan, where they remain until the next scan.
469
470                 This algorithm allocates memory for internal HP array.
471
472                 This function is called internally by ThreadGC object when upper bound of thread's list of reclaimed pointers
473                 is reached.
474             */
475             void classic_scan( details::hp_record * pRec );
476
477             /// In-place scan algorithm
478             /** @anchor hzp_gc_inplace_scan
479                 Unlike the \ref hzp_gc_classic_scan "classic_scan" algorithm, \p inplace_scan does not allocate any memory.
480                 All operations are performed in-place.
481             */
482             void inplace_scan( details::hp_record * pRec );
483         };
484
485         /// Thread's hazard pointer manager
486         /**
487             To use Hazard Pointer reclamation schema each thread object must be linked with the object of ThreadGC class
488             that interacts with GarbageCollector global object. The linkage is performed by calling \ref cds_threading "cds::threading::Manager::attachThread()"
489             on the start of each thread that uses HP GC. Before terminating the thread linked to HP GC it is necessary to call
490             \ref cds_threading "cds::threading::Manager::detachThread()".
491         */
492         class ThreadGC
493         {
494             GarbageCollector&    m_HzpManager; ///< Hazard Pointer GC singleton
495             details::hp_record * m_pHzpRec;    ///< Pointer to thread's HZP record
496
497         public:
498             /// Default constructor
499             ThreadGC()
500                 : m_HzpManager( GarbageCollector::instance() ),
501                 m_pHzpRec( nullptr )
502             {}
503
504             /// The object is not copy-constructible
505             ThreadGC( ThreadGC const& ) = delete;
506
507             ~ThreadGC()
508             {
509                 fini();
510             }
511
512             /// Checks if thread GC is initialized
513             bool    isInitialized() const   { return m_pHzpRec != nullptr; }
514
515             /// Initialization. Repeat call is available
516             void init()
517             {
518                 if ( !m_pHzpRec )
519                     m_pHzpRec = m_HzpManager.alloc_hp_record();
520             }
521
522             /// Finalization. Repeat call is available
523             void fini()
524             {
525                 if ( m_pHzpRec ) {
526                     details::hp_record * pRec = m_pHzpRec;
527                     m_pHzpRec = nullptr;
528                     m_HzpManager.free_hp_record( pRec );
529                 }
530             }
531
532             /// Initializes HP guard \p guard
533             details::hp_guard& allocGuard()
534             {
535                 assert( m_pHzpRec );
536                 return m_pHzpRec->m_hzp.alloc();
537             }
538
539             /// Frees HP guard \p guard
540             void freeGuard( details::hp_guard& guard )
541             {
542                 assert( m_pHzpRec );
543                 m_pHzpRec->m_hzp.free( guard );
544             }
545
546             /// Initializes HP guard array \p arr
547             template <size_t Count>
548             void allocGuard( details::hp_array<Count>& arr )
549             {
550                 assert( m_pHzpRec );
551                 m_pHzpRec->m_hzp.alloc( arr );
552             }
553
554             /// Frees HP guard array \p arr
555             template <size_t Count>
556             void freeGuard( details::hp_array<Count>& arr )
557             {
558                 assert( m_pHzpRec );
559                 m_pHzpRec->m_hzp.free( arr );
560             }
561
562             /// Places retired pointer \p and its deleter \p pFunc into thread's array of retired pointer for deferred reclamation
563             template <typename T>
564             void retirePtr( T * p, void (* pFunc)(T *) )
565             {
566                 /*
567                 union {
568                     T * p;
569                     hazard_pointer hp;
570                 } cast_ptr;
571                 cast_ptr.p = p;
572
573                 union{
574                     void( *pFunc )(T *);
575                     free_retired_ptr_func hpFunc;
576                 } cast_func;
577                 cast_func.pFunc = pFunc;
578
579                 retirePtr( details::retired_ptr( cast_ptr.hp, cast_func.hpFunc ) );
580                 */
581                 retirePtr( details::retired_ptr( reinterpret_cast<void *>( p ), reinterpret_cast<free_retired_ptr_func>( pFunc )));
582             }
583
584             /// Places retired pointer \p into thread's array of retired pointer for deferred reclamation
585             void retirePtr( details::retired_ptr const& p )
586             {
587                 m_pHzpRec->m_arrRetired.push( p );
588
589                 if ( m_pHzpRec->m_arrRetired.isFull() ) {
590                     // Max of retired pointer count is reached. Do scan
591                     scan();
592                 }
593             }
594
595             void scan()
596             {
597                 m_HzpManager.Scan( m_pHzpRec );
598                 m_HzpManager.HelpScan( m_pHzpRec );
599             }
600         };
601
602         /// Auto hp_guard.
603         /**
604             This class encapsulates Hazard Pointer guard to protect a pointer against deletion .
605             It allocates one HP from thread's HP array in constructor and free the hazard pointer allocated 
606             in destructor.
607         */
608         class guard
609         {
610             details::hp_guard&  m_hp    ; ///< Hazard pointer guarded
611
612         public:
613             typedef details::hp_guard::hazard_ptr hazard_ptr ;  ///< Hazard pointer type
614
615         public:
616             /// Allocates HP guard
617             guard(); // inline in hp_impl.h
618
619             /// Allocates HP guard from \p gc and protects the pointer \p p of type \p T
620             template <typename T>
621             explicit guard( T * p ); // inline in hp_impl.h
622
623             /// Frees HP guard. The pointer guarded may be deleted after this.
624             ~guard(); // inline in hp_impl.h
625
626             /// Protects the pointer \p p against reclamation (guards the pointer).
627             template <typename T>
628             T * operator =( T * p )
629             {
630                 return m_hp = p;
631             }
632
633             std::nullptr_t operator =(std::nullptr_t)
634             {
635                 return m_hp = nullptr;
636             }
637
638             hazard_ptr get() const
639             {
640                 return m_hp;
641             }
642         };
643
644         /// Auto-managed array of hazard pointers
645         /**
646             This class is wrapper around cds::gc::hp::details::hp_array class.
647             \p Count is the size of HP array
648         */
649         template <size_t Count>
650         class array : public details::hp_array<Count>
651         {
652         public:
653             /// Rebind array for other size \p COUNT2
654             template <size_t Count2>
655             struct rebind {
656                 typedef array<Count2>  other;   ///< rebinding result
657             };
658
659         public:
660             /// Allocates array of HP guard
661             array(); // inline in hp_impl.h
662
663             /// Frees array of HP guard
664             ~array(); //inline in hp_impl.h
665         };
666
667     }   // namespace hp
668 }}  // namespace cds::gc
669 //@endcond
670
671 //@cond
672 // Inlines
673 namespace cds {
674     namespace gc { namespace hp { namespace details {
675
676         inline retired_vector::retired_vector( const cds::gc::hp::GarbageCollector& HzpMgr )
677             : m_arr( HzpMgr.getMaxRetiredPtrCount() ),
678             m_nSize(0)
679         {}
680
681         inline hp_record::hp_record( const cds::gc::hp::GarbageCollector& HzpMgr )
682             : m_hzp( HzpMgr.getHazardPointerCount() ),
683             m_arrRetired( HzpMgr )
684         {}
685
686     }}} // namespace gc::hp::details
687 } // namespace cds
688 //@endcond
689
690
691 #if CDS_COMPILER == CDS_COMPILER_MSVC
692 #   pragma warning(pop)
693 #endif
694
695 #endif  // #ifndef __CDS_GC_DETAILS_HP_H