| // Copyright (c) 2019, the Dart project authors. Please see the AUTHORS file |
| // for details. All rights reserved. Use of this source code is governed by a |
| // BSD-style license that can be found in the LICENSE file. |
| |
| // @dart = 2.9 |
| |
| // VMOptions=--enable-isolate-groups |
| // VMOptions=--no-enable-isolate-groups |
| |
| import 'dart:async'; |
| import 'dart:io'; |
| import 'dart:isolate'; |
| |
| import 'package:http/http.dart' as http; |
| |
| void main() async { |
| // Start a server to obtain a randomly assigned, free port. |
| final mainServer = await HttpServer.bind('::1', 0, shared: true); |
| final sharedPort = mainServer.port; |
| |
| final workers = |
| List<ServerWorker>.generate(4, (i) => ServerWorker(i, sharedPort)); |
| await Future.wait(workers.map((w) => w.start())); |
| mainServer.close(); |
| |
| await Future.delayed(Duration(seconds: 1)); |
| // spawn client isolate |
| final clientisolate = await Isolate.spawn(client, sharedPort); |
| // Wait for 20 secs. It used to crash within 10 seconds. |
| await Future.delayed(Duration(seconds: 20)); |
| |
| workers.forEach((w) { |
| w.respawn = false; |
| w.terminate(); |
| }); |
| print('kill client isolates'); |
| clientisolate.kill(); |
| } |
| |
| class ServerWorker { |
| final int workerid; |
| final int port; |
| Isolate _isolate; |
| bool respawn = true; |
| |
| ServerWorker(this.workerid, this.port); |
| |
| Future<void> start() async { |
| final onExit = ReceivePort(); |
| onExit.listen((_) { |
| onExit.close(); |
| // Respawn another isolate |
| if (respawn) start(); |
| }); |
| final ready = ReceivePort(); |
| _isolate = await Isolate.spawn(_main, [workerid, port, ready.sendPort], |
| errorsAreFatal: true, onExit: onExit.sendPort); |
| await ready.first; |
| if (workerid == 0) terminate(); |
| } |
| |
| void terminate() { |
| Future.delayed(Duration(seconds: 1), () { |
| print('terminate ${workerid}'); |
| if (_isolate != null) { |
| _isolate.kill(); |
| _isolate = null; |
| } |
| }); |
| } |
| |
| static _main(List args) async { |
| final workerid = args[0] as int; |
| final port = args[1] as int; |
| final readyPort = args[2] as SendPort; |
| |
| final server = await HttpServer.bind('::1', port, shared: true); |
| readyPort.send(null); |
| server.listen((HttpRequest request) { |
| print('from worker ${workerid}'); |
| final response = request.response; |
| response.statusCode = HttpStatus.ok; |
| response.write('server worker ${workerid}'); |
| response.close(); |
| }); |
| } |
| } |
| |
| Future<String> get(Uri url) async { |
| while (true) { |
| try { |
| await http.get(url); |
| return ''; |
| } catch (err) {} |
| } |
| } |
| |
| void client(int port) async { |
| while (true) { |
| final futures = <Future>[]; |
| final numAtOnce = 16; // enough to keep the server busy |
| for (int i = 0; i < numAtOnce; ++i) { |
| futures.add(get(Uri.http('localhost:$port', '')).then((_) {})); |
| } |
| await Future.wait(futures); |
| } |
| } |