Revert "Reland "[VM/Service] Shut down the VM immediately after the VM Service fails to start during VM initialization""
This reverts commitc2095cb347. Reason for revert: These changes are blocking the Dart SDK -> Flutter roll because they cause some Fuchsia tests to fail. Original change's description: > Reland "[VM/Service] Shut down the VM immediately after the VM Service fails to start during VM initialization" > > This reverts commitd510876d9e. > > Reason for revert: g3 and Golem have been made compatible with this CL. > See b/409535026 and > https://chrome-internal-review.googlesource.com/c/golem/+/8345341. > > TEST=pkg/vm_service/test/failure_to_start_vm_service_after_vm_is_initialized_test, > pkg/vm_service/test/failure_to_start_vm_service_during_vm_initialization_test > > Original change's description: > > Revert "[VM/Service] Shut down the VM immediately after the VM Service fails to start during VM initialization" > > > > This reverts commit56ccf437e6. > > > > Reason for revert: b/409535026 > > > > TEST=ci > > > > Original change's description: > > > [VM/Service] Shut down the VM immediately after the VM Service fails to start during VM initialization > > > > > > TEST=pkg/vm_service/test/failure_to_start_vm_service_after_vm_is_initialized_test, > > > pkg/vm_service/test/failure_to_start_vm_service_during_vm_initialization_test > > > > > > Fixes: https://github.com/dart-lang/sdk/issues/60256 > > > Change-Id: I0543ab26e5721a4048136f27e8f4429bef04920f > > > Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/416300 > > > Commit-Queue: Derek Xu <derekx@google.com> > > > Reviewed-by: Ben Konyi <bkonyi@google.com> > > > > Change-Id: I61eb42f0f00ad97e95e3ebf19990fe75d2d416aa > > Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/421622 > > Reviewed-by: Derek Xu <derekx@google.com> > > Commit-Queue: Ivan Inozemtsev <iinozemtsev@google.com> > > Reviewed-by: Ben Konyi <bkonyi@google.com> > > Change-Id: Ieba880b7b298e491055c3f6049bab6772b1f8aa3 > Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/434960 > Reviewed-by: Ben Konyi <bkonyi@google.com> > Commit-Queue: Derek Xu <derekx@google.com> No-Presubmit: true No-Tree-Checks: true No-Try: true Change-Id: I03b2371728de91e4e20008a879280c1ccab4d07c Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/435540 Reviewed-by: Alexander Markov <alexmarkov@google.com> Bot-Commit: Rubber Stamper <rubber-stamper@appspot.gserviceaccount.com> Commit-Queue: Derek Xu <derekx@google.com>
This commit is contained in:
+1
-3
@@ -139,8 +139,6 @@ vm_service/test/enhanced_enum_test: SkipByDesign # Debugger is disabled in AOT m
|
||||
vm_service/test/eval_*test: SkipByDesign # Debugger is disabled in AOT mode.
|
||||
vm_service/test/evaluate_*test: SkipByDesign # Debugger is disabled in AOT mode.
|
||||
vm_service/test/external_compilation_service_test: SkipByDesign # Spawns a secondary process.
|
||||
vm_service/test/failure_to_start_vm_service_after_vm_is_initialized_test: SkipByDesign # Spawns a child process from source, and the codepath under test is the same in JIT and AOT.
|
||||
vm_service/test/failure_to_start_vm_service_during_vm_initialization_test: SkipByDesign # Spawns a child process from source, and the codepath under test is the same in JIT and AOT.
|
||||
vm_service/test/field_script_test: SkipByDesign # Debugger is disabled in AOT mode.
|
||||
vm_service/test/forward_compile_expression_error_from_external_client_with_dds_test: SkipByDesign # Debugger is disabled in AOT mode.
|
||||
vm_service/test/forward_compile_expression_error_from_external_client_without_dds_test: SkipByDesign # Debugger is disabled in AOT mode.
|
||||
@@ -193,7 +191,7 @@ vm_service/test/regress_45684_test: SkipByDesign # Debugger is disabled in AOT m
|
||||
vm_service/test/regress_46419_test: SkipByDesign # Debugger is disabled in AOT mode.
|
||||
vm_service/test/regress_46559_test: SkipByDesign # Debugger is disabled in AOT mode.
|
||||
vm_service/test/regress_48279_test: SkipByDesign # Debugger is disabled in AOT mode.
|
||||
vm_service/test/regress_55559_test: SkipByDesign # Spawns a child process from source, and the codepath under test is the same in JIT and AOT.
|
||||
vm_service/test/regress_55559_test: SkipByDesign # Spawns a child process from source.
|
||||
vm_service/test/regress_60396_test: SkipByDesign # Debugger is disabled in AOT mode.
|
||||
vm_service/test/regress_88104_test: SkipByDesign # Debugger is disabled in AOT mode.
|
||||
vm_service/test/reload_sources_rpc_triggers_isolate_reload_event_test: SkipByDesign # Hot reload is disabled in AOT mode.
|
||||
|
||||
@@ -6,18 +6,10 @@ import 'dart:convert';
|
||||
import 'dart:io';
|
||||
|
||||
// TODO(bkonyi): Share this logic with _ServiceTesteeRunner.launch.
|
||||
Future<(Process, Uri?)> spawnDartProcess(
|
||||
Future<(Process, Uri)> spawnDartProcess(
|
||||
String script, {
|
||||
bool enableDds = true,
|
||||
int vmServicePort = 0,
|
||||
|
||||
/// If true, the second element in the returned record will be a [Uri] that
|
||||
/// can be used to connect to the VM Service running on the testee. If false,
|
||||
/// the second element in the returned record will be null.
|
||||
bool returnServiceUri = true,
|
||||
bool serveObservatory = true,
|
||||
required bool pauseOnStart,
|
||||
required bool pauseOnExit,
|
||||
bool pauseOnStart = true,
|
||||
bool disableServiceAuthCodes = false,
|
||||
bool subscribeToStdio = true,
|
||||
}) async {
|
||||
@@ -27,18 +19,16 @@ Future<(Process, Uri?)> spawnDartProcess(
|
||||
final serviceInfoFile = await File.fromUri(serviceInfoUri).create();
|
||||
|
||||
final arguments = [
|
||||
if (!enableDds) '--no-dds',
|
||||
'--observe=$vmServicePort',
|
||||
'--no-dds',
|
||||
'--observe=0',
|
||||
if (!serveObservatory) '--no-serve-observatory',
|
||||
if (pauseOnStart) '--pause-isolates-on-start',
|
||||
if (pauseOnExit) '--pause-isolates-on-exit',
|
||||
if (disableServiceAuthCodes) '--disable-service-auth-codes',
|
||||
'--write-service-info=$serviceInfoUri',
|
||||
...Platform.executableArguments,
|
||||
Platform.script.resolve(script).toString(),
|
||||
];
|
||||
final process = await Process.start(executable, arguments);
|
||||
|
||||
if (subscribeToStdio) {
|
||||
process.stdout
|
||||
.transform(utf8.decoder)
|
||||
@@ -47,11 +37,6 @@ Future<(Process, Uri?)> spawnDartProcess(
|
||||
.transform(utf8.decoder)
|
||||
.listen((line) => print('TESTEE ERR: $line'));
|
||||
}
|
||||
|
||||
if (!returnServiceUri) {
|
||||
return (process, null);
|
||||
}
|
||||
|
||||
while ((await serviceInfoFile.length()) <= 5) {
|
||||
await Future.delayed(const Duration(milliseconds: 50));
|
||||
}
|
||||
|
||||
@@ -1,92 +0,0 @@
|
||||
// Copyright (c) 2025, the Dart project authors. Please see the AUTHORS file
|
||||
// for details. All rights reserved. Use of this source code is governed by a
|
||||
// BSD-style license that can be found in the LICENSE file.
|
||||
|
||||
// Ensures that the VM does not shut down when an attempt to start the VM
|
||||
// Service via SIGQUIT fails. The VM Service SIGQUIT handler shares nearly all
|
||||
// of its code with dart:developer's controlWebServer, so this effecitvely tests
|
||||
// that function too.
|
||||
|
||||
import 'dart:async' show Completer;
|
||||
import 'dart:convert' show utf8;
|
||||
import 'dart:io'
|
||||
show HttpServer, InternetAddress, Platform, Process, ProcessSignal;
|
||||
|
||||
import 'package:test/test.dart';
|
||||
|
||||
import 'common/utils.dart';
|
||||
|
||||
void main() {
|
||||
HttpServer? server;
|
||||
Process? process;
|
||||
|
||||
tearDown(() async {
|
||||
await server?.close();
|
||||
server = null;
|
||||
process?.kill();
|
||||
process = null;
|
||||
});
|
||||
|
||||
void runTest({required final bool enableDds}) {
|
||||
test(
|
||||
'VM does not shut down when the VM Service fails to start after the VM '
|
||||
'is initialized${enableDds ? '' : ' with --disable-dds'}',
|
||||
() async {
|
||||
const vmServicePort = 8282;
|
||||
|
||||
final (spawnedProcess, _) = await spawnDartProcess(
|
||||
// We reuse 'sigquit_starts_service_script.dart' here because it just
|
||||
// waits in a loop.
|
||||
'sigquit_starts_service_script.dart',
|
||||
enableDds: enableDds,
|
||||
vmServicePort: vmServicePort,
|
||||
pauseOnStart: false,
|
||||
pauseOnExit: true,
|
||||
subscribeToStdio: false,
|
||||
);
|
||||
process = spawnedProcess;
|
||||
|
||||
// Listen for the messages that should be printed when we toggle the VM
|
||||
// Service.
|
||||
final vmServiceShutDownCompleter = Completer<void>();
|
||||
process!.stdout.transform(utf8.decoder).listen((message) {
|
||||
if (message.contains('Dart VM service no longer listening on ')) {
|
||||
vmServiceShutDownCompleter.complete();
|
||||
}
|
||||
});
|
||||
final vmServiceFailedToStartCompleter = Completer<void>();
|
||||
process!.stderr.transform(utf8.decoder).listen((message) {
|
||||
if (message.contains('Could not start the VM service')) {
|
||||
vmServiceFailedToStartCompleter.complete();
|
||||
}
|
||||
});
|
||||
|
||||
// Shut down the VM Service running in the testee.
|
||||
process!.kill(ProcessSignal.sigquit);
|
||||
await vmServiceShutDownCompleter.future;
|
||||
// Wait a bit more to make sure that [vmServicePort] is free.
|
||||
await Future.delayed(const Duration(seconds: 3));
|
||||
|
||||
// Bind an HTTP server to [vmServicePort].
|
||||
server = await HttpServer.bind(
|
||||
InternetAddress.loopbackIPv4,
|
||||
vmServicePort,
|
||||
);
|
||||
|
||||
// Try restarting the VM Service running in the testee. This should fail
|
||||
// because [server] is bound to [vmServicePort].
|
||||
process!.kill(ProcessSignal.sigquit);
|
||||
await vmServiceFailedToStartCompleter.future;
|
||||
|
||||
process!.kill();
|
||||
// Check that the process only exited after receiving SIGTERM, and not
|
||||
// when the VM Service failed to start.
|
||||
expect(await process!.exitCode, -ProcessSignal.sigterm.signalNumber);
|
||||
},
|
||||
skip: Platform.isWindows,
|
||||
);
|
||||
}
|
||||
|
||||
runTest(enableDds: true);
|
||||
runTest(enableDds: false);
|
||||
}
|
||||
@@ -1,72 +0,0 @@
|
||||
// Copyright (c) 2025, the Dart project authors. Please see the AUTHORS file
|
||||
// for details. All rights reserved. Use of this source code is governed by a
|
||||
// BSD-style license that can be found in the LICENSE file.
|
||||
|
||||
// Regression test for https://github.com/dart-lang/sdk/issues/60256.
|
||||
//
|
||||
// Ensures that the VM shuts down immediately after the VM Service fails to
|
||||
// start during VM initialization. The specific problem that motivated this test
|
||||
// was that the VM used to get stuck paused at exit when the VM Service failed
|
||||
// to start during initialization and `--pause-isolates-on-exit` was supplied.
|
||||
|
||||
import 'dart:convert' show utf8;
|
||||
import 'dart:io' show HttpServer, InternetAddress, Process;
|
||||
|
||||
import 'package:test/test.dart';
|
||||
|
||||
import 'common/utils.dart';
|
||||
|
||||
void main() {
|
||||
HttpServer? server;
|
||||
Process? process;
|
||||
|
||||
tearDown(() async {
|
||||
await server?.close();
|
||||
server = null;
|
||||
process?.kill();
|
||||
process = null;
|
||||
});
|
||||
|
||||
void runTest({required final bool enableDds}) {
|
||||
test(
|
||||
'Regress 60256: VM shuts down immediately after the VM Service fails to '
|
||||
'start during VM initialization${enableDds ? '' : ' with --disable-dds'}',
|
||||
() async {
|
||||
server = await HttpServer.bind(InternetAddress.loopbackIPv4, 0);
|
||||
|
||||
// Force the testee VM Service to fail to start by making it try to bind
|
||||
// to the same address [server] is already running on.
|
||||
final (spawnedProcess, _) = await spawnDartProcess(
|
||||
// We expect the VM to shut down before running the script, so we just
|
||||
// pass an arbitrary script here.
|
||||
'regress_55559_script.dart',
|
||||
enableDds: enableDds,
|
||||
vmServicePort: server!.port,
|
||||
returnServiceUri: false,
|
||||
pauseOnStart: false,
|
||||
pauseOnExit: true,
|
||||
subscribeToStdio: false,
|
||||
);
|
||||
process = spawnedProcess;
|
||||
|
||||
final first = utf8.decode(await process!.stderr.first);
|
||||
expect(
|
||||
first,
|
||||
allOf(
|
||||
contains('Could not start the VM service'),
|
||||
contains(
|
||||
'Failed to create server socket',
|
||||
),
|
||||
),
|
||||
);
|
||||
// Ensure that the VM terminates instead of hanging.
|
||||
final exitCode = await process!.exitCode;
|
||||
// 255 is the value of kErrorExitCode in runtime/bin/error_exit.h.
|
||||
expect(exitCode, 255);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
runTest(enableDds: true);
|
||||
runTest(enableDds: false);
|
||||
}
|
||||
@@ -30,21 +30,10 @@ void main() {
|
||||
}
|
||||
|
||||
setUp(() async {
|
||||
final spawnDartProcessResult = await spawnDartProcess(
|
||||
state = await spawnDartProcess(
|
||||
'regress_55559_script.dart',
|
||||
enableDds: false,
|
||||
pauseOnStart: false,
|
||||
pauseOnExit: false,
|
||||
);
|
||||
|
||||
if (spawnDartProcessResult case (final Process process, final Uri uri)) {
|
||||
state = (process, uri);
|
||||
} else {
|
||||
fail(
|
||||
"The implementation of spawnDartProcess's returnServiceUri parameter"
|
||||
'is incorrect',
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
tearDown(() {
|
||||
|
||||
@@ -220,35 +220,13 @@ Future<List<Map<String, dynamic>>> listFilesCallback(Uri dirPath) async {
|
||||
|
||||
Uri? serverInformationCallback() => server.serverAddress;
|
||||
|
||||
/// Thrown when either the VM Service HTTP server or DDS fails to start.
|
||||
class _StartupException implements Exception {
|
||||
final String message;
|
||||
|
||||
_StartupException(this.message);
|
||||
}
|
||||
|
||||
/// Toggles the running state of the VM Service HTTP server. If
|
||||
/// [server._waitForDdsToAdvertiseService] is true, toggles DDS alongside the VM
|
||||
/// Service HTTP server.
|
||||
///
|
||||
/// Logs error messages to stderr and completes the returned [Future] with
|
||||
/// [false] if an attempt is made to enable the VM Service HTTP server or DDS
|
||||
/// and the attempt fails. Completes the returned [Future] with [true]
|
||||
/// otherwise.
|
||||
Future<bool> _toggleWebServer() async {
|
||||
Future<void> _toggleWebServer() async {
|
||||
// Toggle HTTP server.
|
||||
if (server.running) {
|
||||
await server.shutdown(true);
|
||||
await VMService().clearState();
|
||||
return true;
|
||||
} else {
|
||||
try {
|
||||
await server.startup();
|
||||
return true;
|
||||
} on _StartupException catch (e) {
|
||||
stderr.writeln(e.message);
|
||||
return false;
|
||||
}
|
||||
await server.startup();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -284,7 +262,7 @@ void _registerSignalHandler() {
|
||||
}
|
||||
_signalSubscription = signalWatch(
|
||||
ProcessSignal.sigquit,
|
||||
).listen((_) => unawaited(_toggleWebServer()));
|
||||
).listen((_) => _toggleWebServer());
|
||||
}
|
||||
|
||||
@pragma('vm:entry-point', !bool.fromEnvironment('dart.vm.product'))
|
||||
@@ -319,13 +297,7 @@ void main() {
|
||||
);
|
||||
|
||||
if (_autoStart) {
|
||||
unawaited(
|
||||
_toggleWebServer().then((wasSuccessful) {
|
||||
if (!wasSuccessful) {
|
||||
exit(vmErrorExitCode);
|
||||
}
|
||||
}),
|
||||
);
|
||||
_toggleWebServer();
|
||||
}
|
||||
_registerSignalHandler();
|
||||
}
|
||||
|
||||
@@ -4,9 +4,6 @@
|
||||
|
||||
part of vmservice_io;
|
||||
|
||||
// This must be kept in sync with kErrorExitCode in runtime/bin/error_exit.h.
|
||||
const vmErrorExitCode = 255;
|
||||
|
||||
// TODO(48602): deprecate SILENT_OBSERVATORY in favor of SILENT_VM_SERVICE
|
||||
bool silentObservatory = bool.fromEnvironment('SILENT_OBSERVATORY');
|
||||
bool silentVMService = bool.fromEnvironment('SILENT_VM_SERVICE');
|
||||
@@ -145,20 +142,13 @@ class HttpRequestClient extends Client {
|
||||
/// Responsible for launching a DevTools instance when the service is started
|
||||
/// via SIGQUIT.
|
||||
class _DebuggingSession {
|
||||
/// Starts DDS.
|
||||
///
|
||||
/// Throws a [_StartupException] if it fails to start.
|
||||
Future<void> start(
|
||||
Future<bool> start(
|
||||
Uri serverAddress,
|
||||
String host,
|
||||
String port,
|
||||
bool disableServiceAuthCodes,
|
||||
bool enableDevTools,
|
||||
) async {
|
||||
void _throwStartupException(String details) {
|
||||
throw _StartupException('Could not start the VM service:\n$details');
|
||||
}
|
||||
|
||||
// This code is part of the SDK and it is ok to have a reference to the
|
||||
// internals of the Dart SDK in terms of location of the snapshot etc.
|
||||
// It is more efficient doing it this way instead of invoking the Dart CLI
|
||||
@@ -190,25 +180,29 @@ class _DebuggingSession {
|
||||
FileSystemEntityType.notFound) {
|
||||
executable = dart;
|
||||
}
|
||||
try {
|
||||
_process = await Process.start(executable, [
|
||||
script,
|
||||
'--vm-service-uri=$serverAddress',
|
||||
'--bind-address=$host',
|
||||
'--bind-port=$port',
|
||||
if (disableServiceAuthCodes) '--disable-service-auth-codes',
|
||||
if (enableDevTools) '--serve-devtools',
|
||||
if (_enableServicePortFallback) '--enable-service-port-fallback',
|
||||
], mode: ProcessStartMode.detachedWithStdio);
|
||||
} on ProcessException catch (e) {
|
||||
_throwStartupException('Process.start failed: ${e.message}');
|
||||
var process = await Process.start(executable, [
|
||||
script,
|
||||
'--vm-service-uri=$serverAddress',
|
||||
'--bind-address=$host',
|
||||
'--bind-port=$port',
|
||||
if (disableServiceAuthCodes) '--disable-service-auth-codes',
|
||||
if (enableDevTools) '--serve-devtools',
|
||||
if (_enableServicePortFallback) '--enable-service-port-fallback',
|
||||
], mode: ProcessStartMode.detachedWithStdio);
|
||||
if (process == null) {
|
||||
stderr.writeln('Could not start the VM service: Process.start failed\n');
|
||||
return false;
|
||||
}
|
||||
_process = process;
|
||||
|
||||
// DDS will close stderr once it's finished launching.
|
||||
final launchResultStderr = await _process.stderr
|
||||
.transform(utf8.decoder)
|
||||
.join();
|
||||
|
||||
void printError(String details) =>
|
||||
stderr.writeln('Could not start the VM service: $details');
|
||||
|
||||
try {
|
||||
final result = json.decode(launchResultStderr) as Map<String, dynamic>;
|
||||
if (result case {'state': 'started'}) {
|
||||
@@ -223,13 +217,16 @@ class _DebuggingSession {
|
||||
serverPrint('The Dart Tooling Daemon (DTD) is available at: $dtdUri');
|
||||
}
|
||||
} else {
|
||||
_throwStartupException(result['error'] ?? result);
|
||||
printError(result['error'] ?? result);
|
||||
return false;
|
||||
}
|
||||
} catch (_) {
|
||||
// Malformed JSON was likely encountered, so output the entirety of
|
||||
// stderr in the error message.
|
||||
_throwStartupException("Couldn't parse JSON: ${launchResultStderr}");
|
||||
printError("Couldn't parse JSON: ${launchResultStderr}");
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void shutdown() => _process.kill();
|
||||
@@ -289,10 +286,6 @@ class Server {
|
||||
this._enableServicePortFallback,
|
||||
) : _authCodesDisabled = (authCodesDisabled || Platform.isFuchsia);
|
||||
|
||||
/// Starts the VM Service HTTP server. If [_waitForDdsToAdvertiseService] is
|
||||
/// true, starts DDS as well.
|
||||
///
|
||||
/// Throws a [_StartupException] if either of them fails to start.
|
||||
Future<void> startup() async {
|
||||
if (running) {
|
||||
// Already running.
|
||||
@@ -312,7 +305,7 @@ class Server {
|
||||
final startingCompleter = Completer<bool>();
|
||||
_startingCompleter = startingCompleter;
|
||||
// Startup HTTP server.
|
||||
Future<void> startServer() async {
|
||||
Future<bool> startServer() async {
|
||||
try {
|
||||
var address;
|
||||
var addresses = await InternetAddress.lookup(_ip);
|
||||
@@ -331,17 +324,22 @@ class Server {
|
||||
_port = 0;
|
||||
return await startServer();
|
||||
} else {
|
||||
startingCompleter.complete(true);
|
||||
_startingCompleter = null;
|
||||
throw _StartupException(
|
||||
'Could not start the VM service HTTP server:\n'
|
||||
serverPrint(
|
||||
'Could not start Dart VM service HTTP server:\n'
|
||||
'$e\n$st',
|
||||
);
|
||||
_notifyServerState('');
|
||||
onServerAddressChange(null);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
await startServer();
|
||||
if (!(await startServer())) {
|
||||
startingCompleter.complete(true);
|
||||
return;
|
||||
}
|
||||
if (_service.isExiting) {
|
||||
serverPrint(
|
||||
'Dart VM service HTTP server exiting before listening as '
|
||||
@@ -356,29 +354,16 @@ class Server {
|
||||
|
||||
if (_waitForDdsToAdvertiseService) {
|
||||
_ddsInstance = _DebuggingSession();
|
||||
try {
|
||||
await _ddsInstance!.start(
|
||||
serverAddress!,
|
||||
_ddsIP,
|
||||
_ddsPort.toString(),
|
||||
_authCodesDisabled,
|
||||
_serveDevtools,
|
||||
);
|
||||
} on _StartupException {
|
||||
// If DDS fails to start, shut down the HTTP server as well.
|
||||
try {
|
||||
await server.close(force: true);
|
||||
} catch (_) {}
|
||||
_ddsInstance = null;
|
||||
_httpServer = null;
|
||||
startingCompleter.complete(true);
|
||||
_startingCompleter = null;
|
||||
rethrow;
|
||||
}
|
||||
await _ddsInstance!.start(
|
||||
serverAddress!,
|
||||
_ddsIP,
|
||||
_ddsPort.toString(),
|
||||
_authCodesDisabled,
|
||||
_serveDevtools,
|
||||
);
|
||||
} else {
|
||||
await outputConnectionInformation();
|
||||
}
|
||||
|
||||
// Server is up and running.
|
||||
_running = true;
|
||||
_notifyServerState(serverAddress.toString());
|
||||
|
||||
Reference in New Issue
Block a user