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

WebSocket segfaults if connection to server drops out #2706

Open
LunaTheFoxgirl opened this issue Feb 1, 2023 · 7 comments
Open

WebSocket segfaults if connection to server drops out #2706

LunaTheFoxgirl opened this issue Feb 1, 2023 · 7 comments

Comments

@LunaTheFoxgirl
Copy link
Contributor

LunaTheFoxgirl commented Feb 1, 2023

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
@s-ludwig
Copy link
Member

s-ludwig commented Feb 1, 2023

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.

@LunaTheFoxgirl
Copy link
Contributor Author

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

@s-ludwig
Copy link
Member

s-ludwig commented Feb 1, 2023

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.

@LunaTheFoxgirl
Copy link
Contributor Author

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.

@s-ludwig
Copy link
Member

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.

@LunaTheFoxgirl
Copy link
Contributor Author

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

@s-ludwig
Copy link
Member

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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants