4

I wished to create an Isolate in Dart that I could pause and resume programmatically. This is the code that I used.

import 'dart:io';
import 'dart:isolate';

void main() async {
  print("Starting isolate");
  Isolate isolate;
  ReceivePort receivePort = ReceivePort();
  isolate = await Isolate.spawn(run, receivePort.sendPort);
  print("pausing");
  Capability cap = isolate.pause(isolate.pauseCapability);
  sleep(Duration(seconds: 5));
  print("Resuming");
  isolate.resume(cap);
}

void run(SendPort sendPort) {
  sleep(Duration(seconds: 2));
  print("Woke up, 1");
  sleep(Duration(seconds: 2));
  print("Woke up, 2");
  sleep(Duration(seconds: 2));
  print("Woke up, 3");
  sleep(Duration(seconds: 2));
  print("Woke up, 4");
  sleep(Duration(seconds: 2));
  print("Woke up, 5");
}

I'm getting an O/P like

Starting isolate
pausing
Woke up, 1
Woke up, 2
Resuming
Woke up, 3
Woke up, 4
Woke up, 5

But I want to achieve

Starting isolate
pausing
<--- 5 second delay --->
Resuming
Woke up, 1
Woke up, 2
Woke up, 3
Woke up, 4
Woke up, 5

But even after calling pause(), the Isolate keeps running. I found this answer and it said that it was because of the infinite loop in their case but I'm not using any loop. So what am I doing wrong here?

Ashutosh Singh
  • 1,107
  • 17
  • 28

1 Answers1

7

Your problem is that you have missed a detail in the documentation for the pause on isolate:

When the isolate receives the pause command, it stops processing events from the event loop queue. It may still add new events to the queue in response to, e.g., timers or receive-port messages. When the isolate is resumed, it starts handling the already enqueued events.

So pause does not stop the execution of method but will just make sure that after the execution of the current running method, it will then stop giving more work from the event queue.

While sleep is implemented more like a "stop all execution right now for N time" which is more what you expect:

Use this with care, as no asynchronous operations can be processed in a isolate while it is blocked in a sleep call.

To make your example to work you need to replace the sleep calls with Future instances you are awaiting on since this will add events on the event queue.

import 'dart:async';
import 'dart:io';
import 'dart:isolate';

Future<void> main() async {
  print("Starting isolate");
  Isolate isolate;
  ReceivePort receivePort = ReceivePort();
  isolate = await Isolate.spawn(run, receivePort.sendPort);
  print("pausing");
  Capability cap = isolate.pause(isolate.pauseCapability);
  sleep(const Duration(seconds: 5));
  print("Resuming");
  isolate.resume(cap);
}

Future<void> run(SendPort sendPort) async {
  await Future<void>.delayed(const Duration(seconds: 2));
  print("Woke up, 1");
  await Future<void>.delayed(const Duration(seconds: 2));
  print("Woke up, 2");
  await Future<void>.delayed(const Duration(seconds: 2));
  print("Woke up, 3");
  await Future<void>.delayed(const Duration(seconds: 2));
  print("Woke up, 4");
  await Future<void>.delayed(const Duration(seconds: 2));
  print("Woke up, 5");
}

Output:

Starting isolate
pausing
Resuming
Woke up, 1
Woke up, 2
Woke up, 3
Woke up, 4
Woke up, 5
julemand101
  • 28,470
  • 5
  • 52
  • 48
  • So does that means that if there are no futures that we are awaiting for inside the `run()` method, then the entire `run()` method is treated as a single queue event? – Ashutosh Singh May 31 '20 at 22:18
  • 1
    Yes. If you don't wait on any Future (or Stream), the program will just be one big event which cannot be paused. So if you need to do some heavy calculation and want to be able to pause it, you need to split it up into multiple events or made it so it await a future sometimes. You can just spawn a empty Future and await on it without any delay like: `await Future.value();`. It will give a little overhead so make a counter so it only does it for every N iteration. – julemand101 May 31 '20 at 22:23