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