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()
151 m_pManager = nullptr;
154 GarbageCollector::GarbageCollector( size_t nLiberateThreshold, size_t nInitialThreadGuardCount )
155 : m_nLiberateThreshold( nLiberateThreshold ? nLiberateThreshold : 1024 )
156 , m_nInitialThreadGuardCount( nInitialThreadGuardCount ? nInitialThreadGuardCount : 8 )
161 GarbageCollector::~GarbageCollector()
166 void GarbageCollector::scan()
168 details::retired_ptr_buffer::privatize_result retiredList = m_RetiredBuffer.privatize();
169 if ( retiredList.first ) {
171 size_t nLiberateThreshold = m_nLiberateThreshold.load(atomics::memory_order_relaxed);
172 details::liberate_set set( beans::ceil2( retiredList.second > nLiberateThreshold ? retiredList.second : nLiberateThreshold ) );
174 // Get list of retired pointers
175 details::retired_ptr_node * pHead = retiredList.first;
177 details::retired_ptr_node * pNext = pHead->m_pNext;
178 pHead->m_pNextFree = nullptr;
179 set.insert( *pHead );
185 details::retired_ptr_node * pBusyFirst = nullptr;
186 details::retired_ptr_node * pBusyLast = nullptr;
187 size_t nBusyCount = 0;
189 for ( details::guard_data * pGuard = m_GuardPool.begin(); pGuard; pGuard = pGuard->pGlobalNext.load(atomics::memory_order_acquire) )
191 // get guarded pointer
192 details::guard_data::guarded_ptr valGuarded = pGuard->pPost.load(atomics::memory_order_acquire);
195 details::retired_ptr_node * pRetired = set.erase( valGuarded );
197 // Retired pointer is being guarded
198 // pRetired is the head of retired pointers list for which the m_ptr.m_p field is equal
199 // List is linked on m_pNextFree field
202 pBusyLast->m_pNext = pRetired;
204 pBusyFirst = pRetired;
205 pBusyLast = pRetired;
207 while ( pBusyLast->m_pNextFree ) {
208 pBusyLast = pBusyLast->m_pNext = pBusyLast->m_pNextFree;
215 // Place [pBusyList, pBusyLast] back to m_RetiredBuffer
217 m_RetiredBuffer.push_list( pBusyFirst, pBusyLast, nBusyCount );
219 // Free all retired pointers
220 details::liberate_set::list_range range = set.free_all();
222 m_RetiredAllocator.inc_epoch();
225 assert( range.second != nullptr );
226 m_RetiredAllocator.free_range( range.first, range.second );
229 // scan() cycle did not free any retired pointer - double scan() threshold
230 m_nLiberateThreshold.compare_exchange_strong( nLiberateThreshold, nLiberateThreshold * 2, atomics::memory_order_release, atomics::memory_order_relaxed );
234 }}} // namespace cds::gc::dhp