@@ -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
299306struct 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
304329bool 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+ }
0 commit comments