dart-app-core/lib/platform/isolate_manager.dart
leedongmyung[desktop] cc0433b256 Isolate 정리
2023-11-03 19:49:21 +09:00

327 lines
8.9 KiB
Dart

import 'dart:io';
import 'dart:isolate';
import 'dart:async';
import 'package:dart_framework/data/isolate_data.dart';
import 'package:dart_framework/platform/process.dart';
/// ********************** Test ************************* ///
void test() {
var initData = {"param0": "value0"};
var handler =
IsolateManager.create<IsolateTemplate>(IsolateTemplate(), initData);
handler.addEventListener(IsolateEvent.ready, (data) async {
handler.broadcast('test1', 'initData1');
await Future.delayed(Duration(seconds: 2));
handler.send('test2', message: 'data2');
//will not arrive, addListener once
handler.send('test1', message: 'data1');
await Future.delayed(Duration(seconds: 5));
handler.exit();
});
handler.addEventListener(
IsolateEvent.exit, (data) => print('[Handler] Isolate is exited.'));
handler.addListener("CutomMessage", (data) {
print('[Handler] OnMessage - data: $data');
});
}
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) async {
print('[IsolateTemplate] OnTest1Handler: $data');
var dir = Platform.isWindows ? 'dir' : 'ls -l';
ProcessExecutor.run(StringBuffer(dir));
await delayTest();
}
Future delayTest() async {
await Future.delayed(Duration(seconds: 5));
send(IsoMessege('CutomMessage', data: 'This is message'));
}
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 IsolateHandler {
final int _id;
final Map<String, List<void Function(Object?)>> _listenerMap = {};
final Map<IsolateEvent, List<void Function(Object?)>> _listenerEventMap = {};
IsolateHandler(this._id);
void send(String type, {Object? message}) {
IsolateManager.send(_id, IsoMessege(type, data: message));
}
void addListener(String type, void Function(Object?) func) {
removeListener(type, func);
if (!_listenerMap.containsKey(type)) {
_listenerMap[type] = [];
}
_listenerMap[type]?.add(func);
}
void removeListener(String type, void Function(Object? data) func) {
if (_listenerMap.containsKey(type)) {
_listenerMap[type]?.remove(func);
}
}
void addEventListener(IsolateEvent event, void Function(Object? data) func) {
removeEventListener(event, func);
if (!_listenerEventMap.containsKey(event)) {
_listenerEventMap[event] = [];
}
_listenerEventMap[event]?.add(func);
}
void removeEventListener(
IsolateEvent event, void Function(Object? data) func) {
if (_listenerEventMap.containsKey(event)) {
_listenerEventMap[event]?.remove(func);
}
}
void broadcast(String type, Object? data) {
IsolateManager.sendBroadcast(IsoMessege(type, data: data));
}
void _isolateEventHandler(IsolateEvent event, Object? data) {
if (_listenerEventMap.containsKey(event)) {
var list = _listenerEventMap[event];
for (var item in list!) {
item(data);
}
}
}
void _isolateMessageHandler(IsoMessege message) {
var type = message.type;
if (_listenerMap.containsKey(type)) {
var list = _listenerMap[type];
for (var item in list!) {
item(message.data);
}
}
}
void exit() {
IsolateManager.exit(_id);
}
}
class IsolateManager {
IsolateManager._privateConstructor();
static final IsolateManager _instance = IsolateManager._privateConstructor();
static IsolateManager get instance => _instance;
static int _nonce = 0;
static 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 IsolateHandler create<T extends IsolateBase>(
T inst,
Object?
initData /*,
Function(IsolateEvent, Object?) isolateEventListener,
Function(IsoMessege) messageListener*/
) {
var id = nonce;
var handler = IsolateHandler(id);
instance._create<T>(inst, initData, id, handler._isolateEventHandler,
handler._isolateMessageHandler);
return handler;
}
void _create<T extends IsolateBase>(
T inst,
Object? initData,
int id,
Function(IsolateEvent, Object?) isolateEventListener,
Function(IsoMessege) messageListener) async {
final isoListener = ReceivePort();
inst.id = id;
_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.data);
}
return isNotExist;
}
static void sendBroadcast(IsoMessege message) {
var entries = instance._isoMap.entries;
for (var entry in entries) {
entry.value.insertEvent(message);
}
}
static void send(int id, IsoMessege message) {
var map = instance._isoMap;
if (map.containsKey(id)) {
map[id]?.insertEvent(message);
}
}
static void exit(int id) {
var map = instance._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, data: _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.data);
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);
}
void exit() {
IsolateManager.exit(id);
}
}