dart-lang / sdk

The Dart SDK, including the VM, JS and Wasm compilers, analysis, core libraries, and more.
https://dart.dev
BSD 3-Clause "New" or "Revised" License
10.22k stars 1.57k forks source link

Isolate.errors do not send done event #27987

Open alsemenov opened 7 years ago

alsemenov commented 7 years ago

The description for Isolate.errors reads:

Stream errors Returns a broadcast stream of uncaught errors from the isolate.

Each error is provided as an error event on the stream.

The actual error object and stackTraces will not necessarily be the same object types as in the actual isolate, but they will always have the same Object.toString result.

This stream is based on addErrorListener and removeErrorListener.

Although it is not stated explicitly I would expect the done event in the errors stream, when the isolate instance terminates. The following code demonstrates the problem:

import "dart:isolate";
import "dart:async";

void entryPoint(SendPort sendPort) {
  ReceivePort receivePort = new ReceivePort();
  dynamic i = 1;
  StreamSubscription ss;
  ss = receivePort.listen(
     (x) {
       print("received $x");
       if (x == "!stop") {
         ss.cancel();
         receivePort.close();
       } else {
         var z = "a" + i++; // intentional error
       }
     }
  );
  sendPort.send(receivePort.sendPort);
  print("isolate started");
}

test() async {
  ReceivePort receivePort = new ReceivePort();
  Isolate isolate = await Isolate.spawn(
                                    entryPoint,
                                    receivePort.sendPort,
                                    errorsAreFatal:false);
  SendPort sendPort = await receivePort.first;
  // subscribe to isolate.errors
  StreamSubscription ss1 = isolate.errors.listen(
    (data){ print("data $data on errors stream"); },
    onError: (e) { print("error $e on errors stream"); },
    onDone: () { print("errors stream is done");}
  );
  print("subscriptions finished");
  // produce errors
  sendPort.send("hello");
  sendPort.send("world");
  sendPort.send(":-)");
  sendPort.send("!stop");
  await new Future.delayed(new Duration(seconds:1)); // wait for events to travel
  sendPort.send("message to terminated isolated, should not be printed");
}

main() {
  test();
}

Please, note that it does not output errors stream is done. The test just hangs after all work is done. To fix it it is necessary to add ss1.cancel(); at the end of method test(). This behavior seems to be not natural and not convenient - there is no easy way to detect that isolate has finished in order to unsubscribe from its errors stream.

Dart VM version: 1.20.1 (Wed Oct 12 15:07:45 2016) on "windows_x64"

lrhn commented 7 years ago

You currently have to use addOnExitListener to know that an isolate has terminated.

On the other hand, I see no problem in having the errors stream do that for you - it's already an abstraction on top of the base functionality, so why not abstract over more. It should be a non-breaking change, so I'll try implementing it.