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