@@ -117,13 +117,21 @@ struct KRRegion(ParentAllocator = NullAllocator)
117117 {
118118 assert (right);
119119 auto p = payload;
120- return p.ptr + p.length == right ;
120+ return p.ptr < right && right < p.ptr + p.length + Node.sizeof ;
121121 }
122122
123- bool coalesce ()
123+ bool coalesce (void * memoryEnd = null )
124124 {
125+ // Coalesce the last node before the memory end with any possible gap
126+ if (memoryEnd
127+ && memoryEnd < payload.ptr + payload.length + Node.sizeof)
128+ {
129+ size += memoryEnd - (payload.ptr + payload.length);
130+ return true ;
131+ }
132+
125133 if (! adjacent(next)) return false ;
126- size += next.size;
134+ size = ( cast ( ubyte * ) next + next .size) - cast ( ubyte * ) & this ;
127135 next = next.next;
128136 return true ;
129137 }
@@ -137,6 +145,7 @@ struct KRRegion(ParentAllocator = NullAllocator)
137145 if (size < bytes) return typeof (return )();
138146 assert (size >= bytes);
139147 immutable leftover = size - bytes;
148+
140149 if (leftover >= Node.sizeof)
141150 {
142151 // There's room for another node
@@ -146,6 +155,7 @@ struct KRRegion(ParentAllocator = NullAllocator)
146155 assert (next);
147156 return tuple (payload, newNode);
148157 }
158+
149159 // No slack space, just return next node
150160 return tuple (payload, next == &this ? null : next);
151161 }
@@ -400,6 +410,7 @@ struct KRRegion(ParentAllocator = NullAllocator)
400410 }
401411 return result[0 .. n];
402412 }
413+
403414 // Not enough memory, switch to freelist mode and fall through
404415 switchToFreeList;
405416 }
@@ -417,6 +428,7 @@ struct KRRegion(ParentAllocator = NullAllocator)
417428 pnode.next = k[1 ];
418429 return k[0 ][0 .. n];
419430 }
431+
420432 pnode = pnode.next;
421433 if (pnode == root) break ;
422434 }
@@ -443,6 +455,7 @@ struct KRRegion(ParentAllocator = NullAllocator)
443455 // coalesce at this time. Instead, do it lazily during allocation.
444456 auto n = cast (Node* ) b.ptr;
445457 n.size = goodAllocSize(b.length);
458+ auto memoryEnd = payload.ptr + payload.length;
446459
447460 if (regionMode)
448461 {
@@ -458,6 +471,10 @@ struct KRRegion(ParentAllocator = NullAllocator)
458471 // What a sight for sore eyes
459472 root = n;
460473 root.next = root;
474+
475+ // If the first block freed is the last one allocated,
476+ // maybe there's a gap after it.
477+ root.coalesce(memoryEnd);
461478 return true ;
462479 }
463480
@@ -486,8 +503,10 @@ struct KRRegion(ParentAllocator = NullAllocator)
486503 else if (pnode < n)
487504 {
488505 // Insert at the end of the list
506+ // Add any possible gap at the end of n to the length of n
489507 n.next = pnode.next;
490508 pnode.next = n;
509+ n.coalesce(memoryEnd);
491510 pnode.coalesce;
492511 root = pnode;
493512 return true ;
@@ -771,3 +790,91 @@ unittest
771790 p = alloc.allocateAll();
772791 assert (p.length == 1024 * 1024 );
773792}
793+
794+ unittest
795+ {
796+ import std.experimental.allocator.building_blocks ;
797+ import std.random ;
798+ import std.typecons : Ternary;
799+
800+ // Both sequences must work on either system
801+
802+ // A sequence of allocs which generates the error described in issue 16564
803+ // that is a gap at the end of buf from the perspective of the allocator
804+
805+ // for 64 bit systems (leftover balance = 8 bytes < 16)
806+ int [] sizes64 = [18904 , 2008 , 74904 , 224 , 111904 , 1904 , 52288 , 8 ];
807+
808+ // for 32 bit systems (leftover balance < 8)
809+ int [] sizes32 = [81412 , 107068 , 49892 , 23768 ];
810+
811+
812+ void test (int [] sizes)
813+ {
814+ ubyte [256 * 1024 ] buf;
815+ auto a = KRRegion! ()(buf);
816+
817+ void [][] bufs;
818+
819+ foreach (size; sizes)
820+ {
821+ bufs ~= a.allocate(size);
822+ }
823+
824+ foreach (b; bufs.randomCover)
825+ {
826+ a.deallocate(b);
827+ }
828+
829+ assert (a.empty == Ternary.yes);
830+ }
831+
832+ test(sizes64);
833+ test(sizes32);
834+ }
835+
836+ unittest
837+ {
838+ import std.experimental.allocator.building_blocks ;
839+ import std.random ;
840+ import std.typecons : Ternary;
841+
842+ // For 64 bits, we allocate in multiples of 8, but the minimum alloc size is 16.
843+ // This can create gaps.
844+ // This test is an example of such a case. The gap is formed between the block
845+ // allocated for the second value in sizes and the third. There is also a gap
846+ // at the very end. (total lost 2 * word)
847+
848+ int [] sizes64 = [2008 , 18904 , 74904 , 224 , 111904 , 1904 , 52288 , 8 ];
849+ int [] sizes32 = [81412 , 107068 , 49892 , 23768 ];
850+
851+ int word64 = 8 ;
852+ int word32 = 4 ;
853+
854+ void test (int [] sizes, int word)
855+ {
856+ ubyte [256 * 1024 ] buf;
857+ auto a = KRRegion! ()(buf);
858+
859+ void [][] bufs;
860+
861+ foreach (size; sizes)
862+ {
863+ bufs ~= a.allocate(size);
864+ }
865+
866+ a.deallocate(bufs[1 ]);
867+ bufs ~= a.allocate(sizes[1 ] - word);
868+
869+ a.deallocate(bufs[0 ]);
870+ foreach (i; 2 .. bufs.length)
871+ {
872+ a.deallocate(bufs[i]);
873+ }
874+
875+ assert (a.empty == Ternary.yes);
876+ }
877+
878+ test(sizes64, word64);
879+ test(sizes32, word32);
880+ }
0 commit comments