Skip to content

Commit 53ea253

Browse files
committed
Merge branch 'main' into build
2 parents db610f7 + 85686c9 commit 53ea253

File tree

11 files changed

+436
-178
lines changed

11 files changed

+436
-178
lines changed

src/coreclr/gc/satori/SatoriFinalizationQueue.cpp

Lines changed: 117 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -34,17 +34,18 @@
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

5051
void 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)
159202
bool 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
191268
SatoriObject* 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
224301
size_t SatoriFinalizationQueue::Count()
225302
{
226-
return (m_dequeue - m_enqueue) & m_sizeMask;
303+
return (m_enqueue - m_dequeue) & m_sizeMask;
227304
}

src/coreclr/gc/satori/SatoriFinalizationQueue.h

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -40,18 +40,23 @@ class SatoriFinalizationQueue
4040
void Initialize(SatoriHeap* heap);
4141
bool TryUpdateScanTicket(int currentScanTicket);
4242
bool TryScheduleForFinalization(SatoriObject* finalizable);
43+
44+
bool TryReserveSpace(size_t count, size_t* index);
45+
void ScheduleForFinalizationAt(size_t index, SatoriObject* finalizable);
46+
4347
SatoriObject* TryGetNextItem();
4448
bool HasItems();
4549
size_t Count();
4650

4751
int OverflowedGen();
4852
void SetOverflow(int generation);
53+
void TryResizeIfOverflowing();
4954
void ResetOverflow(int generation);
5055

5156
template <typename F>
5257
void ForEachObjectRef(F lambda)
5358
{
54-
for (int i = m_dequeue; i != m_enqueue; i++)
59+
for (size_t i = m_dequeue; i != m_enqueue; i++)
5560
{
5661
lambda(&m_data[i & m_sizeMask].value);
5762
}
@@ -61,17 +66,19 @@ class SatoriFinalizationQueue
6166
struct Entry
6267
{
6368
SatoriObject* value;
64-
int version;
69+
size_t version;
6570
};
6671

67-
int m_enqueue;
68-
int m_dequeue;
72+
size_t m_enqueue;
73+
DECLSPEC_ALIGN(64)
74+
size_t m_dequeue;
75+
DECLSPEC_ALIGN(64)
76+
size_t m_sizeMask;
6977
int m_overflowedGen;
7078
int m_scanTicket;
71-
int m_sizeMask;
7279
Entry* m_data;
7380
SatoriHeap* m_heap;
74-
SatoriRegion* m_region;
81+
Entry* m_newData;
7582
};
7683

7784
#endif

src/coreclr/gc/satori/SatoriGC.cpp

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,8 @@ size_t SatoriGC::GetNumberOfFinalizable()
109109
Object* SatoriGC::GetNextFinalizable()
110110
{
111111
SatoriFinalizationQueue* queue = m_heap->FinalizationQueue();
112+
113+
tryAgain:
112114
Object* f = queue->TryGetNextItem();
113115
if (f == nullptr)
114116
{
@@ -118,7 +120,7 @@ Object* SatoriGC::GetNextFinalizable()
118120
if (queue->OverflowedGen() == 2)
119121
{
120122
m_heap->Recycler()->Collect(2, true, true);
121-
f = queue->TryGetNextItem();
123+
goto tryAgain;
122124
}
123125
}
124126

src/coreclr/gc/satori/SatoriHeap.h

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -146,21 +146,23 @@ class SatoriHeap
146146
static const int availableAddressSpaceBits = 48;
147147
static const int pageCountBits = availableAddressSpaceBits - Satori::PAGE_BITS;
148148

149-
int8_t m_pageByteMap[1 << pageCountBits]{};
150-
static int8_t* s_pageByteMap;
151-
152149
SatoriAllocator m_allocator;
153150
SatoriRecycler m_recycler;
154-
SatoriFinalizationQueue m_finalizationQueue;
155-
156-
size_t m_committedBytes;
157-
158151
size_t m_reservedMapSize;
159152
size_t m_committedMapSize;
160153
size_t m_usedMapLength;
161154
size_t m_nextPageIndex;
162155
SatoriLock m_mapLock;
163156

157+
size_t m_committedBytes;
158+
159+
DECLSPEC_ALIGN(64)
160+
SatoriFinalizationQueue m_finalizationQueue;
161+
162+
DECLSPEC_ALIGN(64)
163+
int8_t m_pageByteMap[1 << pageCountBits]{};
164+
static int8_t* s_pageByteMap;
165+
164166
#if _DEBUG
165167
// make the Heap obj a bit larger to force some page map commits earlier.
166168
int8_t dummy[0x7A70]{};

0 commit comments

Comments
 (0)