vibe-d / vibe.d

Official vibe.d development

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

WebSocket segfaults if connection to server drops out

LunaTheFoxgirl opened this issue · comments

commented

Currently working on https://github.com/Inochi2D/vts-d

The example program segfaults if VTube Studio is closed and the WebSocket Server as a result stops sending data or if for some other reason data doesn't arrive properly.
In the log there's a WARNING: HTTPClientResponse not fully processed before being finalized warning before the crash

Example Program

module app;

import vts;
import std.datetime;
import std.stdio : writeln;
import std.random : choice;
import vibe.core.core : sleep;

void main() {
    VTSPlugin plugin = new VTSPlugin(PluginInfo("Test", "Me", null), "127.0.0.1");
    plugin.login();

    auto models = plugin.getModels();
    do {
        if (models.length > 0) plugin.tryLoadModel(choice(models).modelId);
        
        sleep(5.seconds);
    } while(plugin.isConnected());

    plugin.disconnect();
}

LLDB Backtrace

* thread #1, queue = 'com.apple.main-thread', stop reason = EXC_BAD_ACCESS (code=1, address=0x10)
    frame #0: 0x00000001005e6d70 vts-d`_D6object14TypeInfo_Class7getHashMxFNbNeMxPvZm + 12
vts-d`_D6object14TypeInfo_Class7getHashMxFNbNeMxPvZm:
->  0x1005e6d70 <+12>: ldr    x1, [x8, #0x10]
    0x1005e6d74 <+16>: br     x1
    0x1005e6d78 <+20>: ret    

vts-d`_D6object14TypeInfo_Class6equalsMxFIPvIQdZb:
    0x1005e6d7c <+0>:  ldr    x0, [x1]
Target 0: (vts-d) stopped.
(lldb) bt
* thread #1, queue = 'com.apple.main-thread', stop reason = EXC_BAD_ACCESS (code=1, address=0x10)
  * frame #0: 0x00000001005e6d70 vts-d`_D6object14TypeInfo_Class7getHashMxFNbNeMxPvZm + 12
    frame #1: 0x00000001005eb740 vts-d`_aaInX + 96
    frame #2: 0x00000001000ab898 vts-d`_D4vibe4core14connectionpool__T16LockedConnectionTCQBx4http6client10HTTPClientZQBw6__dtorMFNfZv(this=0x00000001025b6300) at connectionpool.d:277:4
    frame #3: 0x00000001000b2a88 vts-d`_D4vibe4http6client18HTTPClientResponse11__fieldDtorMFNeZv(this=0x00000001025bb000) at client.d:1091:2
    frame #4: 0x00000001000b41cc vts-d`_D4vibe4http6client18HTTPClientResponse10__aggrDtorMFNeZv(this=0x00000001025bb000) at client.d:1091:2
    frame #5: 0x00000001005f2ff8 vts-d`rt_finalize2 + 100
    frame #6: 0x00000001005d348c vts-d`_D4core8internal2gc4impl12conservativeQw3Gcx5sweepMFNbZm + 1120
    frame #7: 0x00000001005d031c vts-d`_D4core8internal2gc4impl12conservativeQw3Gcx11fullcollectMFNbbbbZm + 984
    frame #8: 0x00000001005d065c vts-d`_D4core8internal2gc4impl12conservativeQw14ConservativeGC__T9runLockedS_DQCsQCqQCkQCkQCiQCtQBy18fullCollectNoStackMFNbZ2goFNbPSQEuQEsQEmQEmQEkQEv3GcxZmTQBbZQDsMFNbKQBnZm + 76
    frame #9: 0x00000001005d88a4 vts-d`gc_term + 140
    frame #10: 0x00000001005eed74 vts-d`rt_term + 72
    frame #11: 0x00000001005ef2f4 vts-d`_D2rt6dmain212_d_run_main2UAAamPUQgZiZ6runAllMFZv + 168
    frame #12: 0x00000001005ef070 vts-d`_d_run_main2 + 376
    frame #13: 0x00000001005eeedc vts-d`_d_run_main + 148
    frame #14: 0x0000000100030cc8 vts-d`main(argc=1, argv=0x000000016fdff4a8) at entrypoint.d:42:17
    frame #15: 0x0000000101c79088 dyld`start + 516

It looks like this is using the overload of requestHTTP that returns the response instead of the one that passes it as scope to a callback. If that call is directly in your code, you could probably replace it with the scope version to work around the crash.

For the crash itself, it looks like might be an order-of-destruction issue, where the GC destroys the referenced ConnectionPool prior to the LockedConnection that is as part of the HTTPClientResponse. I'm not sure whether there is a simple fix (backwards compatible) within the library to avoid that, or if this is an obligation that needs to be passed to the user for now.

In the latter case, it would be necessary to catch the exception, either directly, or indirectly using a scope (failure) guard, and call HTTPClientResponse.disconnect() to ensure the response object frees any associated resources in time.

commented

It looks like this is using the overload of requestHTTP that returns the response instead of the one that passes it as scope to a callback. If that call is directly in your code, you could probably replace it with the scope version to work around the crash.

Sadly no, I do not directly make the requestHTTP call, that is being done by connectWebSocket

Okay, then there is also an overload of connectWebSocket that takes a WebSocketHandshakeDelegate that internally uses the other connectHTTP overload. I'll have a look at the exception handling in the web socket module later.

commented

From what I can tell, I can't use the scope callback as the socket has to be a long lived object that is queried in a loop in things like games.

Having it long lived shouldn't be an issue, the task is light-weight and as long as it doesn't perform heavy computations, it shouldn't interfere with anything else in the main thread. The task could then be connected to other tasks/threads by establishing a two-way communication channel, which, in contrast to std.concurrency, is strongly typed and doesn't allocate for each message.

commented

I would need to figure out how to start/stop it on command, seems difficult considering how scope will destroy the object after it exits the scope

Channel has a close method and is reference counted (a copy would exist on both sides), so that could be used to safely communicate the end of the connection/scope.