Index: tools/testing/dart/test_runner.dart |
diff --git a/tools/testing/dart/test_runner.dart b/tools/testing/dart/test_runner.dart |
index ac80516b978b71fcb18f1862368ef165fcd78661..297e72c9fe37a5d985a1b0d66dc1f040891d3242 100644 |
--- a/tools/testing/dart/test_runner.dart |
+++ b/tools/testing/dart/test_runner.dart |
@@ -365,14 +365,25 @@ class AnalysisCommand extends ProcessCommand { |
} |
class VmCommand extends ProcessCommand { |
+ final bool needsDFERunner; |
VmCommand._(String executable, List<String> arguments, |
- Map<String, String> environmentOverrides) |
+ Map<String, String> environmentOverrides, |
+ bool this.needsDFERunner) |
: super._("vm", executable, arguments, environmentOverrides); |
+ |
+ void _buildHashCode(HashCodeBuilder builder) { |
+ super._buildHashCode(builder); |
+ builder.add(needsDFERunner); |
+ } |
+ |
+ bool _equal(VmCommand other) => |
+ super._equal(other) && needsDFERunner == other.needsDFERunner; |
} |
class VmBatchCommand extends ProcessCommand implements VmCommand { |
final String dartFile; |
final bool checked; |
+ final needsDFERunner = false; |
VmBatchCommand._(String executable, String dartFile, List<String> arguments, |
Map<String, String> environmentOverrides, {this.checked: true}) |
@@ -733,8 +744,8 @@ class CommandBuilder { |
} |
VmCommand getVmCommand(String executable, List<String> arguments, |
- Map<String, String> environmentOverrides) { |
- var command = new VmCommand._(executable, arguments, environmentOverrides); |
+ Map<String, String> environmentOverrides, {bool needsDFERunner: false}) { |
+ var command = new VmCommand._(executable, arguments, environmentOverrides, needsDFERunner); |
return _getUniqueCommand(command); |
} |
@@ -1592,6 +1603,7 @@ class AnalysisCommandOutputImpl extends CommandOutputImpl { |
class VmCommandOutputImpl extends CommandOutputImpl |
with UnittestSuiteMessagesMixin { |
+ static const DART_VM_EXITCODE_DFE_ERROR = 252; |
static const DART_VM_EXITCODE_COMPILE_TIME_ERROR = 254; |
static const DART_VM_EXITCODE_UNCAUGHT_EXCEPTION = 255; |
@@ -1601,6 +1613,7 @@ class VmCommandOutputImpl extends CommandOutputImpl |
Expectation result(TestCase testCase) { |
// Handle crashes and timeouts first |
+ if (exitCode == DART_VM_EXITCODE_DFE_ERROR) return Expectation.DARTK_CRASH; |
if (hasCrashed) return Expectation.CRASH; |
if (hasTimedOut) return Expectation.TIMEOUT; |
@@ -1915,8 +1928,9 @@ class RunningProcess { |
List<String> diagnostics = <String>[]; |
bool compilationSkipped = false; |
Completer<CommandOutput> completer; |
+ List<String> preArguments; |
- RunningProcess(this.command, this.timeout); |
+ RunningProcess(this.command, this.timeout, {this.preArguments}); |
Future<CommandOutput> run() { |
completer = new Completer<CommandOutput>(); |
@@ -1932,8 +1946,12 @@ class RunningProcess { |
_commandComplete(0); |
} else { |
var processEnvironment = _createProcessEnvironment(); |
+ var args = command.arguments; |
+ if (preArguments != null) { |
+ args = []..addAll(preArguments)..addAll(args); |
+ } |
Future processFuture = io.Process.start( |
- command.executable, command.arguments, |
+ command.executable, args, |
environment: processEnvironment, |
workingDirectory: command.workingDirectory); |
processFuture.then((io.Process process) { |
@@ -2087,7 +2105,7 @@ class RunningProcess { |
} |
} |
-class BatchRunnerProcess { |
+class BatchRunnerProcess { |
Completer<CommandOutput> _completer; |
ProcessCommand _command; |
List<String> _arguments; |
@@ -2108,8 +2126,6 @@ class BatchRunnerProcess { |
DateTime _startTime; |
Timer _timer; |
- BatchRunnerProcess(); |
- |
Future<CommandOutput> runCommand(String runnerType, ProcessCommand command, |
int timeout, List<String> arguments) { |
assert(_completer == null); |
@@ -2306,6 +2322,99 @@ class BatchRunnerProcess { |
} |
} |
+class BatchDFEProcess { |
+ io.Process _process; |
+ int _port = -1; |
+ Function _processExitHandler; |
+ |
+ Completer terminating = null; |
+ |
+ bool locked = false; |
+ |
+ Future<int> acquire() async { |
+ try { |
+ assert(!locked); |
+ locked = true; |
+ if (_process == null) { |
+ await _startProcess(); |
+ } |
+ return _port; |
+ } catch(e) { |
+ locked = false; |
+ rethrow; |
+ } |
+ } |
+ |
+ void release() { |
+ locked = false; |
+ } |
+ |
+ Future terminate() { |
+ locked = true; |
+ if (_process == null) { |
+ return new Future.value(true); |
+ } |
+ if (terminating == null) { |
+ terminating = new Completer(); |
+ _process.kill(); |
+ } |
+ return terminating.future; |
+ } |
+ |
+ _onExit(exitCode) { |
+ if (terminating != null) { |
+ terminating.complete(); |
+ return; |
+ } |
+ |
+ _process = null; |
+ locked = false; |
+ _port = -1; |
+ } |
+ |
+ static Future<String> _firstLine(stream) { |
+ var completer = new Completer<String>(); |
+ var first = true; |
+ stream.transform(UTF8.decoder) |
+ .transform(new LineSplitter()) |
+ .listen((line) { |
+ if (first) { |
+ completer.complete(line); |
+ first = false; |
+ } |
+ // We need to drain a pipe continuously. |
+ }); |
+ return completer.future; |
+ } |
+ |
+ Future _startProcess() async { |
+ final executable = io.Platform.executable; |
+ final arguments = ['runtime/tools/kernel-service.dart', '--batch']; |
+ |
+ try { |
+ _port = -1; |
+ _process = await io.Process.start(executable, arguments); |
+ _process.exitCode.then(_onExit); |
+ _process.stderr.drain(); |
+ |
+ final readyMsg = await _firstLine(_process.stdout); |
+ final data = readyMsg.split(' '); |
+ assert(data[0] == 'READY'); |
+ |
+ _port = int.parse(data[1]); |
+ } catch (e) { |
+ print("Process error:"); |
+ print(" Command: $executable ${arguments.join(' ')}"); |
+ print(" Error: $e"); |
+ // If there is an error starting a batch process, chances are that |
+ // it will always fail. So rather than re-trying a 1000+ times, we |
+ // exit. |
+ io.exit(1); |
+ return true; |
+ } |
+ } |
+} |
+ |
/** |
* [TestCaseEnqueuer] takes a list of TestSuites, generates TestCases and |
* builds a dependency graph of all commands in every TestSuite. |
@@ -2614,6 +2723,8 @@ class CommandExecutorImpl implements CommandExecutor { |
// We keep a BrowserTestRunner for every configuration. |
final _browserTestRunners = new Map<Map, BrowserTestRunner>(); |
+ List<BatchDFEProcess> _dfeProcesses = null; |
+ |
bool _finishing = false; |
CommandExecutorImpl( |
@@ -2638,7 +2749,14 @@ class CommandExecutorImpl implements CommandExecutor { |
return Future.wait(futures); |
} |
- return Future.wait([_terminateBatchRunners(), _terminateBrowserRunners()]); |
+ Future _terminateDFEWorkers() => |
+ Future.wait((_dfeProcesses ?? []).map((p) => p.terminate())); |
+ |
+ return Future.wait([ |
+ _terminateBatchRunners(), |
+ _terminateBrowserRunners(), |
+ _terminateDFEWorkers() |
+ ]); |
} |
Future<CommandOutput> runCommand(node, Command command, int timeout) { |
@@ -2686,6 +2804,11 @@ class CommandExecutorImpl implements CommandExecutor { |
adbDevicePool.releaseDevice(device); |
}); |
}); |
+ } else if (command is VmCommand && command.needsDFERunner) { |
+ final runner = _getDFEProcess(); |
+ return runner.acquire().then((port) { |
+ return new RunningProcess(command, timeout, preArguments: ['-DDFE_WORKER_PORT=${port}']).run(); |
+ }).whenComplete(() => runner.release()); |
} else if (command is VmBatchCommand) { |
var name = command.displayName; |
return _getBatchRunner(command.displayName + command.dartFile) |
@@ -2788,6 +2911,12 @@ class CommandExecutorImpl implements CommandExecutor { |
throw new Exception('Unable to find inactive batch runner.'); |
} |
+ BatchDFEProcess _getDFEProcess() { |
+ _dfeProcesses ??= new List<BatchDFEProcess>.generate(maxProcesses, |
+ (_) => new BatchDFEProcess()); |
+ return _dfeProcesses.firstWhere((runner) => !runner.locked); |
+ } |
+ |
Future<CommandOutput> _startBrowserControllerTest( |
BrowserTestCommand browserCommand, int timeout) { |
var completer = new Completer<CommandOutput>(); |