a5d22487b2225827ece14c3772acadbaf8c532b2
[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 ) const
23             {
24                 return bucket( node.m_ptr.m_p );
25             }
26             item_type&  bucket( guard_data::guarded_ptr p ) const
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.store( nullptr, atomics::memory_order_relaxed );
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.load( atomics::memory_order_relaxed ) == nullptr );
57
58                             node.m_pNextFree.store( p->m_pNextFree.load( atomics::memory_order_relaxed ), atomics::memory_order_relaxed );
59                             p->m_pNextFree.store( &node, atomics::memory_order_relaxed );
60                             return;
61                         }
62                         p = p->m_pNext.load(atomics::memory_order_relaxed);
63                     } while ( p );
64
65                     node.m_pNext.store( refBucket, atomics::memory_order_relaxed );
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.store( p->m_pNext.load(atomics::memory_order_relaxed ), atomics::memory_order_relaxed );
80                         else
81                             refBucket = p->m_pNext.load(atomics::memory_order_relaxed);
82                         p->m_pNext.store( nullptr, atomics::memory_order_relaxed );
83                         return p;
84                     }
85                     pPrev = p;
86                     p = p->m_pNext.load( atomics::memory_order_relaxed );
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.store( pBucket, atomics::memory_order_relaxed );
107
108                         pTail = pBucket;
109                         for (;;) {
110                             item_type pNext = pTail->m_pNext.load( atomics::memory_order_relaxed );
111                             pTail->m_ptr.free();
112                             pTail->m_pNext.store( nullptr, atomics::memory_order_relaxed );
113
114                             while ( pTail->m_pNextFree.load( atomics::memory_order_relaxed )) {
115                                 pTail = pTail->m_pNextFree.load( atomics::memory_order_relaxed );
116                                 pTail->m_ptr.free();
117                                 pTail->m_pNext.store( nullptr, atomics::memory_order_relaxed );
118                             }
119
120                             if ( pNext ) {
121                                 pTail->m_pNextFree.store( pNext, atomics::memory_order_relaxed );
122                                 pTail = pNext;
123                             }
124                             else
125                                 break;
126                         }
127                     }
128                 }
129
130                 if ( pTail )
131                     pTail->m_pNextFree.store( nullptr, atomics::memory_order_relaxed );
132                 ret.second = pTail;
133                 return ret;
134             }
135         };
136     }
137
138     GarbageCollector * GarbageCollector::m_pManager = nullptr;
139
140     void CDS_STDCALL GarbageCollector::Construct(
141         size_t nLiberateThreshold
142         , size_t nInitialThreadGuardCount
143         , size_t nEpochCount
144     )
145     {
146         if ( !m_pManager ) {
147             m_pManager = new GarbageCollector( nLiberateThreshold, nInitialThreadGuardCount, nEpochCount );
148         }
149     }
150
151     void CDS_STDCALL GarbageCollector::Destruct()
152     {
153         delete m_pManager;
154         m_pManager = nullptr;
155     }
156
157     GarbageCollector::GarbageCollector( size_t nLiberateThreshold, size_t nInitialThreadGuardCount, size_t nEpochCount )
158         : m_nLiberateThreshold( nLiberateThreshold ? nLiberateThreshold : 1024 )
159         , m_nInitialThreadGuardCount( nInitialThreadGuardCount ? nInitialThreadGuardCount : 8 )
160         , m_RetiredAllocator( static_cast<unsigned int>(nEpochCount))
161         , m_bStatEnabled( false )
162     {}
163
164     GarbageCollector::~GarbageCollector()
165     {
166         scan();
167     }
168
169     void GarbageCollector::scan()
170     {
171         details::retired_ptr_buffer::privatize_result retiredList = m_RetiredBuffer.privatize();
172         if ( retiredList.first ) {
173
174             size_t nLiberateThreshold = m_nLiberateThreshold.load(atomics::memory_order_relaxed);
175             details::liberate_set set( beans::ceil2( retiredList.second > nLiberateThreshold ? retiredList.second : nLiberateThreshold ));
176
177             // Get list of retired pointers
178             size_t nRetiredCount = 0;
179             details::retired_ptr_node * pHead = retiredList.first;
180             while ( pHead ) {
181                 details::retired_ptr_node * pNext = pHead->m_pNext.load( atomics::memory_order_relaxed );
182                 pHead->m_pNextFree.store( nullptr, atomics::memory_order_relaxed );
183                 set.insert( *pHead );
184                 pHead = pNext;
185                 ++nRetiredCount;
186             }
187
188             // Liberate cycle
189
190             details::retired_ptr_node * pBusyFirst = nullptr;
191             details::retired_ptr_node * pBusyLast = nullptr;
192             size_t nBusyCount = 0;
193
194             for ( details::guard_data * pGuard = m_GuardPool.begin(); pGuard; pGuard = pGuard->pGlobalNext.load(atomics::memory_order_acquire) )
195             {
196                 // get guarded pointer
197                 details::guard_data::guarded_ptr valGuarded = pGuard->pPost.load(atomics::memory_order_acquire);
198
199                 if ( valGuarded ) {
200                     details::retired_ptr_node * pRetired = set.erase( valGuarded );
201                     if ( pRetired ) {
202                         // Retired pointer is being guarded
203                         // pRetired is the head of retired pointers list for which the m_ptr.m_p field is equal
204                         // List is linked on m_pNextFree field
205
206                         if ( pBusyLast )
207                             pBusyLast->m_pNext.store( pRetired, atomics::memory_order_relaxed );
208                         else
209                             pBusyFirst = pRetired;
210                         pBusyLast = pRetired;
211                         ++nBusyCount;
212                         details::retired_ptr_node * p = pBusyLast->m_pNextFree.load(atomics::memory_order_relaxed);
213                         while ( p != nullptr ) {
214                             pBusyLast->m_pNext.store( p, atomics::memory_order_relaxed );
215                             pBusyLast = p;
216                             ++nBusyCount;
217                         }
218                     }
219                 }
220             }
221
222             // Place [pBusyList, pBusyLast] back to m_RetiredBuffer
223             if ( pBusyFirst )
224                 m_RetiredBuffer.push_list( pBusyFirst, pBusyLast, nBusyCount );
225
226             // Free all retired pointers
227             details::liberate_set::list_range range = set.free_all();
228
229             m_RetiredAllocator.inc_epoch();
230
231             if ( range.first ) {
232                 assert( range.second != nullptr );
233                 m_RetiredAllocator.free_range( range.first, range.second );
234             }
235             else if ( nRetiredCount >= nLiberateThreshold ) {
236                 // scan() cycle did not free any retired pointer - double scan() threshold
237                 m_nLiberateThreshold.compare_exchange_strong( nLiberateThreshold, nLiberateThreshold * 2, atomics::memory_order_release, atomics::memory_order_relaxed );
238             }
239         }
240     }
241 }}} // namespace cds::gc::dhp