Skip to content

Commit 2dd00d4

Browse files
committed
[GC] When using free, make sure we leave space int he thread bin to accomodate it.
1 parent d08de37 commit 2dd00d4

File tree

2 files changed

+69
-3
lines changed

2 files changed

+69
-3
lines changed

sdlib/d/gc/tbin.d

Lines changed: 68 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,7 @@ public:
7575

7676
void refill(ref CachedExtentMap emap, shared(Arena)* arena,
7777
ref ThreadBinState state, ubyte sizeClass, size_t slotSize) {
78-
auto nfill = nmax >> state.fillShift;
78+
auto nfill = state.getFill(nmax);
7979
assert(nfill > 0);
8080

8181
/**
@@ -99,7 +99,14 @@ public:
9999
return true;
100100
}
101101

102-
void flushToFree(ref CachedExtentMap emap) {
102+
void flushToFree(ref CachedExtentMap emap, ref ThreadBinState state) {
103+
/**
104+
* When we use free explicitely, we want to make sure we have room
105+
* left in the bin to accomodate further freed elements, even in case
106+
* where we refill.
107+
*/
108+
state.onFlush();
109+
103110
/**
104111
* We do not have enough space in the bin, so start flushing.
105112
* However, we do not want to flush all of it, as it would leave
@@ -298,7 +305,25 @@ private:
298305

299306
struct ThreadBinState {
300307
bool refilled;
308+
bool flushed;
301309
ubyte fillShift;
310+
311+
uint getFill(ushort nmax) const {
312+
return nmax >> fillShift;
313+
}
314+
315+
bool onFlush() {
316+
if (flushed) {
317+
return false;
318+
}
319+
320+
flushed = true;
321+
if (fillShift == 0) {
322+
fillShift++;
323+
}
324+
325+
return true;
326+
}
302327
}
303328

304329
bool isValidThreadBinCapacity(uint capacity) {
@@ -521,3 +546,44 @@ unittest refill {
521546
"Invalid cached element count!");
522547
}
523548
}
549+
550+
unittest flushed {
551+
import d.gc.base;
552+
shared Base base;
553+
scope(exit) base.clear();
554+
555+
import d.gc.emap;
556+
static shared ExtentMap emapStorage;
557+
auto emap = CachedExtentMap(&emapStorage, &base);
558+
559+
enum BinSize = 200;
560+
561+
// Setup the bin.
562+
void*[BinSize] buffer;
563+
auto tbin = ThreadBin(buffer[0 .. BinSize]);
564+
assert(tbin.empty);
565+
566+
// Setup the state.
567+
ThreadBinState state;
568+
569+
// When we need to flush for free, we reduce refill.
570+
tbin.flushToFree(emap, state);
571+
572+
assert(state.flushed, "Thread bin was not flushed!");
573+
assert(state.getFill(tbin.nmax) == BinSize / 2, "Unexpected fill!");
574+
575+
// On repeat, nothing happens.
576+
tbin.flushToFree(emap, state);
577+
578+
assert(state.flushed, "Thread bin was not flushed!");
579+
assert(state.getFill(tbin.nmax) == BinSize / 2, "Unexpected fill!");
580+
581+
// When flushed is cleared, we redo, but never
582+
// reduce fill to less than half the bin.
583+
state.flushed = false;
584+
tbin.flushToFree(emap, state);
585+
586+
assert(!state.refilled, "Thread bin refilled!");
587+
assert(state.flushed, "Thread bin was not flushed!");
588+
assert(state.getFill(tbin.nmax) == BinSize / 2, "Unexpected fill!");
589+
}

sdlib/d/gc/tcache.d

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -320,7 +320,7 @@ private:
320320
return;
321321
}
322322

323-
bin.flushToFree(emap);
323+
bin.flushToFree(emap, binStates[index]);
324324

325325
auto success = bin.free(ptr);
326326
assert(success, "Unable to free!");

0 commit comments

Comments
 (0)