From df78af96bbd0e232de566398c0ecc91326792af6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=B6nke=20Ludwig?= Date: Sun, 11 Mar 2018 12:08:09 +0100 Subject: [PATCH] Use a custom nogc_assert in cleanup code. Unfortunately the built-in assert GC allocates an exception, which means that if called directly or indirectly form a finalizer, the assertion will not become visible and instead an InvalidMemoryOperationError is thrown. This implements a custom nogc_assert() function that directly prints the assertion message and uses abort() to end the process. --- source/eventcore/drivers/posix/events.d | 9 +++++---- source/eventcore/drivers/posix/signals.d | 3 ++- source/eventcore/drivers/posix/sockets.d | 2 +- source/eventcore/drivers/posix/watchers.d | 7 ++++--- source/eventcore/drivers/timer.d | 5 +++-- source/eventcore/drivers/winapi/core.d | 5 +++-- source/eventcore/drivers/winapi/events.d | 3 ++- source/eventcore/drivers/winapi/sockets.d | 4 ++-- source/eventcore/internal/utils.d | 24 ++++++++++++++++++++++- 9 files changed, 45 insertions(+), 17 deletions(-) diff --git a/source/eventcore/drivers/posix/events.d b/source/eventcore/drivers/posix/events.d index c96c060..0cb5b0d 100644 --- a/source/eventcore/drivers/posix/events.d +++ b/source/eventcore/drivers/posix/events.d @@ -4,6 +4,7 @@ module eventcore.drivers.posix.events; import eventcore.driver; import eventcore.drivers.posix.driver; import eventcore.internal.consumablequeue : ConsumableQueue; +import eventcore.internal.utils : nogc_assert; version (linux) { nothrow @nogc extern (C) int eventfd(uint initval, int flags); @@ -199,13 +200,13 @@ final class PosixEventDriverEvents(Loop : PosixEventLoop, Sockets : EventDriverS final override bool releaseRef(EventID descriptor) { - assert(getRC(descriptor) > 0, "Releasing reference to unreferenced event FD."); + nogc_assert(getRC(descriptor) > 0, "Releasing reference to unreferenced event FD."); if (--getRC(descriptor) == 0) { if (!isInternal(descriptor)) m_loop.m_waiterCount -= getSlot(descriptor).waiters.length; () @trusted nothrow { try .destroy(getSlot(descriptor).waiters); - catch (Exception e) assert(false, e.msg); + catch (Exception e) nogc_assert(false, e.msg); } (); version (linux) { m_loop.unregisterFD(descriptor, EventMask.read); @@ -216,7 +217,7 @@ final class PosixEventDriverEvents(Loop : PosixEventLoop, Sockets : EventDriverS try { synchronized (m_eventsMutex) m_events.remove(rs); - } catch (Exception e) assert(false, e.msg); + } catch (Exception e) nogc_assert(false, e.msg); } m_loop.clearFD(descriptor); version (Posix) close(cast(int)descriptor); @@ -228,7 +229,7 @@ final class PosixEventDriverEvents(Loop : PosixEventLoop, Sockets : EventDriverS private EventSlot* getSlot(EventID id) { - assert(id < m_loop.m_fds.length, "Invalid event ID."); + nogc_assert(id < m_loop.m_fds.length, "Invalid event ID."); return () @trusted { return &m_loop.m_fds[id].event(); } (); } diff --git a/source/eventcore/drivers/posix/signals.d b/source/eventcore/drivers/posix/signals.d index cc57398..4a88c08 100644 --- a/source/eventcore/drivers/posix/signals.d +++ b/source/eventcore/drivers/posix/signals.d @@ -3,6 +3,7 @@ module eventcore.drivers.posix.signals; import eventcore.driver; import eventcore.drivers.posix.driver; +import eventcore.internal.utils : nogc_assert; import std.algorithm.comparison : among; @@ -56,7 +57,7 @@ final class SignalFDEventDriverSignals(Loop : PosixEventLoop) : EventDriverSigna override bool releaseRef(SignalListenID descriptor) { FD fd = cast(FD)descriptor; - assert(m_loop.m_fds[fd].common.refCount > 0, "Releasing reference to unreferenced event FD."); + nogc_assert(m_loop.m_fds[fd].common.refCount > 0, "Releasing reference to unreferenced event FD."); if (--m_loop.m_fds[fd].common.refCount == 1) { // NOTE: 1 because setNotifyCallback adds a second reference m_loop.unregisterFD(fd, EventMask.read); m_loop.clearFD(fd); diff --git a/source/eventcore/drivers/posix/sockets.d b/source/eventcore/drivers/posix/sockets.d index 57abd22..d472e44 100644 --- a/source/eventcore/drivers/posix/sockets.d +++ b/source/eventcore/drivers/posix/sockets.d @@ -804,7 +804,7 @@ final class PosixEventDriverSockets(Loop : PosixEventLoop) : EventDriverSockets { import taggedalgebraic : hasType; auto slot = () @trusted { return &m_loop.m_fds[fd]; } (); - assert(slot.common.refCount > 0, "Releasing reference to unreferenced socket FD."); + nogc_assert(slot.common.refCount > 0, "Releasing reference to unreferenced socket FD."); // listening sockets have an incremented the reference count because of setNotifyCallback int base_refcount = slot.specific.hasType!StreamListenSocketSlot ? 1 : 0; if (--slot.common.refCount == base_refcount) { diff --git a/source/eventcore/drivers/posix/watchers.d b/source/eventcore/drivers/posix/watchers.d index 605686a..288af0f 100644 --- a/source/eventcore/drivers/posix/watchers.d +++ b/source/eventcore/drivers/posix/watchers.d @@ -3,6 +3,7 @@ module eventcore.drivers.posix.watchers; import eventcore.driver; import eventcore.drivers.posix.driver; +import eventcore.internal.utils : nogc_assert; final class InotifyEventDriverWatchers(Events : EventDriverEvents) : EventDriverWatchers @@ -64,7 +65,7 @@ final class InotifyEventDriverWatchers(Events : EventDriverEvents) : EventDriver final override bool releaseRef(WatcherID descriptor) { FD fd = cast(FD)descriptor; - assert(m_loop.m_fds[fd].common.refCount > 0, "Releasing reference to unreferenced event FD."); + nogc_assert(m_loop.m_fds[fd].common.refCount > 0, "Releasing reference to unreferenced event FD."); if (--m_loop.m_fds[fd].common.refCount == 1) { // NOTE: 1 because setNotifyCallback increments the reference count m_loop.unregisterFD(fd, EventMask.read); m_loop.clearFD(fd); @@ -281,10 +282,10 @@ final class PollEventDriverWatchers(Events : EventDriverEvents) : EventDriverWat final override bool releaseRef(WatcherID descriptor) { - assert(descriptor != WatcherID.invalid); + nogc_assert(descriptor != WatcherID.invalid, "Invalid directory watcher ID released"); auto evt = cast(EventID)descriptor; auto pt = evt in m_pollers; - assert(pt !is null); + nogc_assert(pt !is null, "Directory watcher polling thread does not exist"); if (!m_events.releaseRef(evt)) { pt.dispose(); m_pollers.remove(evt); diff --git a/source/eventcore/drivers/timer.d b/source/eventcore/drivers/timer.d index 15277ea..2e1a420 100644 --- a/source/eventcore/drivers/timer.d +++ b/source/eventcore/drivers/timer.d @@ -5,6 +5,7 @@ module eventcore.drivers.timer; import eventcore.driver; import eventcore.internal.dlist; +import eventcore.internal.utils : nogc_assert; final class LoopTimeoutTimerDriver : EventDriverTimers { @@ -143,8 +144,8 @@ final class LoopTimeoutTimerDriver : EventDriverTimers { final override bool releaseRef(TimerID descriptor) { - assert(descriptor != TimerID.init, "Invalid timer ID."); - assert(descriptor in m_timers, "Unknown timer ID."); + nogc_assert(descriptor != TimerID.init, "Invalid timer ID."); + nogc_assert((descriptor in m_timers) !is null, "Unknown timer ID."); if (descriptor !in m_timers) return true; auto tm = m_timers[descriptor]; diff --git a/source/eventcore/drivers/winapi/core.d b/source/eventcore/drivers/winapi/core.d index f485e9f..6dac979 100644 --- a/source/eventcore/drivers/winapi/core.d +++ b/source/eventcore/drivers/winapi/core.d @@ -5,6 +5,7 @@ version (Windows): import eventcore.driver; import eventcore.drivers.timer; import eventcore.internal.consumablequeue; +import eventcore.internal.utils : nogc_assert; import eventcore.internal.win32; import core.time : Duration; import taggedalgebraic; @@ -180,7 +181,7 @@ final class WinAPIEventDriverCore : EventDriverCore { package void freeSlot(HANDLE h) { - assert(h in m_handles, "Handle not in use - cannot free."); + nogc_assert((h in m_handles) !is null, "Handle not in use - cannot free."); m_handles.remove(h); } } @@ -214,7 +215,7 @@ private struct HandleSlot { bool releaseRef(scope void delegate() @safe nothrow on_free) { - assert(refCount > 0); + nogc_assert(refCount > 0, "Releasing unreferenced slot."); if (--refCount == 0) { on_free(); return false; diff --git a/source/eventcore/drivers/winapi/events.d b/source/eventcore/drivers/winapi/events.d index c39049a..e71c6cb 100644 --- a/source/eventcore/drivers/winapi/events.d +++ b/source/eventcore/drivers/winapi/events.d @@ -6,6 +6,7 @@ import eventcore.driver; import eventcore.drivers.winapi.core; import eventcore.internal.win32; import eventcore.internal.consumablequeue; +import eventcore.internal.utils : nogc_assert; final class WinAPIEventDriverEvents : EventDriverEvents { @@ -108,7 +109,7 @@ final class WinAPIEventDriverEvents : EventDriverEvents { override bool releaseRef(EventID descriptor) { auto pe = descriptor in m_events; - assert(pe.refCount > 0); + nogc_assert(pe.refCount > 0, "Releasing unreference event."); if (--pe.refCount == 0) { // make sure to not leak any waiter references for pending waits foreach (i; 0 .. pe.waiters.length) diff --git a/source/eventcore/drivers/winapi/sockets.d b/source/eventcore/drivers/winapi/sockets.d index 22ed186..8e5eba3 100644 --- a/source/eventcore/drivers/winapi/sockets.d +++ b/source/eventcore/drivers/winapi/sockets.d @@ -5,7 +5,7 @@ version (Windows): import eventcore.driver; import eventcore.drivers.winapi.core; import eventcore.internal.win32; -import eventcore.internal.utils : AlgebraicChoppedVector, print; +import eventcore.internal.utils : AlgebraicChoppedVector, print, nogc_assert; import std.socket : Address; private enum WM_USER_SOCKET = WM_USER + 1; @@ -697,7 +697,7 @@ final class WinAPIEventDriverSockets : EventDriverSockets { override bool releaseRef(SocketFD fd) { import taggedalgebraic : hasType; - assert(m_sockets[fd].common.refCount > 0, "Releasing reference to unreferenced socket FD."); + nogc_assert(m_sockets[fd].common.refCount > 0, "Releasing reference to unreferenced socket FD."); if (--m_sockets[fd].common.refCount == 0) { final switch (m_sockets[fd].specific.kind) with (SocketVector.FieldType) { case Kind.none: break; diff --git a/source/eventcore/internal/utils.d b/source/eventcore/internal/utils.d index 70b827e..4f56aab 100644 --- a/source/eventcore/internal/utils.d +++ b/source/eventcore/internal/utils.d @@ -15,6 +15,28 @@ void print(ARGS...)(string str, ARGS args) r.put('\n'); } +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 { + import core.stdc.stdlib : abort; + import std.stdio : stderr; + + if (!cond) { + scope (exit) { + abort(); + 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"); + } +} + struct StdoutRange { @safe: @nogc: nothrow: import core.stdc.stdio; @@ -124,7 +146,7 @@ struct ChoppedVector(T, size_t CHUNK_SIZE = 16*64*1024/nextPOT(T.sizeof)) { } while (m_chunkCount <= chunkidx) { - () @trusted { + () @trusted { auto ptr = cast(ChunkPtr)calloc(chunkSize, T.sizeof); assert(ptr !is null, "Failed to allocate chunk!"); // FIXME: initialize with T.init instead of 0