Skip to content

Commit 05e9cba

Browse files
authored
Merge pull request #4988 from WalterWaldron/fix16352
Fix issue 16352 - dead-lock in std.allocator.free_list unittest
2 parents e08d4f4 + 776202b commit 05e9cba

File tree

1 file changed

+44
-18
lines changed

1 file changed

+44
-18
lines changed

std/experimental/allocator/building_blocks/free_list.d

Lines changed: 44 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -760,6 +760,7 @@ struct SharedFreeList(ParentAllocator,
760760
"Maximum size must accommodate a pointer.");
761761

762762
import core.atomic : atomicOp, cas;
763+
import core.internal.spinlock : SpinLock;
763764

764765
static if (minSize != chooseAtRuntime)
765766
{
@@ -854,6 +855,10 @@ struct SharedFreeList(ParentAllocator,
854855
assert(nodes);
855856
atomicOp!("-=")(nodes, 1);
856857
}
858+
private void resetNodes() shared
859+
{
860+
nodes = 0;
861+
}
857862
private bool nodesFull() shared
858863
{
859864
return nodes >= approxMaxLength;
@@ -863,6 +868,7 @@ struct SharedFreeList(ParentAllocator,
863868
{
864869
private static void incNodes() { }
865870
private static void decNodes() { }
871+
private static void resetNodes() { }
866872
private enum bool nodesFull = false;
867873
}
868874

@@ -924,6 +930,8 @@ struct SharedFreeList(ParentAllocator,
924930

925931
mixin(forwardToMember("parent", "expand"));
926932

933+
private SpinLock lock;
934+
927935
private struct Node { Node* next; }
928936
static assert(ParentAllocator.alignment >= Node.alignof);
929937
private Node* _root;
@@ -958,18 +966,22 @@ struct SharedFreeList(ParentAllocator,
958966
assert(bytes < size_t.max / 2);
959967
if (!freeListEligible(bytes)) return parent.allocate(bytes);
960968
if (maxSize != unbounded) bytes = max;
961-
// Pop off the freelist
962-
shared Node* oldRoot = void, next = void;
963-
do
969+
970+
// Try to pop off the freelist
971+
lock.lock();
972+
if (!_root)
964973
{
965-
oldRoot = _root; // atomic load
966-
if (!oldRoot) return allocateFresh(bytes);
967-
next = oldRoot.next; // atomic load
974+
lock.unlock();
975+
return allocateFresh(bytes);
976+
}
977+
else
978+
{
979+
auto oldRoot = _root;
980+
_root = _root.next;
981+
decNodes();
982+
lock.unlock();
983+
return (cast(ubyte*) oldRoot)[0 .. bytes];
968984
}
969-
while (!cas(&_root, oldRoot, next));
970-
// great, snatched the root
971-
decNodes();
972-
return (cast(ubyte*) oldRoot)[0 .. bytes];
973985
}
974986

975987
private void[] allocateFresh(const size_t bytes) shared
@@ -984,14 +996,11 @@ struct SharedFreeList(ParentAllocator,
984996
if (!nodesFull && freeListEligible(b.length))
985997
{
986998
auto newRoot = cast(shared Node*) b.ptr;
987-
shared Node* oldRoot;
988-
do
989-
{
990-
oldRoot = _root;
991-
newRoot.next = oldRoot;
992-
}
993-
while (!cas(&_root, oldRoot, newRoot));
999+
lock.lock();
1000+
newRoot.next = _root;
1001+
_root = newRoot;
9941002
incNodes();
1003+
lock.unlock();
9951004
return true;
9961005
}
9971006
static if (hasMember!(ParentAllocator, "deallocate"))
@@ -1004,20 +1013,25 @@ struct SharedFreeList(ParentAllocator,
10041013
bool deallocateAll() shared
10051014
{
10061015
bool result = false;
1016+
lock.lock();
1017+
scope(exit) lock.unlock();
10071018
static if (hasMember!(ParentAllocator, "deallocateAll"))
10081019
{
10091020
result = parent.deallocateAll();
10101021
}
10111022
else static if (hasMember!(ParentAllocator, "deallocate"))
10121023
{
10131024
result = true;
1014-
for (auto n = _root; n; n = n.next)
1025+
for (auto n = _root; n;)
10151026
{
1027+
auto tmp = n.next;
10161028
if (!parent.deallocate((cast(ubyte*)n)[0 .. max]))
10171029
result = false;
1030+
n = tmp;
10181031
}
10191032
}
10201033
_root = null;
1034+
resetNodes();
10211035
return result;
10221036
}
10231037
}
@@ -1058,6 +1072,18 @@ unittest
10581072
}
10591073
}
10601074

1075+
unittest
1076+
{
1077+
import std.experimental.allocator.mallocator : Mallocator;
1078+
static shared SharedFreeList!(Mallocator, 64, 128, 10) a;
1079+
auto b = a.allocate(100);
1080+
a.deallocate(b);
1081+
assert(a.nodes == 1);
1082+
b = [];
1083+
a.deallocateAll();
1084+
assert(a.nodes == 0);
1085+
}
1086+
10611087
unittest
10621088
{
10631089
import std.experimental.allocator.mallocator : Mallocator;

0 commit comments

Comments
 (0)