3 // Dynamic Hazard Pointer memory manager implementation
5 #include <algorithm> // std::fill
6 #include <functional> // std::hash
8 #include <cds/gc/details/dhp.h>
9 #include <cds/algo/int_algo.h>
11 namespace cds { namespace gc { namespace dhp {
16 typedef retired_ptr_node * item_type;
17 typedef cds::details::Allocator<item_type, CDS_DEFAULT_ALLOCATOR> allocator_type;
19 size_t const m_nBucketCount;
20 item_type * m_Buckets;
22 item_type& bucket( retired_ptr_node& node )
24 return bucket( node.m_ptr.m_p );
26 item_type& bucket( guard_data::guarded_ptr p )
28 return m_Buckets[ std::hash<guard_data::guarded_ptr>()( p ) & (m_nBucketCount - 1) ];
32 liberate_set( size_t nBucketCount )
33 : m_nBucketCount( nBucketCount )
35 assert( nBucketCount > 0 );
36 assert( (nBucketCount & (nBucketCount - 1)) == 0 );
38 m_Buckets = allocator_type().NewArray( nBucketCount );
39 std::fill( m_Buckets, m_Buckets + nBucketCount, nullptr );
44 allocator_type().Delete( m_Buckets, m_nBucketCount );
47 void insert( retired_ptr_node& node )
49 node.m_pNext = nullptr;
51 item_type& refBucket = bucket( node );
53 item_type p = refBucket;
55 if ( p->m_ptr.m_p == node.m_ptr.m_p ) {
56 assert( node.m_pNextFree == nullptr );
58 node.m_pNextFree = p->m_pNextFree;
59 p->m_pNextFree = &node;
65 node.m_pNext = refBucket;
70 item_type erase( guard_data::guarded_ptr ptr )
72 item_type& refBucket = bucket( ptr );
73 item_type p = refBucket;
74 item_type pPrev = nullptr;
77 if ( p->m_ptr.m_p == ptr ) {
79 pPrev->m_pNext = p->m_pNext;
81 refBucket = p->m_pNext;
92 typedef std::pair<item_type, item_type> list_range;
96 item_type pTail = nullptr;
97 list_range ret = std::make_pair( pTail, pTail );
99 item_type const * pEndBucket = m_Buckets + m_nBucketCount;
100 for ( item_type * ppBucket = m_Buckets; ppBucket < pEndBucket; ++ppBucket ) {
101 item_type pBucket = *ppBucket;
106 pTail->m_pNextFree = pBucket;
110 item_type pNext = pTail->m_pNext;
112 pTail->m_pNext = nullptr;
114 while ( pTail->m_pNextFree ) {
115 pTail = pTail->m_pNextFree;
117 pTail->m_pNext = nullptr;
121 pTail = pTail->m_pNextFree = pNext;
129 pTail->m_pNextFree = nullptr;
136 GarbageCollector * GarbageCollector::m_pManager = nullptr;
138 void CDS_STDCALL GarbageCollector::Construct(
139 size_t nLiberateThreshold
140 , size_t nInitialThreadGuardCount
144 m_pManager = new GarbageCollector( nLiberateThreshold, nInitialThreadGuardCount );
148 void CDS_STDCALL GarbageCollector::Destruct()
152 m_pManager = nullptr;
156 GarbageCollector::GarbageCollector( size_t nLiberateThreshold, size_t nInitialThreadGuardCount )
157 : m_nLiberateThreshold( nLiberateThreshold ? nLiberateThreshold : 1024 )
158 , m_nInitialThreadGuardCount( nInitialThreadGuardCount ? nInitialThreadGuardCount : 8 )
163 GarbageCollector::~GarbageCollector()
168 void GarbageCollector::scan()
170 details::retired_ptr_buffer::privatize_result retiredList = m_RetiredBuffer.privatize();
171 if ( retiredList.first ) {
173 size_t nLiberateThreshold = m_nLiberateThreshold.load(atomics::memory_order_relaxed);
174 details::liberate_set set( beans::ceil2( retiredList.second > nLiberateThreshold ? retiredList.second : nLiberateThreshold ) );
176 // Get list of retired pointers
177 details::retired_ptr_node * pHead = retiredList.first;
179 details::retired_ptr_node * pNext = pHead->m_pNext;
180 pHead->m_pNextFree = nullptr;
181 set.insert( *pHead );
186 for ( details::guard_data * pGuard = m_GuardPool.begin(); pGuard; pGuard = pGuard->pGlobalNext.load(atomics::memory_order_acquire) )
188 // get guarded pointer
189 details::guard_data::guarded_ptr valGuarded = pGuard->pPost.load(atomics::memory_order_acquire);
192 details::retired_ptr_node * pRetired = set.erase( valGuarded );
194 // Retired pointer is being guarded
195 // pRetired is the head of retired pointers list for which the m_ptr.m_p field is equal
196 // List is linked on m_pNextFree field
199 details::retired_ptr_node * pNext = pRetired->m_pNextFree;
200 m_RetiredBuffer.push( *pRetired );
202 } while ( pRetired );
207 // Free all retired pointers
208 details::liberate_set::list_range range = set.free_all();
210 m_RetiredAllocator.inc_epoch();
213 assert( range.second != nullptr );
214 m_RetiredAllocator.free_range( range.first, range.second );
217 // scan() cycle did not free any retired pointer - double scan() threshold
218 m_nLiberateThreshold.compare_exchange_strong( nLiberateThreshold, nLiberateThreshold * 2, atomics::memory_order_release, atomics::memory_order_relaxed );
222 }}} // namespace cds::gc::dhp