Dynamic Hazard Pointer GC refactoring
[libcds.git] / src / dhp_gc.cpp
1 //$$CDS-header$$
2
3 // Dynamic Hazard Pointer memory manager implementation
4
5 #include <algorithm>   // std::fill
6 #include <functional>  // std::hash
7
8 #include <cds/gc/details/dhp.h>
9 #include <cds/algo/int_algo.h>
10
11 namespace cds { namespace gc { namespace dhp {
12
13     namespace details {
14
15         class liberate_set {
16             typedef retired_ptr_node *  item_type;
17             typedef cds::details::Allocator<item_type, CDS_DEFAULT_ALLOCATOR>   allocator_type;
18
19             size_t const m_nBucketCount;
20             item_type *  m_Buckets;
21
22             item_type&  bucket( retired_ptr_node& node )
23             {
24                 return bucket( node.m_ptr.m_p );
25             }
26             item_type&  bucket( guard_data::guarded_ptr p )
27             {
28                 return m_Buckets[ std::hash<guard_data::guarded_ptr>()( p ) & (m_nBucketCount - 1)  ];
29             }
30
31         public:
32             liberate_set( size_t nBucketCount )
33                 : m_nBucketCount( nBucketCount )
34             {
35                 assert( nBucketCount > 0 );
36                 assert( (nBucketCount & (nBucketCount - 1)) == 0 );
37
38                 m_Buckets = allocator_type().NewArray( nBucketCount );
39                 std::fill( m_Buckets, m_Buckets + nBucketCount, nullptr );
40             }
41
42             ~liberate_set()
43             {
44                 allocator_type().Delete( m_Buckets, m_nBucketCount );
45             }
46
47             void insert( retired_ptr_node& node )
48             {
49                 node.m_pNext = nullptr;
50
51                 item_type& refBucket = bucket( node );
52                 if ( refBucket ) {
53                     item_type p = refBucket;
54                     do {
55                         if ( p->m_ptr.m_p == node.m_ptr.m_p ) {
56                             assert( node.m_pNextFree == nullptr );
57
58                             node.m_pNextFree = p->m_pNextFree;
59                             p->m_pNextFree = &node;
60                             return;
61                         }
62                         p = p->m_pNext;
63                     } while ( p );
64
65                     node.m_pNext = refBucket;
66                 }
67                 refBucket = &node;
68             }
69
70             item_type erase( guard_data::guarded_ptr ptr )
71             {
72                 item_type& refBucket = bucket( ptr );
73                 item_type p = refBucket;
74                 item_type pPrev = nullptr;
75
76                 while ( p ) {
77                     if ( p->m_ptr.m_p == ptr ) {
78                         if ( pPrev )
79                             pPrev->m_pNext = p->m_pNext;
80                         else
81                             refBucket = p->m_pNext;
82                         p->m_pNext = nullptr;
83                         return p;
84                     }
85                     pPrev = p;
86                     p = p->m_pNext;
87                 }
88
89                 return nullptr;
90             }
91
92             typedef std::pair<item_type, item_type>     list_range;
93
94             list_range free_all()
95             {
96                 item_type pTail = nullptr;
97                 list_range ret = std::make_pair( pTail, pTail );
98
99                 item_type const * pEndBucket = m_Buckets + m_nBucketCount;
100                 for ( item_type * ppBucket = m_Buckets; ppBucket < pEndBucket; ++ppBucket ) {
101                     item_type pBucket = *ppBucket;
102                     if ( pBucket ) {
103                         if ( !ret.first )
104                             ret.first = pBucket;
105                         else
106                             pTail->m_pNextFree = pBucket;
107
108                         pTail = pBucket;
109                         for (;;) {
110                             item_type pNext = pTail->m_pNext;
111                             pTail->m_ptr.free();
112                             pTail->m_pNext = nullptr;
113
114                             while ( pTail->m_pNextFree ) {
115                                 pTail = pTail->m_pNextFree;
116                                 pTail->m_ptr.free();
117                                 pTail->m_pNext = nullptr;
118                             }
119
120                             if ( pNext )
121                                 pTail = pTail->m_pNextFree = pNext;
122                             else
123                                 break;
124                         }
125                     }
126                 }
127
128                 if ( pTail )
129                     pTail->m_pNextFree = nullptr;
130                 ret.second = pTail;
131                 return ret;
132             }
133         };
134     }
135
136     GarbageCollector * GarbageCollector::m_pManager = nullptr;
137
138     void CDS_STDCALL GarbageCollector::Construct(
139         size_t nLiberateThreshold
140         , size_t nInitialThreadGuardCount
141     )
142     {
143         if ( !m_pManager ) {
144             m_pManager = new GarbageCollector( nLiberateThreshold, nInitialThreadGuardCount );
145         }
146     }
147
148     void CDS_STDCALL GarbageCollector::Destruct()
149     {
150         if ( m_pManager ) {
151             delete m_pManager;
152             m_pManager = nullptr;
153         }
154     }
155
156     GarbageCollector::GarbageCollector( size_t nLiberateThreshold, size_t nInitialThreadGuardCount )
157         : m_nLiberateThreshold( nLiberateThreshold ? nLiberateThreshold : 1024 )
158         , m_nInitialThreadGuardCount( nInitialThreadGuardCount ? nInitialThreadGuardCount : 8 )
159         //, m_nInLiberate(0)
160     {
161     }
162
163     GarbageCollector::~GarbageCollector()
164     {
165         scan();
166     }
167
168     void GarbageCollector::scan()
169     {
170         details::retired_ptr_buffer::privatize_result retiredList = m_RetiredBuffer.privatize();
171         if ( retiredList.first ) {
172
173             size_t nLiberateThreshold = m_nLiberateThreshold.load(atomics::memory_order_relaxed);
174             details::liberate_set set( beans::ceil2( retiredList.second > nLiberateThreshold ? retiredList.second : nLiberateThreshold ) );
175
176             // Get list of retired pointers
177             details::retired_ptr_node * pHead = retiredList.first;
178             while ( pHead ) {
179                 details::retired_ptr_node * pNext = pHead->m_pNext;
180                 pHead->m_pNextFree = nullptr;
181                 set.insert( *pHead );
182                 pHead = pNext;
183             }
184
185             // Liberate cycle
186             for ( details::guard_data * pGuard = m_GuardPool.begin(); pGuard; pGuard = pGuard->pGlobalNext.load(atomics::memory_order_acquire) )
187             {
188                 // get guarded pointer
189                 details::guard_data::guarded_ptr  valGuarded = pGuard->pPost.load(atomics::memory_order_acquire);
190
191                 if ( valGuarded ) {
192                     details::retired_ptr_node * pRetired = set.erase( valGuarded );
193                     if ( pRetired ) {
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
197
198                         do {
199                             details::retired_ptr_node * pNext = pRetired->m_pNextFree;
200                             m_RetiredBuffer.push( *pRetired );
201                             pRetired = pNext;
202                         } while ( pRetired );
203                     }
204                 }
205             }
206
207             // Free all retired pointers
208             details::liberate_set::list_range range = set.free_all();
209
210             m_RetiredAllocator.inc_epoch();
211
212             if ( range.first ) {
213                 assert( range.second != nullptr );
214                 m_RetiredAllocator.free_range( range.first, range.second );
215             }
216             else {
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 );
219             }
220         }
221     }
222 }}} // namespace cds::gc::dhp