3434#include " SatoriHeap.h"
3535#include " SatoriRegion.h"
3636#include " SatoriRegion.inl"
37+ #include " SatoriObject.inl"
3738
3839// limit the queue size to some large value just to have a limit.
3940// needing this many items is likely an indication that finalizer is not keeping up and nothing can help that.
40- static const int MAX_SIZE = 1 << 25 ;
41+ static const size_t MAX_SIZE = 1 << 25 ;
4142
4243#if _DEBUG
4344// smaller size in debug to have overflows
44- static const int INITIAL_SIZE = 1 << 5 ;
45+ static const size_t INITIAL_SIZE = 1 << 5 ;
4546#else
4647// 4K items (65Kb) - roughly the size of 2 region headers
47- static const int INITIAL_SIZE = 1 << 12 ;
48+ static const size_t INITIAL_SIZE = 1 << 12 ;
4849#endif
4950
5051void SatoriFinalizationQueue::Initialize (SatoriHeap* heap)
@@ -54,16 +55,18 @@ void SatoriFinalizationQueue::Initialize(SatoriHeap* heap)
5455 m_scanTicket = 0 ;
5556 m_overflowedGen = 0 ;
5657 m_heap = heap;
58+ m_newData = nullptr ;
5759
58- int size = INITIAL_SIZE;
60+ size_t size = INITIAL_SIZE;
5961
6062 m_sizeMask = size - 1 ;
6163 size_t allocSize = size * sizeof (Entry);
6264 size_t regionSize = SatoriRegion::RegionSizeForAlloc (allocSize);
63- m_region = m_heap->Allocator ()->GetRegion (regionSize);
64- m_data = (Entry*)m_region ->Allocate (allocSize, /* zeroInitialize*/ false );
65+ SatoriRegion* region = m_heap->Allocator ()->GetRegion (regionSize);
66+ m_data = (Entry*)region ->Allocate (allocSize, /* zeroInitialize*/ false );
6567
66- for (int i = 0 ; i < size; i++)
68+ // format as empty
69+ for (size_t i = 0 ; i < size; i++)
6770 {
6871 m_data[i].version = i;
6972 }
@@ -82,15 +85,17 @@ void SatoriFinalizationQueue::SetOverflow(int generation)
8285 }
8386}
8487
85- void SatoriFinalizationQueue::ResetOverflow ( int generation )
88+ void SatoriFinalizationQueue::TryResizeIfOverflowing ( )
8689{
87- if (!m_overflowedGen || generation < m_overflowedGen)
88- {
90+ if (!m_overflowedGen || m_newData != nullptr )
91+ return ;
92+
93+ // claim resizing
94+ if (Interlocked::CompareExchangePointer ((void **)&m_newData, (void *)m_data, (void *)nullptr ) != nullptr )
8995 return ;
90- }
9196
9297 // try resizing
93- int size = (m_sizeMask + 1 ) * 2 ;
98+ size_t size = (m_sizeMask + 1 ) * 2 ;
9499 if (size <= MAX_SIZE)
95100 {
96101 size_t allocSize = size * sizeof (Entry);
@@ -106,33 +111,71 @@ void SatoriFinalizationQueue::ResetOverflow(int generation)
106111 return ;
107112 }
108113
109- // transfer items from the old queue
110- int dst = 0 ;
111- for (int src = m_dequeue; src != m_enqueue; src++)
112- {
113- newData[dst].value = m_data[src & m_sizeMask].value ;
114- newData[dst].version = dst + 1 ;
115- dst++;
116- }
117-
118- // format the rest of the items as empty
119- for (int i = dst; i < size; i++)
114+ // format as empty
115+ for (size_t i = 0 ; i < size; i++)
120116 {
121117 newData[i].version = i;
122118 }
123119
124- m_data = newData;
125- m_sizeMask = size - 1 ;
126- m_enqueue = dst;
127- m_dequeue = 0 ;
120+ m_newData = newData;
121+ }
122+ }
123+ }
124+
125+ void SatoriFinalizationQueue::ResetOverflow (int generation)
126+ {
127+ if (!m_overflowedGen || generation < m_overflowedGen)
128+ {
129+ // no overflow or a minor collection after a major overflow.
130+ // either way not our job to fix things up.
131+ return ;
132+ }
133+
134+ if (m_newData == m_data)
135+ {
136+ // someone claimed, but failed to resize.
137+ m_newData = nullptr ;
138+ }
139+
140+ if (m_newData == nullptr )
141+ {
142+ // we will be pending what last failed, try get more space.
143+ TryResizeIfOverflowing ();
144+ if (m_newData == m_data)
145+ {
146+ m_newData = nullptr ;
147+ }
148+ }
149+
150+ // if there was a resize, move things over.
151+ if (m_newData != nullptr )
152+ {
153+ Entry* newData = m_newData;
154+ m_newData = nullptr ;
128155
129- m_region->MakeBlank ();
130- m_heap->Allocator ()->ReturnRegion (m_region);
131- m_region = newRegion;
156+ // transfer items from the old queue
157+ size_t dst = 0 ;
158+ for (size_t src = m_dequeue; src != m_enqueue; src++)
159+ {
160+ Entry* e = &newData[dst];
161+ e->value = m_data[src & m_sizeMask].value ;
162+ _ASSERTE (e->version == dst);
163+ e->version = dst + 1 ;
164+ dst++;
132165 }
166+
167+ SatoriRegion* oldRegion = ((SatoriObject*)m_data)->ContainingRegion ();
168+ oldRegion->MakeBlank ();
169+ m_heap->Allocator ()->ReturnRegion (oldRegion);
170+
171+ m_data = newData;
172+ size_t size = (m_sizeMask + 1 ) * 2 ;
173+ m_sizeMask = size - 1 ;
174+ m_enqueue = dst;
175+ m_dequeue = 0 ;
133176 }
134177
135- // either way we are not in an overflow unless we see a full queue again.
178+ // we are not in an overflow unless we see a full queue again.
136179 m_overflowedGen = 0 ;
137180}
138181
@@ -159,12 +202,13 @@ bool SatoriFinalizationQueue::TryUpdateScanTicket(int currentScanTicket)
159202bool SatoriFinalizationQueue::TryScheduleForFinalization (SatoriObject* finalizable)
160203{
161204 Entry* e;
162- int enq = m_enqueue;
205+ size_t enq = m_enqueue;
206+ size_t i = 0 ;
163207 for (;;)
164208 {
165209 e = &m_data[enq & m_sizeMask];
166- int version = VolatileLoad (&e->version );
167- int diff = version - enq;
210+ size_t version = VolatileLoad (&e->version );
211+ ptrdiff_t diff = version - enq;
168212
169213 if (diff == 0 )
170214 {
@@ -178,7 +222,13 @@ bool SatoriFinalizationQueue::TryScheduleForFinalization(SatoriObject* finalizab
178222 return false ;
179223 }
180224
181- YieldProcessor ();
225+ i++;
226+ size_t lim = i * i;
227+ for (size_t j = 0 ; j < lim; j++)
228+ {
229+ YieldProcessor ();
230+ }
231+
182232 enq = m_enqueue;
183233 }
184234
@@ -187,16 +237,43 @@ bool SatoriFinalizationQueue::TryScheduleForFinalization(SatoriObject* finalizab
187237 return true ;
188238}
189239
190- // called by a single consuming thread
240+ // called by multiple producers with no active consumers
241+ bool SatoriFinalizationQueue::TryReserveSpace (size_t count, size_t * index)
242+ {
243+ size_t deq = m_dequeue;
244+ size_t mask = m_sizeMask;
245+ size_t enq;
246+
247+ do
248+ {
249+ enq = m_enqueue;
250+ if (enq + count - deq >= mask)
251+ return false ; // overflow
252+ }
253+ while (Interlocked::CompareExchange (&m_enqueue, enq + count, enq) != enq);
254+
255+ *index = enq;
256+ return true ;
257+ }
258+
259+ void SatoriFinalizationQueue::ScheduleForFinalizationAt (size_t index, SatoriObject* finalizable)
260+ {
261+ Entry* e = &m_data[index & m_sizeMask];
262+ e->value = finalizable;
263+ _ASSERTE (e->version == index);
264+ e->version = index + 1 ;
265+ }
266+
267+ // called by a single consuming thread with possibly active producers
191268SatoriObject* SatoriFinalizationQueue::TryGetNextItem ()
192269{
193- int deq = m_dequeue;
270+ size_t deq = m_dequeue;
194271 Entry* e = &m_data[deq & m_sizeMask];
195272
196- int version = VolatileLoad (&e->version );
273+ size_t version = VolatileLoad (&e->version );
197274 SatoriObject* value = e->value ;
198275
199- int diff = version - (deq + 1 );
276+ ptrdiff_t diff = version - (deq + 1 );
200277 if (diff < 0 )
201278 {
202279 value = nullptr ;
@@ -223,5 +300,5 @@ bool SatoriFinalizationQueue::HasItems()
223300// only makes sense in quiescent state
224301size_t SatoriFinalizationQueue::Count ()
225302{
226- return (m_dequeue - m_enqueue ) & m_sizeMask;
303+ return (m_enqueue - m_dequeue ) & m_sizeMask;
227304}
0 commit comments