update: updating files with detailed comments
This commit is contained in:
parent
0a3d903320
commit
5fd57e1ebd
4 changed files with 122 additions and 9 deletions
|
@ -7,15 +7,6 @@
|
||||||
* file that was distributed with this source code.
|
* file that was distributed with this source code.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/*
|
|
||||||
* This file is part of the Protevus Platform.
|
|
||||||
*
|
|
||||||
* (C) Protevus <developers@protevus.com>
|
|
||||||
*
|
|
||||||
* For the full copyright and license information, please view the LICENSE
|
|
||||||
* file that was distributed with this source code.
|
|
||||||
*/
|
|
||||||
|
|
||||||
/// This library provides functionality for working with isolates in Dart.
|
/// This library provides functionality for working with isolates in Dart.
|
||||||
/// It exports three main components:
|
/// It exports three main components:
|
||||||
/// 1. Executable: Defines the structure for tasks that can be executed in isolates.
|
/// 1. Executable: Defines the structure for tasks that can be executed in isolates.
|
||||||
|
|
|
@ -11,25 +11,48 @@ import 'dart:async';
|
||||||
import 'dart:isolate';
|
import 'dart:isolate';
|
||||||
import 'dart:mirrors';
|
import 'dart:mirrors';
|
||||||
|
|
||||||
|
/// An abstract class representing an executable task in an isolate.
|
||||||
|
///
|
||||||
|
/// This class provides a framework for executing tasks in separate isolates,
|
||||||
|
/// with built-in communication capabilities.
|
||||||
abstract class Executable<T extends Object?> {
|
abstract class Executable<T extends Object?> {
|
||||||
|
/// Constructor for the Executable class.
|
||||||
|
///
|
||||||
|
/// @param message A map containing the message data, including a SendPort.
|
||||||
Executable(this.message) : _sendPort = message["_sendPort"];
|
Executable(this.message) : _sendPort = message["_sendPort"];
|
||||||
|
|
||||||
|
/// Abstract method to be implemented by subclasses.
|
||||||
|
///
|
||||||
|
/// This method should contain the main logic of the task to be executed.
|
||||||
|
/// @returns A Future that completes with the result of type T.
|
||||||
Future<T> execute();
|
Future<T> execute();
|
||||||
|
|
||||||
|
/// The message data passed to the Executable.
|
||||||
final Map<String, dynamic> message;
|
final Map<String, dynamic> message;
|
||||||
|
|
||||||
|
/// A SendPort for communicating back to the main isolate.
|
||||||
final SendPort? _sendPort;
|
final SendPort? _sendPort;
|
||||||
|
|
||||||
|
/// Creates an instance of a specified type using reflection.
|
||||||
|
///
|
||||||
|
/// @param typeName The name of the type to instantiate.
|
||||||
|
/// @param positionalArguments List of positional arguments for the constructor.
|
||||||
|
/// @param namedArguments Map of named arguments for the constructor.
|
||||||
|
/// @param constructorName The name of the constructor to use.
|
||||||
|
/// @returns An instance of the specified type U.
|
||||||
U instanceOf<U>(
|
U instanceOf<U>(
|
||||||
String typeName, {
|
String typeName, {
|
||||||
List positionalArguments = const [],
|
List positionalArguments = const [],
|
||||||
Map<Symbol, dynamic> namedArguments = const {},
|
Map<Symbol, dynamic> namedArguments = const {},
|
||||||
Symbol constructorName = Symbol.empty,
|
Symbol constructorName = Symbol.empty,
|
||||||
}) {
|
}) {
|
||||||
|
// Try to find the ClassMirror in the root library
|
||||||
ClassMirror? typeMirror = currentMirrorSystem()
|
ClassMirror? typeMirror = currentMirrorSystem()
|
||||||
.isolate
|
.isolate
|
||||||
.rootLibrary
|
.rootLibrary
|
||||||
.declarations[Symbol(typeName)] as ClassMirror?;
|
.declarations[Symbol(typeName)] as ClassMirror?;
|
||||||
|
|
||||||
|
// If not found in the root library, search in all libraries
|
||||||
typeMirror ??= currentMirrorSystem()
|
typeMirror ??= currentMirrorSystem()
|
||||||
.libraries
|
.libraries
|
||||||
.values
|
.values
|
||||||
|
@ -44,6 +67,7 @@ abstract class Executable<T extends Object?> {
|
||||||
),
|
),
|
||||||
) as ClassMirror?;
|
) as ClassMirror?;
|
||||||
|
|
||||||
|
// Create and return a new instance of the specified type
|
||||||
return typeMirror!
|
return typeMirror!
|
||||||
.newInstance(
|
.newInstance(
|
||||||
constructorName,
|
constructorName,
|
||||||
|
@ -53,10 +77,16 @@ abstract class Executable<T extends Object?> {
|
||||||
.reflectee as U;
|
.reflectee as U;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Sends a message back to the main isolate.
|
||||||
|
///
|
||||||
|
/// @param message The message to be sent.
|
||||||
void send(dynamic message) {
|
void send(dynamic message) {
|
||||||
_sendPort!.send(message);
|
_sendPort!.send(message);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Logs a message by sending it back to the main isolate.
|
||||||
|
///
|
||||||
|
/// @param message The message to be logged.
|
||||||
void log(String message) {
|
void log(String message) {
|
||||||
_sendPort!.send({"_line_": message});
|
_sendPort!.send({"_line_": message});
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,25 +12,64 @@ import 'dart:io';
|
||||||
import 'dart:isolate';
|
import 'dart:isolate';
|
||||||
import 'package:protevus_isolate/isolate.dart';
|
import 'package:protevus_isolate/isolate.dart';
|
||||||
|
|
||||||
|
/// A class that manages the execution of code in an isolate.
|
||||||
|
///
|
||||||
|
/// This class provides functionality to run code in a separate isolate,
|
||||||
|
/// allowing for concurrent execution and isolation of resources.
|
||||||
|
/// It handles the creation of the isolate, communication between the
|
||||||
|
/// main isolate and the spawned isolate, and manages the lifecycle
|
||||||
|
/// of the execution.
|
||||||
class IsolateExecutor<U> {
|
class IsolateExecutor<U> {
|
||||||
|
/// Creates an instance of IsolateExecutor.
|
||||||
|
///
|
||||||
|
/// [generator] is the [SourceGenerator] that provides the source code
|
||||||
|
/// to be executed in the isolate.
|
||||||
|
/// [packageConfigURI] is the optional URI of the package configuration file.
|
||||||
|
/// If provided, it will be used for package resolution in the isolate.
|
||||||
|
/// [message] is an optional map of data to be passed to the isolate.
|
||||||
|
/// This data will be available to the code running in the isolate.
|
||||||
IsolateExecutor(
|
IsolateExecutor(
|
||||||
this.generator, {
|
this.generator, {
|
||||||
this.packageConfigURI,
|
this.packageConfigURI,
|
||||||
this.message = const {},
|
this.message = const {},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
/// The source generator that provides the code to be executed.
|
||||||
final SourceGenerator generator;
|
final SourceGenerator generator;
|
||||||
|
|
||||||
|
/// A map of data to be passed to the isolate.
|
||||||
final Map<String, dynamic> message;
|
final Map<String, dynamic> message;
|
||||||
|
|
||||||
|
/// The URI of the package configuration file.
|
||||||
final Uri? packageConfigURI;
|
final Uri? packageConfigURI;
|
||||||
|
|
||||||
|
/// A completer that completes when the isolate execution is finished.
|
||||||
final Completer completer = Completer();
|
final Completer completer = Completer();
|
||||||
|
|
||||||
|
/// Stream of events from the isolate.
|
||||||
|
///
|
||||||
|
/// This stream emits any custom events sent from the isolate during execution.
|
||||||
Stream<dynamic> get events => _eventListener.stream;
|
Stream<dynamic> get events => _eventListener.stream;
|
||||||
|
|
||||||
|
/// Stream of console output from the isolate.
|
||||||
|
///
|
||||||
|
/// This stream emits any console output (print statements, etc.) from the isolate.
|
||||||
Stream<String> get console => _logListener.stream;
|
Stream<String> get console => _logListener.stream;
|
||||||
|
|
||||||
|
/// StreamController for managing console output from the isolate.
|
||||||
final StreamController<String> _logListener = StreamController<String>();
|
final StreamController<String> _logListener = StreamController<String>();
|
||||||
|
|
||||||
|
/// StreamController for managing custom events from the isolate.
|
||||||
final StreamController<dynamic> _eventListener = StreamController<dynamic>();
|
final StreamController<dynamic> _eventListener = StreamController<dynamic>();
|
||||||
|
|
||||||
|
/// Executes the code in the isolate and returns the result.
|
||||||
|
///
|
||||||
|
/// This method spawns a new isolate, runs the provided code, and returns
|
||||||
|
/// the result. It handles error cases and ensures proper cleanup of resources.
|
||||||
|
///
|
||||||
|
/// Throws a [StateError] if the package configuration file is not found.
|
||||||
|
///
|
||||||
|
/// Returns a [Future] that completes with the result of the isolate execution.
|
||||||
Future<U> execute() async {
|
Future<U> execute() async {
|
||||||
if (packageConfigURI != null &&
|
if (packageConfigURI != null &&
|
||||||
!File.fromUri(packageConfigURI!).existsSync()) {
|
!File.fromUri(packageConfigURI!).existsSync()) {
|
||||||
|
@ -93,6 +132,21 @@ class IsolateExecutor<U> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Runs an executable in an isolate.
|
||||||
|
///
|
||||||
|
/// This static method provides a convenient way to execute code in an isolate.
|
||||||
|
/// It creates a [SourceGenerator], sets up an [IsolateExecutor], and manages
|
||||||
|
/// the execution process.
|
||||||
|
///
|
||||||
|
/// [executable] is an instance of [Executable<T>] containing the code to be executed.
|
||||||
|
/// [imports] is an optional list of import statements to be included in the isolate.
|
||||||
|
/// [packageConfigURI] is the optional URI of the package configuration file.
|
||||||
|
/// [additionalContents] is optional additional code to be included in the isolate.
|
||||||
|
/// [additionalTypes] is an optional list of additional types to be included in the isolate.
|
||||||
|
/// [eventHandler] is an optional function to handle events from the isolate.
|
||||||
|
/// [logHandler] is an optional function to handle console output from the isolate.
|
||||||
|
///
|
||||||
|
/// Returns a [Future] that completes with the result of type [T] from the isolate execution.
|
||||||
static Future<T> run<T>(
|
static Future<T> run<T>(
|
||||||
Executable<T> executable, {
|
Executable<T> executable, {
|
||||||
List<String> imports = const [],
|
List<String> imports = const [],
|
||||||
|
|
|
@ -20,7 +20,14 @@ import 'package:analyzer/file_system/physical_file_system.dart';
|
||||||
import 'package:path/path.dart';
|
import 'package:path/path.dart';
|
||||||
import 'package:protevus_isolate/isolate.dart';
|
import 'package:protevus_isolate/isolate.dart';
|
||||||
|
|
||||||
|
/// A class responsible for generating source code for isolate execution.
|
||||||
class SourceGenerator {
|
class SourceGenerator {
|
||||||
|
/// Constructs a SourceGenerator instance.
|
||||||
|
///
|
||||||
|
/// [executableType]: The Type of the executable class.
|
||||||
|
/// [imports]: List of import statements to include in the generated source.
|
||||||
|
/// [additionalTypes]: List of additional Types to include in the generated source.
|
||||||
|
/// [additionalContents]: Optional additional content to append to the generated source.
|
||||||
SourceGenerator(
|
SourceGenerator(
|
||||||
this.executableType, {
|
this.executableType, {
|
||||||
this.imports = const [],
|
this.imports = const [],
|
||||||
|
@ -28,24 +35,40 @@ class SourceGenerator {
|
||||||
this.additionalContents,
|
this.additionalContents,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
/// The Type of the executable class.
|
||||||
Type executableType;
|
Type executableType;
|
||||||
|
|
||||||
|
/// Returns the name of the executable type.
|
||||||
String get typeName =>
|
String get typeName =>
|
||||||
MirrorSystem.getName(reflectType(executableType).simpleName);
|
MirrorSystem.getName(reflectType(executableType).simpleName);
|
||||||
|
|
||||||
|
/// List of import statements to include in the generated source.
|
||||||
final List<String> imports;
|
final List<String> imports;
|
||||||
|
|
||||||
|
/// Optional additional content to append to the generated source.
|
||||||
final String? additionalContents;
|
final String? additionalContents;
|
||||||
|
|
||||||
|
/// List of additional Types to include in the generated source.
|
||||||
final List<Type> additionalTypes;
|
final List<Type> additionalTypes;
|
||||||
|
|
||||||
|
/// Generates the complete script source for isolate execution.
|
||||||
|
///
|
||||||
|
/// Returns a Future<String> containing the generated source code.
|
||||||
Future<String> get scriptSource async {
|
Future<String> get scriptSource async {
|
||||||
final typeSource = (await _getClass(executableType)).toSource();
|
final typeSource = (await _getClass(executableType)).toSource();
|
||||||
final builder = StringBuffer();
|
final builder = StringBuffer();
|
||||||
|
|
||||||
|
// Add standard imports
|
||||||
builder.writeln("import 'dart:async';");
|
builder.writeln("import 'dart:async';");
|
||||||
builder.writeln("import 'dart:isolate';");
|
builder.writeln("import 'dart:isolate';");
|
||||||
builder.writeln("import 'dart:mirrors';");
|
builder.writeln("import 'dart:mirrors';");
|
||||||
|
|
||||||
|
// Add custom imports
|
||||||
for (final anImport in imports) {
|
for (final anImport in imports) {
|
||||||
builder.writeln("import '$anImport';");
|
builder.writeln("import '$anImport';");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Add main function for isolate execution
|
||||||
builder.writeln(
|
builder.writeln(
|
||||||
"""
|
"""
|
||||||
Future main (List<String> args, Map<String, dynamic> message) async {
|
Future main (List<String> args, Map<String, dynamic> message) async {
|
||||||
|
@ -56,14 +79,20 @@ Future main (List<String> args, Map<String, dynamic> message) async {
|
||||||
}
|
}
|
||||||
""",
|
""",
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// Add executable class source
|
||||||
builder.writeln(typeSource);
|
builder.writeln(typeSource);
|
||||||
|
|
||||||
|
// Add Executable base class source
|
||||||
builder.writeln((await _getClass(Executable)).toSource());
|
builder.writeln((await _getClass(Executable)).toSource());
|
||||||
|
|
||||||
|
// Add additional types' sources
|
||||||
for (final type in additionalTypes) {
|
for (final type in additionalTypes) {
|
||||||
final source = await _getClass(type);
|
final source = await _getClass(type);
|
||||||
builder.writeln(source.toSource());
|
builder.writeln(source.toSource());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Add additional contents if provided
|
||||||
if (additionalContents != null) {
|
if (additionalContents != null) {
|
||||||
builder.writeln(additionalContents);
|
builder.writeln(additionalContents);
|
||||||
}
|
}
|
||||||
|
@ -71,6 +100,10 @@ Future main (List<String> args, Map<String, dynamic> message) async {
|
||||||
return builder.toString();
|
return builder.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Retrieves the ClassDeclaration for a given Type.
|
||||||
|
///
|
||||||
|
/// [type]: The Type to retrieve the ClassDeclaration for.
|
||||||
|
/// Returns a Future<ClassDeclaration>.
|
||||||
static Future<ClassDeclaration> _getClass(Type type) async {
|
static Future<ClassDeclaration> _getClass(Type type) async {
|
||||||
final uri =
|
final uri =
|
||||||
await Isolate.resolvePackageUri(reflectClass(type).location!.sourceUri);
|
await Isolate.resolvePackageUri(reflectClass(type).location!.sourceUri);
|
||||||
|
@ -88,6 +121,11 @@ Future main (List<String> args, Map<String, dynamic> message) async {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Creates an AnalysisContext for a given file path.
|
||||||
|
///
|
||||||
|
/// [path]: The file path to create the context for.
|
||||||
|
/// [resourceProvider]: Optional ResourceProvider, defaults to PhysicalResourceProvider.INSTANCE.
|
||||||
|
/// Returns an AnalysisContext.
|
||||||
AnalysisContext _createContext(
|
AnalysisContext _createContext(
|
||||||
String path, {
|
String path, {
|
||||||
ResourceProvider? resourceProvider,
|
ResourceProvider? resourceProvider,
|
||||||
|
|
Loading…
Reference in a new issue