Skip to content

Commit 0b879c4

Browse files
authored
Report and shutdown after file watch errors (#3411)
* Report and shutdown after file watch errors * change error codes
1 parent f02a133 commit 0b879c4

File tree

7 files changed

+102
-70
lines changed

7 files changed

+102
-70
lines changed

build_daemon/CHANGELOG.md

+5
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,8 @@
1+
## 3.1.1-dev
2+
3+
- Report file watching errors and stop the daemon.
4+
5+
16
## 3.1.0
27

38
- Add `BuildResults.changedAssets` containing asset URIs changed during a

build_daemon/lib/constants.dart

+6
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,12 @@ const optionsSkew = 'DIFFERENT OPTIONS';
1212

1313
const buildModeFlag = 'build-mode';
1414

15+
/// Daemon shuts down after this timeout if there is no active connection.
16+
const defaultIdleTimeout = Duration(seconds: 30);
17+
18+
const fileChangeEventErrorCode = 101;
19+
const fileChangeStreamClosedErrorCode = 102;
20+
1521
enum BuildMode {
1622
// ignore: constant_identifier_names
1723
Manual,

build_daemon/lib/daemon.dart

+8-7
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ import 'src/server.dart';
2525
class Daemon {
2626
final String _workingDirectory;
2727
final RandomAccessFile? _lock;
28-
final _doneCompleter = Completer();
28+
final _doneCompleter = Completer<int>();
2929

3030
Server? _server;
3131
StreamSubscription? _sub;
@@ -34,7 +34,8 @@ class Daemon {
3434
: _workingDirectory = workingDirectory,
3535
_lock = _tryGetLock(workingDirectory);
3636

37-
Future<void> get onDone => _doneCompleter.future;
37+
/// Returns exit code.
38+
Future<int> get onDone => _doneCompleter.future;
3839

3940
Future<void> stop({String message = '', int failureType = 0}) =>
4041
_server!.stop(message: message, failureType: failureType);
@@ -65,7 +66,7 @@ class Daemon {
6566
ChangeProvider changeProvider, {
6667
Serializers? serializersOverride,
6768
bool Function(BuildTarget, Iterable<WatchEvent>)? shouldBuild,
68-
Duration timeout = const Duration(seconds: 30),
69+
Duration timeout = defaultIdleTimeout,
6970
}) async {
7071
if (_server != null || _lock == null) return;
7172
_handleGracefulExit();
@@ -83,12 +84,12 @@ class Daemon {
8384
var port = await server.listen();
8485
_createPortFile(port);
8586

86-
unawaited(server.onDone.then((_) async {
87-
await _cleanUp();
87+
unawaited(server.onDone.then((exitCode) async {
88+
await _cleanUp(exitCode);
8889
}));
8990
}
9091

91-
Future<void> _cleanUp() async {
92+
Future<void> _cleanUp(int exitCode) async {
9293
await _server?.stop();
9394
await _sub?.cancel();
9495
// We need to close the lock prior to deleting the file.
@@ -97,7 +98,7 @@ class Daemon {
9798
if (workspace.existsSync()) {
9899
workspace.deleteSync(recursive: true);
99100
}
100-
if (!_doneCompleter.isCompleted) _doneCompleter.complete();
101+
if (!_doneCompleter.isCompleted) _doneCompleter.complete(exitCode);
101102
}
102103

103104
void _createPortFile(int port) =>
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,15 @@
11
// Copyright (c) 2019, the Dart project authors. Please see the AUTHORS file
22
// for details. All rights reserved. Use of this source code is governed by a
33
// BSD-style license that can be found in the LICENSE file.
4-
54
import 'dart:async';
65

76
import 'package:build_daemon/change_provider.dart';
87
import 'package:watcher/watcher.dart' show WatchEvent;
98

109
class FakeChangeProvider implements ChangeProvider {
10+
final changeStreamController = StreamController<List<WatchEvent>>();
1111
@override
12-
Stream<List<WatchEvent>> get changes => Stream.empty();
13-
12+
Stream<List<WatchEvent>> get changes => changeStreamController.stream;
1413
@override
1514
Future<List<WatchEvent>> collectChanges() async => [];
1615
}

build_daemon/lib/src/server.dart

+14-18
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
// Copyright (c) 2019, the Dart project authors. Please see the AUTHORS file
22
// for details. All rights reserved. Use of this source code is governed by a
33
// BSD-style license that can be found in the LICENSE file.
4-
54
import 'dart:async';
65
import 'dart:convert';
76
import 'dart:io';
@@ -17,6 +16,7 @@ import 'package:watcher/watcher.dart';
1716
import 'package:web_socket_channel/web_socket_channel.dart';
1817

1918
import '../change_provider.dart';
19+
import '../constants.dart';
2020
import '../daemon_builder.dart';
2121
import '../data/build_request.dart';
2222
import '../data/build_target.dart';
@@ -31,24 +31,19 @@ import 'managers/build_target_manager.dart';
3131
/// Note the server will only notify clients of pertinent events.
3232
class Server {
3333
static final loggerName = 'BuildDaemonServer';
34-
35-
final _isDoneCompleter = Completer();
34+
final _isDoneCompleter = Completer<int>();
3635
final BuildTargetManager _buildTargetManager;
3736
final _pool = Pool(1);
3837
final Serializers _serializers;
3938
final ChangeProvider _changeProvider;
4039
late final Timer _timeout;
41-
4240
HttpServer? _server;
4341
final DaemonBuilder _builder;
4442
// Channels that are interested in the current build.
4543
var _interestedChannels = <WebSocketChannel>{};
46-
4744
final _subs = <StreamSubscription>[];
48-
4945
final _outputStreamController = StreamController<ServerLog>();
5046
late final Stream<ServerLog> _logs;
51-
5247
Server(
5348
this._builder,
5449
Duration timeout,
@@ -61,9 +56,7 @@ class Server {
6156
BuildTargetManager(shouldBuildOverride: shouldBuild) {
6257
_logs = _outputStreamController.stream;
6358
_forwardData();
64-
6559
_handleChanges(changeProvider.changes);
66-
6760
// Stop the server if nobody connects.
6861
_timeout = Timer(timeout, () async {
6962
if (_buildTargetManager.isEmpty) {
@@ -72,7 +65,8 @@ class Server {
7265
});
7366
}
7467

75-
Future<void> get onDone => _isDoneCompleter.future;
68+
/// Returns exit code.
69+
Future<int> get onDone => _isDoneCompleter.future;
7670

7771
/// Starts listening for build daemon clients.
7872
Future<int> listen() async {
@@ -98,7 +92,6 @@ class Server {
9892
_removeChannel(channel);
9993
});
10094
});
101-
10295
var server = _server = await HttpMultiServer.loopback(0);
10396
// Serve requests in an error zone to prevent failures
10497
// when running from another error zone.
@@ -124,7 +117,7 @@ class Server {
124117
await sub.cancel();
125118
}
126119
await _outputStreamController.close();
127-
if (!_isDoneCompleter.isCompleted) _isDoneCompleter.complete();
120+
if (!_isDoneCompleter.isCompleted) _isDoneCompleter.complete(failureType);
128121
}
129122

130123
Future<void> _build(
@@ -134,7 +127,6 @@ class Server {
134127
buildTargets.expand(_buildTargetManager.channels).toSet();
135128
return _builder.build(buildTargets, changes);
136129
});
137-
138130
void _forwardData() {
139131
_subs
140132
..add(_builder.logs.listen((log) {
@@ -147,14 +139,11 @@ class Server {
147139
// Don't serialize or send changed assets if the client isn't interested
148140
// in them.
149141
String? message, messageWithoutChangedAssets;
150-
151142
for (var channel in _interestedChannels) {
152143
var targets = _buildTargetManager.targetsFor(channel);
153144
var wantsChangedAssets = targets
154145
.any((e) => e is DefaultBuildTarget && e.reportChangedAssets);
155-
156146
String messageForChannel;
157-
158147
if (wantsChangedAssets) {
159148
messageForChannel =
160149
message ??= jsonEncode(_serializers.serialize(status));
@@ -163,7 +152,6 @@ class Server {
163152
_serializers
164153
.serialize(status.rebuild((b) => b.changedAssets = null)));
165154
}
166-
167155
channel.sink.add(messageForChannel);
168156
}
169157
}))
@@ -183,7 +171,15 @@ class Server {
183171
var buildTargets = _buildTargetManager.targetsForChanges(changes);
184172
if (buildTargets.isEmpty) return;
185173
await _build(buildTargets, changes);
186-
}).listen((_) {}));
174+
}).listen((_) {}, onError: (e) {
175+
stop(
176+
message: 'Error in file change event: $e',
177+
failureType: fileChangeEventErrorCode);
178+
}, onDone: () {
179+
stop(
180+
message: 'File change stream closed',
181+
failureType: fileChangeStreamClosedErrorCode);
182+
}));
187183
}
188184

189185
void _removeChannel(WebSocketChannel channel) async {

build_daemon/pubspec.yaml

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
name: build_daemon
2-
version: 3.1.0
2+
version: 3.1.1-dev
33
description: A daemon for running Dart builds.
44
repository: https://github.com/dart-lang/build/tree/master/build_daemon
55

0 commit comments

Comments
 (0)