refactor: encapsulate isolate container

This commit is contained in:
Tien Do Nam
2024-10-28 01:57:45 +01:00
parent a87928b2e2
commit f43c9da494
11 changed files with 54 additions and 23 deletions
@@ -1,9 +1,9 @@
import 'package:common/model/device.dart';
import 'package:common/src/discovery/http_scan_discovery.dart';
import 'package:common/src/isolate/child/main.dart';
import 'package:common/src/isolate/dto/isolate_task.dart';
import 'package:common/src/isolate/dto/isolate_task_stream_result.dart';
import 'package:common/src/isolate/dto/send_to_isolate_data.dart';
import 'package:common/src/task/discovery/http_scan_discovery.dart';
import 'package:meta/meta.dart';
sealed class HttpScanTask {}
@@ -41,14 +41,14 @@ Future<void> setupHttpScanDiscoveryIsolate(
receiveFromMain: receiveFromMain,
sendToMain: sendToMain,
initialData: initialData,
handler: (task) async {
handler: (ref, task) async {
final stream = switch (task.data) {
HttpInterfaceScanTask data => isolateContainer.read(httpScanDiscoveryProvider).getStream(
HttpInterfaceScanTask data => ref.read(httpScanDiscoveryProvider).getStream(
networkInterface: data.networkInterface,
port: data.port,
https: data.https,
),
HttpFavoriteScanTask data => isolateContainer.read(httpScanDiscoveryProvider).getFavoriteStream(
HttpFavoriteScanTask data => ref.read(httpScanDiscoveryProvider).getFavoriteStream(
devices: data.favorites,
https: data.https,
),
@@ -1,9 +1,9 @@
import 'package:common/model/device.dart';
import 'package:common/src/discovery/http_target_discovery.dart';
import 'package:common/src/isolate/child/main.dart';
import 'package:common/src/isolate/dto/isolate_task.dart';
import 'package:common/src/isolate/dto/isolate_task_result.dart';
import 'package:common/src/isolate/dto/send_to_isolate_data.dart';
import 'package:common/src/task/discovery/http_target_discovery.dart';
import 'package:meta/meta.dart';
class HttpTargetTask {
@@ -29,8 +29,8 @@ Future<void> setupHttpTargetDiscoveryIsolate(
receiveFromMain: receiveFromMain,
sendToMain: sendToMain,
initialData: initialData,
handler: (task) async {
final device = await isolateContainer.read(httpTargetDiscoveryProvider).discover(
handler: (ref, task) async {
final device = await ref.read(httpTargetDiscoveryProvider).discover(
ip: task.data.ip,
port: task.data.port,
https: task.data.https,
+13 -10
View File
@@ -7,11 +7,11 @@ import 'package:logging/logging.dart';
import 'package:meta/meta.dart';
import 'package:refena/refena.dart';
@internal
final isolateContainer = RefenaContainer();
final _logger = Logger('ChildIsolateMain');
/// The container for the child isolate.
final _isolateContainer = RefenaContainer();
class InitialData {
final SyncState syncState;
final Level logLevel;
@@ -26,6 +26,9 @@ class InitialData {
/// constructing an endless running task that listens to [receiveFromMain]
/// and calls [handler] for each message (usually calling [sendToMain] in the [handler]).
///
/// The provided [Ref]s in the [handler] and [init] functions are instance of the child isolate [RefenaContainer],
/// therefore, they can be safely used to call providers in the task directory.
///
/// An optional [init] function can be provided to run before the endless loop.
@internal
Future<void> setupChildIsolateHelper<S, R>({
@@ -33,12 +36,12 @@ Future<void> setupChildIsolateHelper<S, R>({
required Stream<SendToIsolateData<S>> receiveFromMain,
required void Function(R) sendToMain,
required InitialData initialData,
Future<void> Function()? init,
required Future<void> Function(S data) handler,
Future<void> Function(Ref ref)? init,
required Future<void> Function(Ref ref, S data) handler,
}) async {
initLogger(initialData.logLevel);
isolateContainer.set(
_isolateContainer.set(
syncProvider.overrideWithNotifier(
(ref) => SyncService(
initial: initialData.syncState,
@@ -47,7 +50,7 @@ Future<void> setupChildIsolateHelper<S, R>({
);
if (init != null) {
await init();
await init(_isolateContainer);
}
_logger.info('Child isolate is ready: $debugLabel (logLevel: ${initialData.logLevel})');
@@ -58,16 +61,16 @@ Future<void> setupChildIsolateHelper<S, R>({
}
// separate function to avoid blocking the for loop
void _handleMessage<S>(String debugLabel, SendToIsolateData<S> message, Future<void> Function(S data) handler) async {
void _handleMessage<S>(String debugLabel, SendToIsolateData<S> message, Future<void> Function(Ref ref, S data) handler) async {
final syncState = message.syncState;
if (syncState != null) {
isolateContainer.redux(syncProvider).dispatch(UpdateSyncStateAction(syncState));
_isolateContainer.redux(syncProvider).dispatch(UpdateSyncStateAction(syncState));
}
final data = message.data;
if (data != null) {
try {
await handler(data);
await handler(_isolateContainer, data);
} catch (e) {
_logger.severe('Error in $debugLabel: $e', e);
}
@@ -1,11 +1,15 @@
import 'package:common/model/device.dart';
import 'package:common/src/discovery/multicast_discovery.dart';
import 'package:common/src/isolate/child/main.dart';
import 'package:common/src/isolate/dto/send_to_isolate_data.dart';
import 'package:common/src/task/discovery/multicast_discovery.dart';
import 'package:meta/meta.dart';
/// Sends an announcement to all devices to all network interfaces.
/// They will respond with their device information.
///
/// This is not wrapped in an [IsolateTask] because
/// - (1) it is not important if the device was found by this specific announcement or another one
/// - (2) it is not 100% accurate to know if a device was found by this announcement or another one
class MulticastAnnouncementTask {
static const instance = MulticastAnnouncementTask._();
@@ -23,13 +27,13 @@ Future<void> setupMulticastDiscoveryIsolate(
receiveFromMain: receiveFromMain,
sendToMain: sendToMain,
initialData: initialData,
init: () async {
isolateContainer.read(multicastDiscoveryProvider).startListener().listen((event) {
init: (ref) async {
ref.read(multicastDiscoveryProvider).startListener().listen((event) {
sendToMain(event);
});
},
handler: (_) async {
await isolateContainer.read(multicastDiscoveryProvider).sendAnnouncement();
handler: (ref, task) async {
await ref.read(multicastDiscoveryProvider).sendAnnouncement();
},
);
}
@@ -1,5 +1,18 @@
import 'package:common/src/isolate/dto/isolate_task_result.dart';
import 'package:common/src/isolate/dto/isolate_task_stream_result.dart';
/// A data structure that can be sent to an isolate.
/// This is used to represent the following schemas:
///
/// Main sends an [IsolateTask] -> Isolate responds with an [IsolateTaskResult]
/// Main sends an [IsolateTask] -> Isolate responds with an [IsolateTaskStreamResult]
///
/// Generally speaking, we need an exact request-response pair.
class IsolateTask<T> {
/// The id of the task to be matched with [IsolateTaskResult.id] or [IsolateTaskStreamResult.id].
final int id;
/// The payload of the request.
final T data;
IsolateTask({
@@ -1,5 +1,11 @@
import 'package:common/src/isolate/dto/isolate_task.dart';
/// The response data structure from an [IsolateTask].
class IsolateTaskResult<T> {
/// The id of the task to be matched with [IsolateTask.id].
final int id;
/// The payload of the response.
final T data;
IsolateTaskResult({
@@ -1,5 +1,7 @@
import 'package:common/src/isolate/dto/isolate_task.dart';
import 'package:common/src/isolate/dto/isolate_task_result.dart';
/// Stream version of [IsolateTaskResult].
class IsolateTaskStreamResult<T> {
/// The id of the task.
/// Corresponds to [IsolateTask.id] that started the stream.
+3
View File
@@ -0,0 +1,3 @@
All files in this directory should only call utility functions.
Isolate logic is prohibited in this directory.
@@ -1,5 +1,5 @@
import 'package:common/model/device.dart';
import 'package:common/src/discovery/http_target_discovery.dart';
import 'package:common/src/task/discovery/http_target_discovery.dart';
import 'package:common/util/task_runner.dart';
import 'package:logging/logging.dart';
import 'package:refena/refena.dart';