dart-app-core/lib/core/isolate_manager.dart
2023-03-20 00:04:47 +09:00

235 lines
6.6 KiB
Dart

import 'dart:isolate';
import 'dart:async';
import 'package:dart_framework/data/isolate_data.dart';
/// ********************** Test ************************* ///
void test() {
var initData = {"param0": "value0"};
IsolateManager.create<IsolateTemplate>(IsolateTemplate(), initData,
(isoEvent, data) async {
if (isoEvent == IsolateEvent.ready) {
int id = data as int;
IsolateManager.sendBroadcast(IsoMessege('test1'));
await Future.delayed(Duration(seconds: 2));
IsolateManager.send(id, IsoMessege('test2'));
IsolateManager.send(id, IsoMessege('test1'));
IsolateManager.exit(id);
} else if (isoEvent == IsolateEvent.exit) {
print('Isolate is exited.');
}
print('On Event from Isolate: ${isoEvent.name}');
}, (message) {
print('On Message from Isolate: ${message.type}');
});
}
class IsolateTemplate extends IsolateBase {
@override
void onReady(Object? initData) async {
print('[IsolateTemplate] onReady - initData: $initData');
addEventListener('test1', onTest1Handler, executeOnce: true);
addEventListener('test2', onTest2Handler);
}
void onTest1Handler(Object? data) {
print('[IsolateTemplate] OnTest1Handler: $data');
}
void onTest2Handler(Object? data) {
print('[IsolateTemplate] OnTest2Handler: $data');
}
}
/// ********************** Define *********************** ///
class _IsolateInnerEvent {
static const String ready = '_IsolateInnerEvent.Ready';
static const String exit = '_IsolateInnerEvent.Exit';
}
class _IsolateConstructData {
late int id;
late SendPort mainIsoSender;
late Object? initData;
_IsolateConstructData(this.id, this.mainIsoSender, this.initData);
}
class IsolateManager {
IsolateManager._privateConstructor();
static final IsolateManager _instance = IsolateManager._privateConstructor();
static IsolateManager get to => _instance;
int _nonce = 0;
int get nonce {
if (_nonce == double.maxFinite.toInt()) {
_nonce = 0;
} else {
_nonce++;
}
return _nonce;
}
final Map<int, IsolateBase> _isoMap = {};
final Map<int, Map<String, Function(Object?)>> _innerEventMap = {};
static void create<T extends IsolateBase>(
T inst,
Object? initData,
Function(IsolateEvent, Object?) isolateEventListener,
Function(IsoMessege) messageListener) async {
to._create<T>(inst, initData, isolateEventListener, messageListener);
}
void _create<T extends IsolateBase>(
T inst,
Object? initData,
Function(IsolateEvent, Object?) isolateEventListener,
Function(IsoMessege) messageListener) async {
final isoListener = ReceivePort();
var id = nonce;
_isoMap[id] = inst;
_addInnerEvent(id, _IsolateInnerEvent.ready, (message) {
inst.isoListener = message as SendPort;
isolateEventListener(IsolateEvent.ready, id);
});
await Isolate.spawn(inst.initialize,
_IsolateConstructData(id, isoListener.sendPort, initData));
await for (IsoMessege message in isoListener) {
if (message.type == _IsolateInnerEvent.exit) {
isolateEventListener(IsolateEvent.exit, null);
break;
} else if (_filterInnerEvent(id, message)) {
messageListener(message);
}
}
isoListener.close();
_isoMap.remove(id);
}
void _addInnerEvent(int id, String type, Function(Object?) listener) {
Map<String, Function(Object?)>? eventMap;
if (!_innerEventMap.containsKey(id)) {
eventMap = {};
_innerEventMap[id] = eventMap;
}
eventMap![type] = listener;
}
bool _filterInnerEvent(int id, IsoMessege message) {
var isNotExist = true;
if (_innerEventMap.containsKey(id) &&
_innerEventMap[id]!.containsKey(message.type)) {
isNotExist = false;
_innerEventMap[id]![message.type]!(message.message);
}
return isNotExist;
}
static void sendBroadcast(IsoMessege message) {
var entries = to._isoMap.entries;
for (var entry in entries) {
entry.value.insertEvent(message);
}
}
static void send(int id, IsoMessege message) {
var map = to._isoMap;
if (map.containsKey(id)) {
map[id]?.insertEvent(message);
}
}
static void exit(int id) {
var map = to._isoMap;
if (map.containsKey(id)) {
map[id]!.insertEvent(IsoMessege(_IsolateInnerEvent.exit));
}
}
}
class _IsolateEventListener {
late bool executeOnce;
late Function(Object?) listener;
_IsolateEventListener(this.listener, this.executeOnce);
}
class IsolateBase {
late int id;
late ReceivePort _mainIsoListener;
late SendPort _mainIsoSender;
late SendPort isoListener;
final Map<String, List<_IsolateEventListener>> _eventMap = {};
final List<_IsolateEventListener> _removeList = [];
// ignore: library_private_types_in_public_api
Future<void> initialize(_IsolateConstructData initData) async {
id = initData.id;
_mainIsoListener = ReceivePort();
_mainIsoSender = initData.mainIsoSender;
send(IsoMessege(_IsolateInnerEvent.ready,
message: _mainIsoListener.sendPort));
onReady(initData.initData);
await for (IsoMessege message in _mainIsoListener) {
if (message.type == _IsolateInnerEvent.exit) {
send(IsoMessege(_IsolateInnerEvent.exit));
break;
} else {
_onEvent(message);
}
}
Isolate.exit(_mainIsoSender);
}
void onReady(Object? initData) async {}
void send(IsoMessege message) {
_mainIsoSender.send(message);
}
void _onEvent(IsoMessege message) {
if (_eventMap.containsKey(message.type)) {
var list = _eventMap[message.type]!;
for (var item in list) {
item.listener(message.message);
if (item.executeOnce) {
_removeList.add(item);
}
}
if (_removeList.isNotEmpty) {
for (var item in _removeList) {
removeEventListener(message.type, item.listener);
}
_removeList.clear();
}
}
}
void addEventListener(String type, Function(Object?) listener,
{bool executeOnce = false}) {
List<_IsolateEventListener> list;
if (_eventMap.containsKey(type)) {
list = _eventMap[type]!;
} else {
list = [];
_eventMap[type] = list;
}
list.add(_IsolateEventListener(listener, executeOnce));
}
void removeEventListener(String type, Function(Object?) listener) {
if (_eventMap.containsKey(type)) {
var list = _eventMap[type]!;
for (int i = 0; i < list.length; ++i) {
var item = list[i];
if (item.listener == listener) {
list.removeAt(i);
--i;
}
}
}
}
void insertEvent(IsoMessege message) {
isoListener.send(message);
}
}