vibe-d / vibe-core

Repository for the next generation of vibe.d's core package.
MIT License
61 stars 46 forks source link

`FreeListRef.checkInvariant`'s `assert` fails since v2.096 (dtorfields on by default) #283

Open Geod24 opened 3 years ago

Geod24 commented 3 years ago

Situation: A client connects to a node. The node crashes (e.g. SEGV) or just abruptly drops the connection.

The following error is triggeredm client side:

object.Exception@../../submodules/vibe.d/stream/vibe/stream/operations.d(378): Reached EOF while searching for end marker.
----------------
??:? pure @safe void std.exception.bailOut!(Exception).bailOut(immutable(char)[], ulong, scope const(char)[]) [0x10040e51a]
??:? pure @safe bool std.exception.enforce!().enforce!(bool).enforce(bool, lazy const(char)[], immutable(char)[], ulong) [0x10040e48b]
source/main.d:125 @safe void vibe.stream.operations.readUntilSmall!(vibe.utils.array.AllocAppender!(ubyte[], ubyte).AllocAppender, vibe.internal.interfaceproxy.InterfaceProxy!(vibe.core.stream.Stream).InterfaceProxy).readUntilSmall(vibe.internal.interfaceproxy.InterfaceProxy!(vibe.core.stream.Stream).InterfaceProxy, ref vibe.utils.array.AllocAppender!(ubyte[], ubyte).AllocAppender, in ubyte[], ulong) [0x1002b01a3]
source/main.d:125 @safe void vibe.stream.operations.readUntil!(vibe.utils.array.AllocAppender!(ubyte[], ubyte).AllocAppender, vibe.internal.interfaceproxy.InterfaceProxy!(vibe.core.stream.Stream).InterfaceProxy).readUntil(vibe.internal.interfaceproxy.InterfaceProxy!(vibe.core.stream.Stream).InterfaceProxy, ref vibe.utils.array.AllocAppender!(ubyte[], ubyte).AllocAppender, in ubyte[], ulong) [0x1002b0073]
source/main.d:125 @safe void vibe.stream.operations.readLine!(vibe.utils.array.AllocAppender!(ubyte[], ubyte).AllocAppender, vibe.internal.interfaceproxy.InterfaceProxy!(vibe.core.stream.Stream).InterfaceProxy).readLine(vibe.internal.interfaceproxy.InterfaceProxy!(vibe.core.stream.Stream).InterfaceProxy, ref vibe.utils.array.AllocAppender!(ubyte[], ubyte).AllocAppender, ulong, immutable(char)[]) [0x1002aff94]
source/main.d:125 @safe ubyte[] vibe.stream.operations.readLine!(vibe.internal.interfaceproxy.InterfaceProxy!(vibe.core.stream.Stream).InterfaceProxy).readLine(vibe.internal.interfaceproxy.InterfaceProxy!(vibe.core.stream.Stream).InterfaceProxy, ulong, immutable(char)[], stdx.allocator.IAllocator) [0x1002afeb4]
source/main.d:125 @trusted immutable(char)[] vibe.http.client.HTTPClientResponse.__ctor(vibe.http.client.HTTPClient, bool, bool, stdx.allocator.IAllocator, std.datetime.systime.SysTime).__lambda6() [0x1003207c1]
source/main.d:125 @safe vibe.http.client.HTTPClientResponse vibe.http.client.HTTPClientResponse.__ctor(vibe.http.client.HTTPClient, bool, bool, stdx.allocator.IAllocator, std.datetime.systime.SysTime) [0x10031fe36]
source/main.d:125 @safe vibe.http.client.HTTPClientResponse vibe.http.client.HTTPClient.request(scope void delegate(vibe.http.client.HTTPClientRequest)) [0x10031d6d2]
source/main.d:125 @safe vibe.http.client.HTTPClientResponse vibe.http.client.requestHTTP(vibe.inet.url.URL, void delegate(scope vibe.http.client.HTTPClientRequest), const(vibe.http.client.HTTPClientSettings)) [0x10031b823]
source/main.d:125 @safe vibe.http.client.HTTPClientResponse vibe.web.rest.request(vibe.inet.url.URL, void delegate(vibe.http.client.HTTPClientRequest) @safe, scope void delegate(vibe.http.client.HTTPClientRequest, scope vibe.core.stream.InputStream) @safe, vibe.http.common.HTTPMethod, immutable(char)[], scope ref const(vibe.utils.dictionarylist.DictionaryList!(immutable(char)[], false, 12uL, false).DictionaryList), immutable(char)[], immutable(char)[], ref vibe.utils.dictionarylist.DictionaryList!(immutable(char)[], false, 12uL, false).DictionaryList, ref vibe.utils.dictionarylist.DictionaryList!(immutable(char)[], false, 12uL, false).DictionaryList, in vibe.http.client.HTTPClientSettings) [0x1002c5953]
source/main.d:125 @safe ulong vibe.web.rest.executeClientMethod!(agora.api.FullNode.API, 3).executeClientMethod(scope ref const(vibe.web.internal.rest.common.RestInterface!(agora.api.FullNode.API).RestInterface), void delegate(vibe.http.client.HTTPClientRequest) @safe, scope void delegate(vibe.http.client.HTTPClientRequest, scope vibe.core.stream.InputStream) @safe) [0x1002cd322]
source/main.d:125 @safe ulong vibe.web.rest.RestInterfaceClient!(agora.api.FullNode.API).RestInterfaceClient.__mixin23.getBlockHeight() [0x1002c68c2]
source/main.d:87 void main.main(immutable(char)[][]).assertBlockHeightAtleast(ulong) [0x100006436]
source/main.d:125 void main.main(immutable(char)[][]).__lambda14!(int).__lambda14(int) [0x10000be9b]
source/main.d:125 std.typecons.Flag!("each").Flag std.algorithm.iteration.each!(main.main(immutable(char)[][]).__lambda14).each!(std.range.iota!(int, int).iota(int, int).Result).each(std.range.iota!(int, int).iota(int, int).Result) [0x10000bed0]
source/main.d:125 _Dmain [0x1000027d9]
Process 60969 exited with status = 1 (0x00000001)

(I'm on OSX, so line numbers are botched) However, what was also triggered was an InvalidMemoryOperationError. Using LLDB, I could see that the InvalidMemoryOperationError was triggered by an assert triggering in a destructor, and assert allocating on failure. This is an upstream bug (reported in #248 ) and will be fixed in the next release (v2.097.0).

The assertion in question, obviously, shouldn't fail: https://github.com/vibe-d/vibe-core/blob/519d1c0fe7c0e5c5fcde9f72ecc4fefa207df109/source/vibe/internal/freelistref.d#L175

    frame #5: 0x00000001003c1626 systemtest-simple`_d_assertp + 94
    frame #6: 0x00000001001eaf53 systemtest-simple`_D4vibe8internal11freelistref__T11FreeListRefTCQBt6stream8counting22EndCallbackInputStreamVbi1ZQCl15checkInvariantsMxFNaNbNiNfZv at freelistref.d:175
    frame #7: 0x00000001001eae6c systemtest-simple`_D4vibe8internal11freelistref__T11FreeListRefTCQBt6stream8counting22EndCallbackInputStreamVbi1ZQCl5clearMFNbNfZv at freelistref.d:149
    frame #8: 0x00000001001eadbd systemtest-simple`_D4vibe8internal11freelistref__T11FreeListRefTCQBt6stream8counting22EndCallbackInputStreamVbi1ZQCl6__dtorMFNbNfZv at freelistref.d:121
    frame #9: 0x000000010032149c systemtest-simple`_D4vibe4http6client18HTTPClientResponse11__fieldDtorMFNeZv + 44
    frame #10: 0x00000001003214fe systemtest-simple`_D4vibe4http6client18HTTPClientResponse10__aggrDtorMFNeZv + 30
    frame #11: 0x00000001003ea370 systemtest-simple`rt_finalize2 + 112
    frame #12: 0x00000001003ea4ab systemtest-simple`rt_finalizeFromGC + 39

So after going down that rabbit hole, I was left wondering why on earth the assert fails. Luckily I have most major DMD change memorized so I added a -revert=dtorfields to my config and the assert stopped failing. -preview=dtorfields have been enabled by default since v2.096.0 and that matches the timeline of when things started to spuriously fail for us.

s-ludwig commented 3 years ago

There seems to be a double-destruction bug that has been introduced by dtorFields: https://run.dlang.io/is/l0nsn4

Geod24 commented 3 years ago

Thanks for the fast reduction! Reported as https://issues.dlang.org/show_bug.cgi?id=21989 . That's pretty bad :/