@@ -36,7 +36,8 @@ public:
3636 return null ;
3737 }
3838
39- auto asize = getAllocSize(alignUp(size, 2 * Quantum));
39+ auto reservedBytes = finalizer is null ? 0 : PointerSize;
40+ auto asize = getAllocSize(alignUp(size + reservedBytes, 2 * Quantum));
4041 assert (sizeClassSupportsMetadata(getSizeClass(asize)),
4142 " allocAppendable got size class without metadata support!" );
4243
@@ -47,12 +48,11 @@ public:
4748 auto ptr = arena.allocSmall(emap, asize);
4849 auto pd = getPageDescriptor(ptr);
4950 auto si = SlabAllocInfo(pd, ptr);
50- si.setUsedCapacity(size);
51- assert (finalizer is null , " finalizer not yet supported for slab!" );
51+ si.initializeMetadata(finalizer, size);
5252 return ptr;
5353 }
5454
55- auto ptr = arena.allocLarge(emap, asize , false );
55+ auto ptr = arena.allocLarge(emap, size , false );
5656 auto pd = getPageDescriptor(ptr);
5757 auto e = pd.extent;
5858 e.setUsedCapacity(size);
@@ -92,11 +92,21 @@ public:
9292 }
9393
9494 auto pd = getPageDescriptor(ptr);
95-
9695 auto e = pd.extent;
97- // Slab is not yet supported
98- if (e ! is null && ! pd.isSlab() && e.finalizer ! is null ) {
99- e.finalizer(ptr, e.usedCapacity);
96+
97+ if (pd.isSlab()) {
98+ auto si = SlabAllocInfo(pd, ptr);
99+ auto finalizer = si.finalizer;
100+ if (finalizer ! is null ) {
101+ assert (cast (void * ) si.address == ptr,
102+ " destroy() was invoked on an interior pointer!" );
103+
104+ finalizer(ptr, si.usedCapacity);
105+ }
106+ } else {
107+ if (e.finalizer ! is null ) {
108+ e.finalizer(ptr, e.usedCapacity);
109+ }
100110 }
101111
102112 pd.arena.free(emap, pd, ptr);
@@ -125,8 +135,9 @@ public:
125135 auto oldSizeClass = pd.sizeClass;
126136 if (samePointerness && newSizeClass == oldSizeClass) {
127137 auto si = SlabAllocInfo(pd, ptr);
128- si.setUsedCapacity(size);
129- return ptr;
138+ if (! si.allowsMetadata || si.setUsedCapacity(size)) {
139+ return ptr;
140+ }
130141 }
131142
132143 if (newSizeClass > oldSizeClass) {
@@ -834,4 +845,33 @@ unittest finalization {
834845 auto oldDestroyCount = destroyCount;
835846 threadCache.destroy (s1);
836847 assert (destroyCount == oldDestroyCount);
848+
849+ // Finalizers for small allocs:
850+ auto s2 = threadCache.allocAppendable(45 , false , &destruct);
851+ assert (threadCache.getCapacity(s2[0 .. 45 ]) == 56 );
852+ assert (! threadCache.extend(s2[0 .. 45 ], 12 ));
853+ assert (threadCache.extend(s2[0 .. 45 ], 11 ));
854+ assert (threadCache.getCapacity(s2[0 .. 56 ]) == 56 );
855+ threadCache.destroy (s2);
856+ assert (lastKilledAddress == s2);
857+ assert (lastKilledUsedCapacity == 56 );
858+
859+ // Behaviour of realloc() on small allocs with finalizers:
860+ auto s3 = threadCache.allocAppendable(70 , false , &destruct);
861+ assert (threadCache.getCapacity(s3[0 .. 70 ]) == 72 );
862+ auto s4 = threadCache.realloc(s3, 70 , false );
863+ assert (s3 == s4);
864+
865+ // This is in the same size class, but will not work in-place
866+ // given as finalizer occupies final 8 of the 80 bytes in the slot:
867+ auto s5 = threadCache.realloc(s4, 75 , false );
868+ assert (s5 != s4);
869+
870+ // So we end up with a new alloc, without metadata:
871+ assert (threadCache.getCapacity(s5[0 .. 80 ]) == 80 );
872+
873+ // And the finalizer has been discarded:
874+ oldDestroyCount = destroyCount;
875+ threadCache.destroy (s5);
876+ assert (destroyCount == oldDestroyCount);
837877}
0 commit comments