From 90a60f7981f7be3820cf110a3331fb65f4e94657 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=B6nke=20Ludwig?= Date: Sun, 21 Oct 2018 20:15:12 +0200 Subject: [PATCH] Add nogc typed alloc/free functions and make assert_nogc actually nogc. --- dub.sdl | 2 +- source/eventcore/internal/utils.d | 98 +++++++++++++++++++++++++++---- 2 files changed, 86 insertions(+), 14 deletions(-) diff --git a/dub.sdl b/dub.sdl index d9ea537..be18279 100644 --- a/dub.sdl +++ b/dub.sdl @@ -8,7 +8,7 @@ targetType "library" libs "anl" "resolv" platform="linux" libs "ws2_32" "user32" platform="windows-dmd" -dependency "taggedalgebraic" version="~>0.10.4" +dependency "taggedalgebraic" version="~>0.10.12" configuration "epoll" { platforms "linux" diff --git a/source/eventcore/internal/utils.d b/source/eventcore/internal/utils.d index c845648..2613522 100644 --- a/source/eventcore/internal/utils.d +++ b/source/eventcore/internal/utils.d @@ -1,5 +1,7 @@ module eventcore.internal.utils; +import core.memory : GC; +import std.traits : hasIndirections; import taggedalgebraic; @@ -8,17 +10,81 @@ void print(ARGS...)(string str, ARGS args) import std.format : formattedWrite; StdoutRange r; scope cb = () { - scope (failure) assert(false); - (&r).formattedWrite(str, args); + try (&r).formattedWrite(str, args); + catch (Exception e) assert(false, e.msg); }; (cast(void delegate() @nogc @safe nothrow)cb)(); r.put('\n'); } +T mallocT(T, ARGS...)(ARGS args) +@trusted @nogc { + import core.stdc.stdlib : malloc; + import std.conv : emplace; + + enum size = __traits(classInstanceSize, T); + auto ret = cast(T)malloc(size); + static if (hasIndirections!T) + GC.addRange(cast(void*)ret, __traits(classInstanceSize, T)); + scope doit = { emplace!T((cast(void*)ret)[0 .. size], args); }; + static if (__traits(compiles, () nothrow { typeof(doit).init(); })) // NOTE: doing the typeof thing here, because LDC 1.7.0 otherwise thinks doit gets escaped here + (cast(void delegate() @nogc nothrow)doit)(); + else + (cast(void delegate() @nogc)doit)(); + return ret; +} + +void freeT(T)(ref T inst) @nogc + if (is(T == class)) +{ + import core.stdc.stdlib : free; + + noGCDestroy(inst); + static if (hasIndirections!T) + GC.removeRange(cast(void*)inst); + free(cast(void*)inst); + inst = null; +} + +T[] mallocNT(T)(size_t cnt) +@trusted { + import core.stdc.stdlib : malloc; + import std.conv : emplace; + + auto ret = (cast(T*)malloc(T.sizeof * cnt))[0 .. cnt]; + static if (hasIndirections!T) + GC.addRange(cast(void*)ret, T.sizeof * cnt); + foreach (ref v; ret) + static if (!is(T == class)) + emplace!T(&v); + else v = null; + return ret; +} + +void freeNT(T)(ref T[] arr) +{ + import core.stdc.stdlib : free; + + foreach (ref v; arr) + static if (!is(T == class)) + destroy(v); + static if (hasIndirections!T) + GC.removeRange(arr.ptr); + free(arr.ptr); + arr = null; +} + +private void noGCDestroy(T)(ref T t) +@trusted { + // FIXME: only do this if the destructor chain is actually nogc + scope doit = { destroy(t); }; + (cast(void delegate() @nogc)doit)(); +} + private extern(C) Throwable.TraceInfo _d_traceContext(void* ptr = null); void nogc_assert(bool cond, string message, string file = __FILE__, int line = __LINE__) -@trusted nothrow { +@trusted nothrow @nogc { import core.stdc.stdlib : abort; import std.stdio : stderr; @@ -28,12 +94,15 @@ void nogc_assert(bool cond, string message, string file = __FILE__, int line = _ assert(false); } - stderr.writefln("Assertion failure @%s(%s): %s", file, line, message); - stderr.writeln("------------------------"); - if (auto info = _d_traceContext(null)) { - foreach (s; info) - stderr.writeln(s); - } else stderr.writeln("no stack trace available"); + scope doit = { + stderr.writefln("Assertion failure @%s(%s): %s", file, line, message); + stderr.writeln("------------------------"); + if (auto info = _d_traceContext(null)) { + foreach (s; info) + stderr.writeln(s); + } else stderr.writeln("no stack trace available"); + }; + (cast(void delegate() @nogc)doit)(); // write and _d_traceContext are not nogc } } @@ -53,14 +122,11 @@ struct StdoutRange { } struct ChoppedVector(T, size_t CHUNK_SIZE = 16*64*1024/nextPOT(T.sizeof)) { - import core.memory : GC; - static assert(nextPOT(CHUNK_SIZE) == CHUNK_SIZE, "CHUNK_SIZE must be a power of two for performance reasons."); @safe: nothrow: import core.stdc.stdlib : calloc, free, malloc, realloc; - import std.traits : hasIndirections; alias chunkSize = CHUNK_SIZE; @@ -183,7 +249,7 @@ struct AlgebraicChoppedVector(TCommon, TSpecific...) import std.format : format; string ret; foreach (i, U; TSpecific) - ret ~= "@property ref TSpecific[%s] %s() nothrow @safe { return this.specific.get!(TSpecific[%s]); }\n" + ret ~= "@property ref TSpecific[%s] %s() nothrow @safe @nogc { return this.specific.get!(TSpecific[%s]); }\n" .format(i, U.Handle.name, i); return ret; } @@ -207,10 +273,13 @@ struct SmallIntegerSet(V : size_t) size_t m_count; } + @disable this(this); + @property bool empty() const { return m_count == 0; } void insert(V i) { + assert(i >= 0); foreach (j; 0 .. m_bits.length) { uint b = 1u << (i%32); i /= 32; @@ -223,6 +292,9 @@ struct SmallIntegerSet(V : size_t) void remove(V i) { + assert(i >= 0); + if (i >= m_bits[0].length * 32) return; + foreach (j; 0 .. m_bits.length) { uint b = 1u << (i%32); i /= 32;