10 KiB
10 KiB
Process Package Specification
Overview
The Process package provides a robust system process handling system that matches Laravel's process functionality. It supports process execution, input/output handling, process pools, and signal handling while integrating with our Event and Queue packages.
Related Documentation
- See Laravel Compatibility Roadmap for implementation status
- See Foundation Integration Guide for integration patterns
- See Testing Guide for testing approaches
- See Getting Started Guide for development setup
- See Events Package Specification for process events
- See Queue Package Specification for background processing
Core Features
1. Process Manager
/// Core process manager implementation
class ProcessManager implements ProcessContract {
/// Container instance
final Container _container;
/// Active processes
final Map<int, Process> _processes = {};
/// Process event dispatcher
final EventDispatcherContract _events;
ProcessManager(this._container, this._events);
/// Starts a process
Future<Process> start(
String command, [
List<String>? arguments,
ProcessOptions? options
]) async {
options ??= ProcessOptions();
var process = await Process.start(
command,
arguments ?? [],
workingDirectory: options.workingDirectory,
environment: options.environment,
includeParentEnvironment: options.includeParentEnvironment,
runInShell: options.runInShell
);
_processes[process.pid] = process;
await _events.dispatch(ProcessStarted(process));
return process;
}
/// Runs a process to completion
Future<ProcessResult> run(
String command, [
List<String>? arguments,
ProcessOptions? options
]) async {
var process = await start(command, arguments, options);
var result = await process.exitCode;
await _events.dispatch(ProcessCompleted(
process,
result
));
return ProcessResult(
process.pid,
result,
await _readOutput(process.stdout),
await _readOutput(process.stderr)
);
}
/// Kills a process
Future<void> kill(int pid, [ProcessSignal signal = ProcessSignal.sigterm]) async {
var process = _processes[pid];
if (process == null) return;
process.kill(signal);
await _events.dispatch(ProcessKilled(process));
_processes.remove(pid);
}
/// Gets active processes
List<Process> get activeProcesses => List.from(_processes.values);
/// Reads process output
Future<String> _readOutput(Stream<List<int>> stream) async {
var buffer = StringBuffer();
await for (var data in stream) {
buffer.write(String.fromCharCodes(data));
}
return buffer.toString();
}
}
2. Process Pool
/// Process pool for parallel execution
class ProcessPool {
/// Maximum concurrent processes
final int concurrency;
/// Process manager
final ProcessManager _manager;
/// Active processes
final Set<Process> _active = {};
/// Pending commands
final Queue<PendingCommand> _pending = Queue();
ProcessPool(this._manager, {this.concurrency = 5});
/// Starts a process in the pool
Future<ProcessResult> start(
String command, [
List<String>? arguments,
ProcessOptions? options
]) async {
var pending = PendingCommand(
command,
arguments,
options
);
_pending.add(pending);
await _processQueue();
return await pending.future;
}
/// Processes pending commands
Future<void> _processQueue() async {
while (_active.length < concurrency && _pending.isNotEmpty) {
var command = _pending.removeFirst();
await _startProcess(command);
}
}
/// Starts a process
Future<void> _startProcess(PendingCommand command) async {
var process = await _manager.start(
command.command,
command.arguments,
command.options
);
_active.add(process);
process.exitCode.then((result) {
_active.remove(process);
command.complete(ProcessResult(
process.pid,
result,
'',
''
));
_processQueue();
});
}
}
3. Process Events
/// Process started event
class ProcessStarted {
/// The started process
final Process process;
ProcessStarted(this.process);
}
/// Process completed event
class ProcessCompleted {
/// The completed process
final Process process;
/// Exit code
final int exitCode;
ProcessCompleted(this.process, this.exitCode);
}
/// Process killed event
class ProcessKilled {
/// The killed process
final Process process;
ProcessKilled(this.process);
}
/// Process failed event
class ProcessFailed {
/// The failed process
final Process process;
/// Error details
final Object error;
ProcessFailed(this.process, this.error);
}
4. Process Options
/// Process execution options
class ProcessOptions {
/// Working directory
final String? workingDirectory;
/// Environment variables
final Map<String, String>? environment;
/// Include parent environment
final bool includeParentEnvironment;
/// Run in shell
final bool runInShell;
/// Process timeout
final Duration? timeout;
/// Idle timeout
final Duration? idleTimeout;
/// Retry attempts
final int retryAttempts;
/// Retry delay
final Duration retryDelay;
ProcessOptions({
this.workingDirectory,
this.environment,
this.includeParentEnvironment = true,
this.runInShell = false,
this.timeout,
this.idleTimeout,
this.retryAttempts = 0,
this.retryDelay = const Duration(seconds: 1)
});
}
Integration Examples
1. Basic Process Execution
// Run process
var result = await processManager.run('ls', ['-la']);
print('Output: ${result.stdout}');
print('Exit code: ${result.exitCode}');
// Start long-running process
var process = await processManager.start('server', ['--port=8080']);
await process.exitCode; // Wait for completion
2. Process Pool
// Create process pool
var pool = ProcessPool(processManager, concurrency: 3);
// Run multiple processes
await Future.wait([
pool.start('task1'),
pool.start('task2'),
pool.start('task3'),
pool.start('task4') // Queued until slot available
]);
3. Process Events
// Listen for process events
events.listen<ProcessStarted>((event) {
print('Process ${event.process.pid} started');
});
events.listen<ProcessCompleted>((event) {
print('Process ${event.process.pid} completed with code ${event.exitCode}');
});
// Start process
await processManager.start('long-task');
Testing
void main() {
group('Process Manager', () {
test('runs processes', () async {
var manager = ProcessManager(container, events);
var result = await manager.run('echo', ['Hello']);
expect(result.exitCode, equals(0));
expect(result.stdout, contains('Hello'));
});
test('handles process failure', () async {
var manager = ProcessManager(container, events);
expect(
() => manager.run('invalid-command'),
throwsA(isA<ProcessException>())
);
});
});
group('Process Pool', () {
test('limits concurrent processes', () async {
var pool = ProcessPool(manager, concurrency: 2);
var started = <String>[];
events.listen<ProcessStarted>((event) {
started.add(event.process.pid.toString());
});
await Future.wait([
pool.start('task1'),
pool.start('task2'),
pool.start('task3')
]);
expect(started.length, equals(3));
expect(started.take(2).length, equals(2));
});
});
}
Next Steps
- Implement core process features
- Add process pool
- Add process events
- Add retry handling
- Write tests
- Add benchmarks
Development Guidelines
1. Getting Started
Before implementing process features:
- Review Getting Started Guide
- Check Laravel Compatibility Roadmap
- Follow Testing Guide
- Use Foundation Integration Guide
- Review Events Package Specification
- Review Queue Package Specification
2. Implementation Process
For each process feature:
- Write tests following Testing Guide
- Implement following Laravel patterns
- Document following Getting Started Guide
- Integrate following Foundation Integration Guide
3. Quality Requirements
All implementations must:
- Pass all tests (see Testing Guide)
- Meet Laravel compatibility requirements
- Follow integration patterns (see Foundation Integration Guide)
- Support event integration (see Events Package Specification)
- Support queue integration (see Queue Package Specification)
4. Integration Considerations
When implementing process features:
- Follow patterns in Foundation Integration Guide
- Ensure Laravel compatibility per Laravel Compatibility Roadmap
- Use testing approaches from Testing Guide
- Follow development setup in Getting Started Guide
5. Performance Guidelines
Process system must:
- Handle concurrent processes efficiently
- Manage system resources
- Support process pooling
- Scale with process count
- Meet performance targets in Laravel Compatibility Roadmap
6. Testing Requirements
Process tests must:
- Cover all process operations
- Test concurrent execution
- Verify event handling
- Check resource cleanup
- Follow patterns in Testing Guide
7. Documentation Requirements
Process documentation must:
- Explain process patterns
- Show pool examples
- Cover error handling
- Include performance tips
- Follow standards in Getting Started Guide