From e3d3bcd99e869844db3cbae33825930277aa6623 Mon Sep 17 00:00:00 2001 From: Simon Binder Date: Tue, 5 Nov 2019 21:36:51 +0100 Subject: [PATCH] Proper api to terminate a MoorIsolate --- .../src/runtime/isolate/communication.dart | 14 +++++++++- .../lib/src/runtime/isolate/moor_isolate.dart | 27 ++++++++++++++----- moor/lib/src/runtime/isolate/protocol.dart | 4 +++ moor/lib/src/runtime/isolate/server.dart | 4 +++ moor/test/isolate_test.dart | 4 +-- 5 files changed, 43 insertions(+), 10 deletions(-) diff --git a/moor/lib/src/runtime/isolate/communication.dart b/moor/lib/src/runtime/isolate/communication.dart index ce0bffc1..5309c057 100644 --- a/moor/lib/src/runtime/isolate/communication.dart +++ b/moor/lib/src/runtime/isolate/communication.dart @@ -1,6 +1,8 @@ import 'dart:async'; import 'dart:isolate'; +import 'package:pedantic/pedantic.dart'; + /// An isolate communication setup where there's a single "server" isolate that /// communicates with a varying amount of "client" isolates. /// @@ -46,7 +48,12 @@ class IsolateCommunication { final response = (await stream.first) as _ServerConnectionResponse; - return IsolateCommunication._(response.sendPort, stream, debugLog); + final communication = + IsolateCommunication._(response.sendPort, stream, debugLog); + + unawaited(communication.closed.then((_) => clientReceive.close())); + + return communication; } /// Closes the connection to the server. @@ -183,6 +190,10 @@ class Server { void close() { _openConnectionPort.close(); _opened.close(); + + for (var connected in currentChannels) { + connected.close(); + } } void _handleMessageOnConnectionPort(dynamic message) { @@ -199,6 +210,7 @@ class Server { communication.closed.whenComplete(() { currentChannels.remove(communication); + receiveFromClient.close(); }); } } diff --git a/moor/lib/src/runtime/isolate/moor_isolate.dart b/moor/lib/src/runtime/isolate/moor_isolate.dart index 1dc9f729..87421525 100644 --- a/moor/lib/src/runtime/isolate/moor_isolate.dart +++ b/moor/lib/src/runtime/isolate/moor_isolate.dart @@ -3,6 +3,7 @@ import 'dart:isolate'; import 'package:moor/moor.dart'; import 'package:moor/src/runtime/executor/stream_queries.dart'; +import 'package:pedantic/pedantic.dart'; import 'communication.dart'; part 'client.dart'; @@ -33,9 +34,7 @@ class MoorIsolate { /// Identifier for the server isolate that we can connect to. final ServerKey _server; - final Isolate _isolate; - - MoorIsolate._(this._server, this._isolate); + MoorIsolate._(this._server); /// Connects to this [MoorIsolate] from another isolate. All operations on the /// returned [DatabaseConnection] will be executed on a background isolate. @@ -45,6 +44,21 @@ class MoorIsolate { return client._connection; } + /// Stops the background isolate and disconnects all [DatabaseConnection]s + /// created. + /// If you only want to disconnect a database connection created via + /// [connect], use [GeneratedDatabase.close] instead. + Future shutdownAll() async { + final connection = await IsolateCommunication.connectAsClient(_server); + unawaited(connection.request(_NoArgsRequest.terminateAll).then((_) {}, + onError: (_) { + // the background isolate is closed before it gets a chance to reply + // to the terminateAll request. Ignore the error + })); + + await connection.closed; + } + /// Creates a new [MoorIsolate] on a background thread. /// /// The [opener] function will be used to open the [DatabaseConnection] used @@ -58,10 +72,9 @@ class MoorIsolate { final receiveServer = ReceivePort(); final keyFuture = receiveServer.first; - final isolate = await Isolate.spawn( - _startMoorIsolate, [receiveServer.sendPort, opener]); + await Isolate.spawn(_startMoorIsolate, [receiveServer.sendPort, opener]); final key = await keyFuture as ServerKey; - return MoorIsolate._(key, isolate); + return MoorIsolate._(key); } /// Creates a [MoorIsolate] in the [Isolate.current] isolate. The returned @@ -70,7 +83,7 @@ class MoorIsolate { /// connection which operations are all executed on this isolate. static MoorIsolate inCurrent(DatabaseOpener opener) { final server = _MoorServer(opener); - return MoorIsolate._(server.key, Isolate.current); + return MoorIsolate._(server.key); } } diff --git a/moor/lib/src/runtime/isolate/protocol.dart b/moor/lib/src/runtime/isolate/protocol.dart index 82846d91..6d5f7858 100644 --- a/moor/lib/src/runtime/isolate/protocol.dart +++ b/moor/lib/src/runtime/isolate/protocol.dart @@ -18,6 +18,10 @@ enum _NoArgsRequest { /// integer, which serves as an identifier for the transaction in /// [_ExecuteQuery.transactionId]. startTransaction, + + /// Close the background isolate, disconnect all clients, release all + /// associated resources + terminateAll, } enum _StatementMethod { diff --git a/moor/lib/src/runtime/isolate/server.dart b/moor/lib/src/runtime/isolate/server.dart index 9abaf13c..49fa01f8 100644 --- a/moor/lib/src/runtime/isolate/server.dart +++ b/moor/lib/src/runtime/isolate/server.dart @@ -37,6 +37,10 @@ class _MoorServer { return connection.executor.ensureOpen(); case _NoArgsRequest.startTransaction: return _spawnTransaction(); + case _NoArgsRequest.terminateAll: + server.close(); + Isolate.current.kill(); + break; // the following are requests which are handled on the client side case _NoArgsRequest.runOnCreate: throw UnsupportedError( diff --git a/moor/test/isolate_test.dart b/moor/test/isolate_test.dart index b0adb901..18fedce5 100644 --- a/moor/test/isolate_test.dart +++ b/moor/test/isolate_test.dart @@ -11,12 +11,12 @@ void main() { setUp(() async { isolate = await MoorIsolate.spawn(_backgroundConnection); - isolateConnection = await isolate.connect(isolateDebugLog: true); + isolateConnection = await isolate.connect(isolateDebugLog: false); }); tearDown(() { isolateConnection.executor.close(); - isolate.kill(); + return isolate.shutdownAll(); }); test('can open database and send requests', () async {