Skip to content

[Property Editor] Property Editor is unresponsive after computer goes to sleep #9028

Closed

Description

@elliette

If you leave a project open in VS Code with the property editor open, it stops working when you come back to it the next day. I'm guessing it's because we've lost connection with the DTD, but will need to investigate more to confirm.

I think at the very least we should show a disconnected screen to the user with instructions on how to reconnect. Ideally we should automatically reconnect for them.

Activity

  1. added
    P1high priority issues at the top of the work list, actively being worked on.
    on Mar 13, 2025
  2. elliette commented on Mar 13, 2025

    @elliette
    MemberAuthor

    FYI @DanTup - I need to figure out if there is a way to reproduce that doesn't involve having to wait a few hours, but I wouldn't be surprised if this involves work in both DevTools and the VS Code extension. Will update here once I have more details.

  3. DanTup commented on Mar 13, 2025

    @DanTup
    Contributor

    it stops working

    Can you describe how it stops working? Are there exceptions? Are you no longer getting the events from the editor?

    There are some log files you can enable in your VS Code settings that might help (for example if the issue is with DTD, there's a log for that):

    "dart.extensionLogFile": "D:\\Logs\\${workspaceName}_extension.txt",
    "dart.analyzerLogFile": "D:\\Logs\\${workspaceName}_analyzer.txt",
    "dart.toolingDaemonLogFile": "D:\\Logs\\${workspaceName}_tooling_daemon.txt",
    
  4. elliette commented on Mar 13, 2025

    @elliette
    MemberAuthor

    Can you describe how it stops working? Are there exceptions? Are you no longer getting the events from the editor?

    The Property Editor no longer responds to cursor position changes, and trying to trigger an edit results in an error.

    Image

    There are some log files you can enable in your VS Code settings that might help

    Ah that is helpful! I was able to re-trigger this with logs enabled (emailed you). As of yet, not seeing anything to indicate this is a VS Code extension issue.

  5. elliette commented on Mar 14, 2025

    @elliette
    MemberAuthor

    Some notes so that I don't forget over the weekend:

    This change along with exposing the following getter in DTD (described below) appear to keep the Property Editor alive.

    I initially tried listening for DTD's done future (and reconnecting when done), however that did not work. Chrome puts the frame to sleep, and so the done future would be completed while the frame was asleep, and therefore we would never reconnect to DTD.

    By pinging, even though the pings stop when Chrome puts the frame to sleep, when it is activated again, we begin pinging once more. When that happens, we can then handle reconnecting to DTD.

    DTD changes:

    Add the following getter:

      /// Whether or not the connection is closed.
      bool get isClosed => _clientPeer.isClosed;
    
  6. elliette commented on Mar 18, 2025

    @elliette
    MemberAuthor

    Unfortunately the solution described above works to keep the Property Editor alive for longer than before, but not enough to keep it alive overnight. After that, we get the following error when calling one of the APIs indicating that the websocket has closed: https://github.com/dart-lang/tools/blob/b55643dadafd3ac6b2bd20823802f75929ebf98e/pkgs/json_rpc_2/lib/src/client.dart#L151

    Chatted with @DanTup offline, he suggested setting the pingInterval to try to keep the WebSocket alive: https://api.dart.dev/dart-io/WebSocket/pingInterval.html

    I believe this will require changes to package:json_rpc_2, but need investigate more here.

    For now, my plan is:

    1. Periodically check that we can send a request to DTD, and if not force the IFRAME to reload
    2. Update DTD to add the isClosed getter (described above)
    3. Instead of checking that we can send a request to DTD, check that DTD is not closed
    4. Hopefully remove the reload logic by using pingInterval or some other mechanism to keep the websocket alive
  7. DanTup commented on Mar 20, 2025

    @DanTup
    Contributor

    Chatted with @DanTup offline, he suggested setting the pingInterval to try to keep the WebSocket alive: https://api.dart.dev/dart-io/WebSocket/pingInterval.html

    I believe this will require changes to package:json_rpc_2, but need investigate more here.

    I think the WebSocket is available here:

    https://github.com/dart-lang/http/blob/1b6e28d7e1c61f7b0f8561be7aee9c35b03de193/pkgs/web_socket_channel/lib/src/channel.dart#L107-L108

    However it doesn't seem to be accessible later. I wonder if adding a pingInterval parameter to WebSocket.connect would be reasonable?

    I haven't tested this, but you could possibly patch DTD to inline some of those constructors so you have access to the raw dart:io WebSocket to set pingInterval to see if it would help:

    diff --git a/pkg/dtd/lib/src/dart_tooling_daemon.dart b/pkg/dtd/lib/src/dart_tooling_daemon.dart
    index 57d6024d5a7..83e00d9da7f 100644
    --- a/pkg/dtd/lib/src/dart_tooling_daemon.dart
    +++ b/pkg/dtd/lib/src/dart_tooling_daemon.dart
    @@ -4,10 +4,13 @@
     
     import 'dart:async';
     import 'dart:convert';
    +import 'dart:io';
     
     import 'package:json_rpc_2/json_rpc_2.dart';
     import 'package:stream_channel/stream_channel.dart';
    -import 'package:web_socket_channel/web_socket_channel.dart';
    +import 'package:web_socket/io_web_socket.dart' show IOWebSocket;
    +import 'package:web_socket_channel/adapter_web_socket_channel.dart'
    +    show AdapterWebSocketChannel;
     
     import '../dtd.dart';
     
    @@ -56,7 +59,11 @@ class DartToolingDaemon {
       /// final client = DartToolingDaemon.connect(uri);
       /// ```
       static Future<DartToolingDaemon> connect(Uri uri) async {
    -    final channel = WebSocketChannel.connect(uri);
    +    final webSocket = await WebSocket.connect(uri.toString());
    +    webSocket.pingInterval = const Duration(minutes: 1);                  // We can set this here
    +    final ioSocket = IOWebSocket.fromWebSocket(webSocket);
    +    final channel = AdapterWebSocketChannel(ioSocket);
    +
         await channel.ready;
         return DartToolingDaemon.fromStreamChannel(channel.cast<String>());
       }
    diff --git a/pkg/dtd/pubspec.yaml b/pkg/dtd/pubspec.yaml
    index 5c1775758d2..c6117e8c3f8 100644
    --- a/pkg/dtd/pubspec.yaml
    +++ b/pkg/dtd/pubspec.yaml
    @@ -10,6 +10,7 @@ dependencies:
       json_rpc_2: ^3.0.2
       stream_channel: ^2.1.2
       unified_analytics: ^7.0.0
    +  web_socket:
       web_socket_channel: '>=2.0.0 <4.0.0'
     
     # We use 'any' version constraints here as we get our package versions from
  8. derekxu16 commented on Mar 20, 2025

    @derekxu16
    Contributor

    Here from the DTD CL, just trying to get a clearer understanding of the problem. If the Future resolves in a sleeping tab, does that mean the program in the tab is still running? If so, shouldn't code to reconnect when the Future resolves still run in the tab too? Maybe the problem with that is networking is blocked in sleeping tabs. If so, can you set a dtdIsConnectionIsClosed bool when the Future resolves instead of checking dtd.isClosed?

  9. elliette commented on Mar 20, 2025

    @elliette
    MemberAuthor

    If the Future resolves in a sleeping tab, does that mean the program in the tab is still running?

    No, I don't think the program in the tab is still running. Chrome puts all processes in the tab to sleep - for example, I see that the pings I've added using Timer stop, and re-start again once the tab is re-awoken.

    If so, shouldn't code to reconnect when the Future resolves still run in the tab too? Maybe the problem with that is networking is blocked in sleeping tabs. If so, can you set a dtdIsConnectionIsClosed bool when the Future resolves instead of checking dtd.isClosed?

    I initially tried the following in DevTools (see below), however _attemptDtdReconnection never ended up getting called. However, when the tab was re-awoken, the DTD connection was closed. This is why I don't think waiting on the Future is sufficient here.

    _connectionClosedSubscription = Stream.fromFuture(dtdConnection.done)
       .listen(_attemptDtdReconnection);
    
  10. derekxu16 commented on Mar 20, 2025

    @derekxu16
    Contributor

    I think that _attemptDtdReconnection not getting called means that the Future never resolves. So I think the problem is that if package:json_rpc_2isn't running (e.g. is in a sleeping tab) when the disconnection happens, it never resolves the Future. There's probably a way to fix this, but adding dtd.isClosed to get around it works for me.

  11. added a commit that references this issue on Mar 20, 2025
  12. changed the title [-][Property Editor] Does not work when left overnight[/-] [+][Property Editor] Property Editor is unresponsive after computer goes to sleep[/+] on Apr 2, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    P1high priority issues at the top of the work list, actively being worked on.devtools-property-editor

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions