Skip to content

Commit 45559e2

Browse files
authored
support for finalization in slab allocs and tests for same. (#320)
1 parent 3e1ada2 commit 45559e2

File tree

1 file changed

+50
-10
lines changed

1 file changed

+50
-10
lines changed

sdlib/d/gc/tcache.d

Lines changed: 50 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -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

Comments
 (0)