From 5fd57e1ebd643481751231f77f4d77ad17c87a84 Mon Sep 17 00:00:00 2001 From: Patrick Stewart Date: Sun, 8 Sep 2024 23:09:16 -0700 Subject: [PATCH] update: updating files with detailed comments --- packages/isolate/lib/isolate.dart | 9 ---- packages/isolate/lib/src/executable.dart | 30 +++++++++++ packages/isolate/lib/src/executor.dart | 54 +++++++++++++++++++ .../isolate/lib/src/source_generator.dart | 38 +++++++++++++ 4 files changed, 122 insertions(+), 9 deletions(-) diff --git a/packages/isolate/lib/isolate.dart b/packages/isolate/lib/isolate.dart index 595750a..b27b2cc 100644 --- a/packages/isolate/lib/isolate.dart +++ b/packages/isolate/lib/isolate.dart @@ -7,15 +7,6 @@ * file that was distributed with this source code. */ -/* - * This file is part of the Protevus Platform. - * - * (C) Protevus - * - * 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. /// It exports three main components: /// 1. Executable: Defines the structure for tasks that can be executed in isolates. diff --git a/packages/isolate/lib/src/executable.dart b/packages/isolate/lib/src/executable.dart index f24d344..86319dd 100644 --- a/packages/isolate/lib/src/executable.dart +++ b/packages/isolate/lib/src/executable.dart @@ -11,25 +11,48 @@ import 'dart:async'; import 'dart:isolate'; 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 { + /// Constructor for the Executable class. + /// + /// @param message A map containing the message data, including a 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 execute(); + /// The message data passed to the Executable. final Map message; + + /// A SendPort for communicating back to the main isolate. 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( String typeName, { List positionalArguments = const [], Map namedArguments = const {}, Symbol constructorName = Symbol.empty, }) { + // Try to find the ClassMirror in the root library ClassMirror? typeMirror = currentMirrorSystem() .isolate .rootLibrary .declarations[Symbol(typeName)] as ClassMirror?; + // If not found in the root library, search in all libraries typeMirror ??= currentMirrorSystem() .libraries .values @@ -44,6 +67,7 @@ abstract class Executable { ), ) as ClassMirror?; + // Create and return a new instance of the specified type return typeMirror! .newInstance( constructorName, @@ -53,10 +77,16 @@ abstract class Executable { .reflectee as U; } + /// Sends a message back to the main isolate. + /// + /// @param message The message to be sent. void send(dynamic 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) { _sendPort!.send({"_line_": message}); } diff --git a/packages/isolate/lib/src/executor.dart b/packages/isolate/lib/src/executor.dart index fa8946d..a90096d 100644 --- a/packages/isolate/lib/src/executor.dart +++ b/packages/isolate/lib/src/executor.dart @@ -12,25 +12,64 @@ import 'dart:io'; import 'dart:isolate'; 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 { + /// 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( this.generator, { this.packageConfigURI, this.message = const {}, }); + /// The source generator that provides the code to be executed. final SourceGenerator generator; + + /// A map of data to be passed to the isolate. final Map message; + + /// The URI of the package configuration file. final Uri? packageConfigURI; + + /// A completer that completes when the isolate execution is finished. final Completer completer = Completer(); + /// Stream of events from the isolate. + /// + /// This stream emits any custom events sent from the isolate during execution. Stream get events => _eventListener.stream; + /// Stream of console output from the isolate. + /// + /// This stream emits any console output (print statements, etc.) from the isolate. Stream get console => _logListener.stream; + /// StreamController for managing console output from the isolate. final StreamController _logListener = StreamController(); + + /// StreamController for managing custom events from the isolate. final StreamController _eventListener = StreamController(); + /// 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 execute() async { if (packageConfigURI != null && !File.fromUri(packageConfigURI!).existsSync()) { @@ -93,6 +132,21 @@ class IsolateExecutor { } } + /// 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] 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 run( Executable executable, { List imports = const [], diff --git a/packages/isolate/lib/src/source_generator.dart b/packages/isolate/lib/src/source_generator.dart index 7c4ba86..8225a1e 100644 --- a/packages/isolate/lib/src/source_generator.dart +++ b/packages/isolate/lib/src/source_generator.dart @@ -20,7 +20,14 @@ import 'package:analyzer/file_system/physical_file_system.dart'; import 'package:path/path.dart'; import 'package:protevus_isolate/isolate.dart'; +/// A class responsible for generating source code for isolate execution. 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( this.executableType, { this.imports = const [], @@ -28,24 +35,40 @@ class SourceGenerator { this.additionalContents, }); + /// The Type of the executable class. Type executableType; + /// Returns the name of the executable type. String get typeName => MirrorSystem.getName(reflectType(executableType).simpleName); + + /// List of import statements to include in the generated source. final List imports; + + /// Optional additional content to append to the generated source. final String? additionalContents; + + /// List of additional Types to include in the generated source. final List additionalTypes; + /// Generates the complete script source for isolate execution. + /// + /// Returns a Future containing the generated source code. Future get scriptSource async { final typeSource = (await _getClass(executableType)).toSource(); final builder = StringBuffer(); + // Add standard imports builder.writeln("import 'dart:async';"); builder.writeln("import 'dart:isolate';"); builder.writeln("import 'dart:mirrors';"); + + // Add custom imports for (final anImport in imports) { builder.writeln("import '$anImport';"); } + + // Add main function for isolate execution builder.writeln( """ Future main (List args, Map message) async { @@ -56,14 +79,20 @@ Future main (List args, Map message) async { } """, ); + + // Add executable class source builder.writeln(typeSource); + // Add Executable base class source builder.writeln((await _getClass(Executable)).toSource()); + + // Add additional types' sources for (final type in additionalTypes) { final source = await _getClass(type); builder.writeln(source.toSource()); } + // Add additional contents if provided if (additionalContents != null) { builder.writeln(additionalContents); } @@ -71,6 +100,10 @@ Future main (List args, Map message) async { return builder.toString(); } + /// Retrieves the ClassDeclaration for a given Type. + /// + /// [type]: The Type to retrieve the ClassDeclaration for. + /// Returns a Future. static Future _getClass(Type type) async { final uri = await Isolate.resolvePackageUri(reflectClass(type).location!.sourceUri); @@ -88,6 +121,11 @@ Future main (List args, Map 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( String path, { ResourceProvider? resourceProvider,