Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Make KRR allocate @safe #10585

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
72 changes: 46 additions & 26 deletions std/experimental/allocator/building_blocks/kernighan_ritchie.d
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,7 @@
Source: $(PHOBOSSRC std/experimental/allocator/building_blocks/kernighan_ritchie.d)
*/
module std.experimental.allocator.building_blocks.kernighan_ritchie;
import std.experimental.allocator.building_blocks.null_allocator :
NullAllocator;
import std.experimental.allocator.building_blocks.null_allocator;

//debug = KRRegion;
debug(KRRegion) import std.stdio;
Expand Down Expand Up @@ -111,18 +110,21 @@ struct KRRegion(ParentAllocator = NullAllocator)

this(this) @disable;

pure nothrow @trusted @nogc
void[] payload() inout
{
return (cast(ubyte*) &this)[0 .. size];
}

pure nothrow @trusted @nogc
bool adjacent(in Node* right) const
{
assert(right);
auto p = payload;
return p.ptr < right && right < p.ptr + p.length + Node.sizeof;
}

pure nothrow @trusted @nogc
bool coalesce(void* memoryEnd = null)
{
// Coalesce the last node before the memory end with any possible gap
Expand All @@ -139,6 +141,7 @@ struct KRRegion(ParentAllocator = NullAllocator)
return true;
}

@safe
Tuple!(void[], Node*) allocateHere(size_t bytes)
{
assert(bytes >= Node.sizeof);
Expand All @@ -152,7 +155,7 @@ struct KRRegion(ParentAllocator = NullAllocator)
if (leftover >= Node.sizeof)
{
// There's room for another node
auto newNode = cast(Node*) ((cast(ubyte*) &this) + bytes);
auto newNode = (() @trusted => cast(Node*) ((cast(ubyte*) &this) + bytes))();
newNode.size = leftover;
newNode.next = next == &this ? newNode : next;
assert(next);
Expand Down Expand Up @@ -358,7 +361,7 @@ struct KRRegion(ParentAllocator = NullAllocator)
/// Ditto
static if (!is(ParentAllocator == NullAllocator)
&& hasMember!(ParentAllocator, "deallocate"))
~this()
@trusted ~this()
{
parent.deallocate(payload);
}
Expand Down Expand Up @@ -396,6 +399,7 @@ struct KRRegion(ParentAllocator = NullAllocator)

Returns: A word-aligned buffer of `n` bytes, or `null`.
*/
@safe
void[] allocate(size_t n)
{
if (!n || !root) return null;
Expand All @@ -413,7 +417,7 @@ struct KRRegion(ParentAllocator = NullAllocator)
immutable balance = root.size - actualBytes;
if (balance >= Node.sizeof)
{
auto newRoot = cast(Node*) (result + actualBytes);
auto newRoot = (() @trusted => cast(Node*) (result + actualBytes))();
newRoot.next = root.next;
newRoot.size = balance;
root = newRoot;
Expand All @@ -423,7 +427,7 @@ struct KRRegion(ParentAllocator = NullAllocator)
root = null;
switchToFreeList;
}
return result[0 .. n];
return (() @trusted => result[0 .. n])();
}

// Not enough memory, switch to freelist mode and fall through
Expand Down Expand Up @@ -554,6 +558,7 @@ struct KRRegion(ParentAllocator = NullAllocator)
at the front of the free list. These blocks get coalesced, whether
`allocateAll` succeeds or fails due to fragmentation.
*/

void[] allocateAll()
{
if (regionMode) switchToFreeList;
Expand Down Expand Up @@ -647,7 +652,7 @@ fronting the GC allocator.
import std.experimental.allocator.gc_allocator : GCAllocator;
import std.typecons : Ternary;
// KRRegion fronting a general-purpose allocator
align(KRRegion!().alignment) ubyte[1024 * 128] buf;
ubyte[1024 * 128] buf;
auto alloc = fallbackAllocator(KRRegion!()(buf), GCAllocator.instance);
auto b = alloc.allocate(100);
assert(b.length == 100);
Expand All @@ -669,6 +674,7 @@ it actually returns memory to the operating system when possible.
import std.algorithm.comparison : max;
import std.experimental.allocator.building_blocks.allocator_list
: AllocatorList;
import std.experimental.allocator.gc_allocator : GCAllocator;
import std.experimental.allocator.mmap_allocator : MmapAllocator;
AllocatorList!(n => KRRegion!MmapAllocator(max(n * 16, 1024 * 1024))) alloc;
}
Expand All @@ -678,6 +684,7 @@ it actually returns memory to the operating system when possible.
import std.algorithm.comparison : max;
import std.experimental.allocator.building_blocks.allocator_list
: AllocatorList;
import std.experimental.allocator.gc_allocator : GCAllocator;
import std.experimental.allocator.mallocator : Mallocator;
import std.typecons : Ternary;
/*
Expand Down Expand Up @@ -710,6 +717,7 @@ it actually returns memory to the operating system when possible.
import std.algorithm.comparison : max;
import std.experimental.allocator.building_blocks.allocator_list
: AllocatorList;
import std.experimental.allocator.gc_allocator : GCAllocator;
import std.experimental.allocator.mmap_allocator : MmapAllocator;
import std.typecons : Ternary;
/*
Expand Down Expand Up @@ -742,7 +750,6 @@ it actually returns memory to the operating system when possible.
}
}

version (StdUnittest)
@system unittest
{
import std.algorithm.comparison : max;
Expand All @@ -754,16 +761,17 @@ version (StdUnittest)
n => KRRegion!GCAllocator(max(n * 16, 1024 * 1024)))());
}

@system unittest
@trusted unittest
{
import std.experimental.allocator.gc_allocator : GCAllocator;

auto alloc = KRRegion!GCAllocator(1024 * 1024);



void[][] array;
foreach (i; 1 .. 4)
{
array ~= alloc.allocate(i);
array ~= (() nothrow @safe => alloc.allocate(i))();
assert(array[$ - 1].length == i);
}
() nothrow @nogc { alloc.deallocate(array[1]); }();
Expand All @@ -778,11 +786,11 @@ version (StdUnittest)
import std.typecons : Ternary;
auto alloc = KRRegion!()(
cast(ubyte[])(GCAllocator.instance.allocate(1024 * 1024)));
const store = alloc.allocate(KRRegion!().sizeof);
const store = (() pure nothrow @safe @nogc => alloc.allocate(KRRegion!().sizeof))();
auto p = cast(KRRegion!()* ) store.ptr;
import core.lifetime : emplace;
import core.stdc.string : memcpy;
import std.conv : text;
import std.algorithm.mutation : move;
import std.conv : text, emplace;

memcpy(p, &alloc, alloc.sizeof);
emplace(&alloc);
Expand All @@ -791,7 +799,7 @@ version (StdUnittest)
foreach (i; 0 .. array.length)
{
auto length = 100 * i + 1;
array[i] = p.allocate(length);
array[i] = (() pure nothrow @safe @nogc => p.allocate(length))();
assert(array[i].length == length, text(array[i].length));
assert((() pure nothrow @safe @nogc => p.owns(array[i]))() == Ternary.yes);
}
Expand Down Expand Up @@ -820,14 +828,16 @@ version (StdUnittest)
assert(p.length == 1024 * 1024);
}


@system unittest
{
import std.random : randomCover;
import std.experimental.allocator.building_blocks;
import std.random;
import std.typecons : Ternary;

// Both sequences must work on either system

// A sequence of allocs which generates the error described in https://issues.dlang.org/show_bug.cgi?id=16564
// A sequence of allocs which generates the error described in issue 16564
// that is a gap at the end of buf from the perspective of the allocator

// for 64 bit systems (leftover balance = 8 bytes < 16)
Expand All @@ -837,16 +847,16 @@ version (StdUnittest)
int[] sizes32 = [81412, 107068, 49892, 23768];


void test(int[] sizes)
@system void test(int[] sizes)
{
align(size_t.sizeof) ubyte[256 * 1024] buf;
auto a = KRRegion!()(buf);
auto a = (() @trusted => createAllocator(buf))();

void[][] bufs;

foreach (size; sizes)
{
bufs ~= a.allocate(size);
bufs ~= (() pure nothrow @safe @nogc => a.allocate(size))();
}

foreach (b; bufs.randomCover)
Expand All @@ -857,12 +867,22 @@ version (StdUnittest)
assert((() pure nothrow @safe @nogc => a.empty)() == Ternary.yes);
}

test(sizes64);
test(sizes32);
() @trusted {
test(sizes64);
test(sizes32);
}();
}

@system unittest
@system KRRegion!NullAllocator createAllocator(ubyte[] buf)
{
return KRRegion!NullAllocator(buf);
}


@safe unittest
{
import std.experimental.allocator.building_blocks;
import std.random;
import std.typecons : Ternary;

// For 64 bits, we allocate in multiples of 8, but the minimum alloc size is 16.
Expand All @@ -886,11 +906,11 @@ version (StdUnittest)

foreach (size; sizes)
{
bufs ~= a.allocate(size);
bufs ~= (() pure nothrow @safe @nogc => a.allocate(size))();
}

() nothrow @nogc { a.deallocate(bufs[1]); }();
bufs ~= a.allocate(sizes[1] - word);
bufs ~= (() pure nothrow @safe @nogc => a.allocate(sizes[1] - word))();

() nothrow @nogc { a.deallocate(bufs[0]); }();
foreach (i; 2 .. bufs.length)
Expand All @@ -916,7 +936,7 @@ version (StdUnittest)
@system unittest
{ import std.typecons : Ternary;

align(KRRegion!().alignment) ubyte[1024] b;
ubyte[1024] b;
auto alloc = KRRegion!()(b);

auto k = alloc.allocate(128);
Expand Down
Loading