Fixed Markdown layout
[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.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     )
144     {
145         if ( !m_pManager ) {
146             m_pManager = new GarbageCollector( nLiberateThreshold, nInitialThreadGuardCount );
147         }
148     }
149
150     void CDS_STDCALL GarbageCollector::Destruct()
151     {
152         delete m_pManager;
153         m_pManager = nullptr;
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.load( atomics::memory_order_relaxed );
180                 pHead->m_pNextFree.store( nullptr, atomics::memory_order_relaxed );
181                 set.insert( *pHead );
182                 pHead = pNext;
183             }
184
185             // Liberate cycle
186
187             details::retired_ptr_node * pBusyFirst = nullptr;
188             details::retired_ptr_node * pBusyLast = nullptr;
189             size_t nBusyCount = 0;
190
191             for ( details::guard_data * pGuard = m_GuardPool.begin(); pGuard; pGuard = pGuard->pGlobalNext.load(atomics::memory_order_acquire) )
192             {
193                 // get guarded pointer
194                 details::guard_data::guarded_ptr valGuarded = pGuard->pPost.load(atomics::memory_order_acquire);
195
196                 if ( valGuarded ) {
197                     details::retired_ptr_node * pRetired = set.erase( valGuarded );
198                     if ( pRetired ) {
199                         // Retired pointer is being guarded
200                         // pRetired is the head of retired pointers list for which the m_ptr.m_p field is equal
201                         // List is linked on m_pNextFree field
202
203                         if ( pBusyLast )
204                             pBusyLast->m_pNext.store( pRetired, atomics::memory_order_relaxed );
205                         else
206                             pBusyFirst = pRetired;
207                         pBusyLast = pRetired;
208                         ++nBusyCount;
209                         details::retired_ptr_node * p = pBusyLast->m_pNextFree.load(atomics::memory_order_relaxed);
210                         while ( p != nullptr ) {
211                             pBusyLast->m_pNext.store( p, atomics::memory_order_relaxed );
212                             pBusyLast = p;
213                             ++nBusyCount;
214                         }
215                     }
216                 }
217             }
218
219             // Place [pBusyList, pBusyLast] back to m_RetiredBuffer
220             if ( pBusyFirst )
221                 m_RetiredBuffer.push_list( pBusyFirst, pBusyLast, nBusyCount );
222
223             // Free all retired pointers
224             details::liberate_set::list_range range = set.free_all();
225
226             m_RetiredAllocator.inc_epoch();
227
228             if ( range.first ) {
229                 assert( range.second != nullptr );
230                 m_RetiredAllocator.free_range( range.first, range.second );
231             }
232             else {
233                 // scan() cycle did not free any retired pointer - double scan() threshold
234                 m_nLiberateThreshold.compare_exchange_strong( nLiberateThreshold, nLiberateThreshold * 2, atomics::memory_order_release, atomics::memory_order_relaxed );
235             }
236         }
237     }
238 }}} // namespace cds::gc::dhp