[vm/resident_frontend_server] Use File.create(exclusive:true) instead of links.

This makes resident frontend server more Windows-friendly, uses recently introduced api to ensure only one file creator succeeds.

Fixes https://github.com/dart-lang/sdk/issues/49706
TEST=ci

Change-Id: I520487f4f198cc2b2e9d656dca85607889723ef2
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/256543
Reviewed-by: Siva Annamalai <asiva@google.com>
Commit-Queue: Alexander Aprelev <aam@google.com>
This commit is contained in:
Alexander Aprelev 2022-08-26 21:09:11 +00:00 committed by Commit Bot
parent dfc82df6f7
commit 0e9b056f39
2 changed files with 7 additions and 38 deletions

View file

@ -10,8 +10,6 @@ import 'dart:io'
exit,
File,
InternetAddress,
Link,
Platform,
ProcessSignal,
ServerSocket,
Socket;
@ -34,9 +32,7 @@ import '../frontend_server.dart';
/// required.
const _STAT_GRANULARITY = const Duration(seconds: 1);
const RESIDENT_SERVER_LINK_POSTFIX = '_link';
/// Ensures the symbolic link is removed if ctrl-C is sent to the server.
/// Ensures the info file is removed if Ctrl-C is sent to the server.
/// Mostly used when debugging.
StreamSubscription<ProcessSignal> _cleanupHandler;
@ -427,24 +423,18 @@ Future<Map<String, dynamic>> sendAndReceiveResponse(
}
/// Closes the ServerSocket and removes the [serverInfoFile] that is used
/// to access this instance of the Resident Frontend Server as well as the
/// lock to prevent the concurrent start race.
/// to access this instance of the Resident Frontend Server.
Future<void> residentServerCleanup(
ServerSocket server, File serverInfoFile) async {
final serverFilesystemLock =
Link('${serverInfoFile.path}$RESIDENT_SERVER_LINK_POSTFIX');
try {
if (_cleanupHandler != null) {
_cleanupHandler.cancel();
}
if (serverInfoFile.existsSync()) {
serverInfoFile.deleteSync();
}
} catch (_) {
} finally {
try {
if (serverFilesystemLock.existsSync()) {
serverFilesystemLock.deleteSync();
if (serverInfoFile.existsSync()) {
serverInfoFile.deleteSync();
}
} catch (_) {}
}
@ -469,25 +459,10 @@ Future<StreamSubscription<Socket>> residentListenAndCompile(
InternetAddress address, int port, File serverInfoFile,
{Duration inactivityTimeout = const Duration(minutes: 30)}) async {
ServerSocket server;
// Create a link to the serverInfoFile to ensure that concurrent requests
// to start the server result in only 1 server being started. This
// also ensures that the serverInfoFile is only
// visible once the server is started and ready to receive connections.
// TODO https://github.com/dart-lang/sdk/issues/49647 use exclusive mode
// on File objects
final serverInfoLink =
Link('${serverInfoFile.path}$RESIDENT_SERVER_LINK_POSTFIX');
try {
try {
serverInfoLink.createSync(serverInfoFile.path);
serverInfoFile.createSync(exclusive: true);
} catch (e) {
// TODO: https://github.com/dart-lang/sdk/issues/49647 Using a File
// in exclusive mode removes the need for this check.
if (Platform.isWindows && e.toString().contains('errno = 1314')) {
throw StateError('Dart must be running in Administrator mode '
'or Developer mode must be enabled when '
'using the Resident Frontend Compiler.');
}
throw StateError('A server is already running.');
}
server = await ServerSocket.bind(address, port);
@ -498,9 +473,9 @@ Future<StreamSubscription<Socket>> residentListenAndCompile(
print('Error: $e\n');
return null;
} catch (e) {
// lock was acquired but bind or writing failed
// If we created a file, but bind or writing failed, clean up.
try {
serverInfoLink.deleteSync();
serverInfoFile.deleteSync();
} catch (_) {}
print('Error: $e\n');
return null;

View file

@ -577,9 +577,6 @@ void main() async {
expect(serverSubscription, isNot(null));
expect(startWhileAlreadyRunning, null);
expect(serverInfo.existsSync(), true);
expect(
Link('${serverInfo.path}$RESIDENT_SERVER_LINK_POSTFIX').existsSync(),
true);
final info = serverInfo.readAsStringSync();
final address = InternetAddress(
@ -590,9 +587,6 @@ void main() async {
address, port, ResidentFrontendServer.shutdownCommand);
expect(shutdownResult, equals(<String, dynamic>{"shutdown": true}));
expect(serverInfo.existsSync(), false);
expect(
Link('${serverInfo.path}$RESIDENT_SERVER_LINK_POSTFIX').existsSync(),
false);
});
test('resident server starter', () async {