Windows: every start ended itself after 3 s (1.3.0, 1.3.1)
ci / core (push) Successful in 28s
ci / server (push) Successful in 51s
ci / updater-windows (push) Successful in 6m27s

Win32Window::Create calls Destroy() before it makes the window, which
ran FlutterWindow::OnDestroy and armed the 3 s exit timer at launch: the
window showed, then the process ended with code 0, no crash, no close
lines. The timer is only armed with an engine to tear down. Found and
fixed by Fun Maker's agent on Windows (a debugger caught the exit on the
timer's thread).

The smoke runs missed it (they closed at ~4 s anyway): the app now stays
up 10 s, and the scripts require 'closing' and 'quitting' from its own
close.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
foxyzandClaude Opus 5.5 committed 2026-10-17 02:04:55 +03:00
1 parent 8aa1186d29
commit e16cf50f2c
6 files changed
+29 -9

No files matched your search

+12 -4
View File
@@ -12,9 +12,14 @@ import 'ports/player_host.dart';
// in a session player with a video output through the media pipeline, // in a session player with a video output through the media pipeline,
// play a second and close the normal way ([shutDownApp]). Progress goes to // play a second and close the normal way ([shutDownApp]). Progress goes to
// `STARSYNC_SMOKE_OUT` (one line per step: `start`, `opened <ms> render=<mode>`, // `STARSYNC_SMOKE_OUT` (one line per step: `start`, `opened <ms> render=<mode>`,
// `closing`, or `skip <why>` / `fail <why>`), so the script can tell a // `closing`, `quitting`, or `skip <why>` / `fail <why>`), so the script can
// runner that cannot show a window (skip) from a close that hangs or // tell a runner that cannot show a window (skip) from a close that hangs or
// leaves a process behind (fail). // leaves a process behind, or a process that ends on its own (fail). It
// stays up [smokeUptime] first: a start that dies by itself (the 1.3.0
// Windows exit timer) ends before `closing`.
/// How long the smoke run keeps the app up before closing it.
const smokeUptime = Duration(seconds: 10);
/// The smoke run's media, or null for a normal start. /// The smoke run's media, or null for a normal start.
String? get smokeMedia { String? get smokeMedia {
@@ -65,6 +70,9 @@ Future<void> runSmoke(AppGraph graph, String media) async {
} catch (e) { } catch (e) {
step('fail $e'); step('fail $e');
} }
final wait = smokeUptime - sw.elapsed;
if (wait > Duration.zero) await Future.delayed(wait);
step('closing'); step('closing');
await shutDownApp(); // Reached only through the app's own close (not a kill on the way).
await shutDownApp(beforeQuit: () async => step('quitting'));
} }
@@ -80,7 +80,11 @@ bool FlutterWindow::OnCreate() {
void FlutterWindow::OnDestroy() { void FlutterWindow::OnDestroy() {
// The engine and the plugins go now; the app released its players first. // The engine and the plugins go now; the app released its players first.
// Win32Window::Create runs this too, before there is an engine: only a
// real teardown may end the process (else every start dies at 3 s).
if (flutter_controller_) {
ExitAfter(3000); ExitAfter(3000);
}
window_channel_ = nullptr; window_channel_ = nullptr;
if (flutter_controller_) { if (flutter_controller_) {
flutter_controller_ = nullptr; flutter_controller_ = nullptr;
+3 -1
View File
@@ -2293,7 +2293,9 @@ app kills itself (`Process.killPid`, no exit handlers; it only gets there
if the close did not go through); the runners end the process 15 s after if the close did not go through); the runners end the process 15 s after
the first close request (Linux: a `delete-event` handler connected before the first close request (Linux: a `delete-event` handler connected before
window_manager's; Windows: the first `WM_CLOSE`), and Windows also 3 s window_manager's; Windows: the first `WM_CLOSE`), and Windows also 3 s
after the window is destroyed, while the engine and plugins go. after the window is destroyed, while the engine and plugins go (only
with an engine: `Win32Window::Create` runs `OnDestroy` before the window
exists, and a timer armed there ended every start at 3 s).
## Logging ## Logging
+6 -4
View File
@@ -210,10 +210,12 @@ updater tests, the release build and the close smoke run on that build
`STARSYNC_SMOKE=<clip>` (a 5 s clip from ffmpeg, else a WAV the script `STARSYNC_SMOKE=<clip>` (a 5 s clip from ffmpeg, else a WAV the script
writes) under its own profile (`STARSYNC_PROFILE=smoke`), opens the clip writes) under its own profile (`STARSYNC_PROFILE=smoke`), opens the clip
in a player with a video output through the media pipeline in a player with a video output through the media pipeline
(`platform/smoke.dart`), plays a second and closes the normal way; the (`platform/smoke.dart`), plays it, stays up 10 s from its start and
steps go to `STARSYNC_SMOKE_OUT` (`start`, `opened <ms> render=<mode>`, closes the normal way; the steps go to `STARSYNC_SMOKE_OUT` (`start`,
`closing`, or `skip` / `fail` with why). It fails when the app does not `opened <ms> render=<mode>`, `closing`, `quitting` just before the
exit within 45 s or leaves a `starsync` process, skips with a warning window closes, or `skip` / `fail` with why). It fails when the app ends
by itself before `closing` or never reaches `quitting`, does not exit
within 45 s or leaves a `starsync` process, skips with a warning
when the runner cannot bring the app or its video output up, and prints when the runner cannot bring the app or its video output up, and prints
the `smoke:`, `close:` and `mpv: render` lines of the app log. the `smoke:`, `close:` and `mpv: render` lines of the app log.
`tool/smoke_linux.sh` is the same on Linux (needs a display; after `tool/smoke_linux.sh` is the same on Linux (needs a display; after
+2
View File
@@ -34,6 +34,8 @@ grep -qx start "$OUT" 2>/dev/null || { [[ $exited == 1 ]] && { echo "skipped: th
grep -q '^skip ' "$OUT" && { echo "skipped: $(grep '^skip ' "$OUT")"; exit 0; } grep -q '^skip ' "$OUT" && { echo "skipped: $(grep '^skip ' "$OUT")"; exit 0; }
grep -q '^fail ' "$OUT" && { echo "smoke failed: $(grep '^fail ' "$OUT")" >&2; exit 1; } grep -q '^fail ' "$OUT" && { echo "smoke failed: $(grep '^fail ' "$OUT")" >&2; exit 1; }
grep -q '^opened ' "$OUT" || { echo "the media never opened" >&2; exit 1; } grep -q '^opened ' "$OUT" || { echo "the media never opened" >&2; exit 1; }
grep -qx closing "$OUT" || { echo "the app ended on its own before closing" >&2; exit 1; }
grep -qx quitting "$OUT" || { echo "the close did not reach its end (killed on the way?)" >&2; exit 1; }
[[ $exited == 1 ]] || { echo "the app did not close within $LIMIT s (P32)" >&2; exit 1; } [[ $exited == 1 ]] || { echo "the app did not close within $LIMIT s (P32)" >&2; exit 1; }
[[ -z "$left" ]] || { echo "a starsync process was left behind (P32)" >&2; exit 1; } [[ -z "$left" ]] || { echo "a starsync process was left behind (P32)" >&2; exit 1; }
echo "smoke ok: opened and closed in $secs s, no process left" echo "smoke ok: opened and closed in $secs s, no process left"
+2
View File
@@ -64,6 +64,8 @@ if (-not ($lines -contains 'start')) {
if ($lines | Where-Object { $_ -like 'skip *' }) { Write-Warning "skipped: $($lines -like 'skip *')"; exit 0 } if ($lines | Where-Object { $_ -like 'skip *' }) { Write-Warning "skipped: $($lines -like 'skip *')"; exit 0 }
if ($lines | Where-Object { $_ -like 'fail *' }) { Write-Error "smoke failed: $($lines -like 'fail *')"; exit 1 } if ($lines | Where-Object { $_ -like 'fail *' }) { Write-Error "smoke failed: $($lines -like 'fail *')"; exit 1 }
if (-not ($lines | Where-Object { $_ -like 'opened *' })) { Write-Error 'the media never opened'; exit 1 } if (-not ($lines | Where-Object { $_ -like 'opened *' })) { Write-Error 'the media never opened'; exit 1 }
if (-not ($lines -contains 'closing')) { Write-Error "the app ended on its own before closing (exit $($p.ExitCode))"; exit 1 }
if (-not ($lines -contains 'quitting')) { Write-Error 'the close did not reach its end (killed on the way?)'; exit 1 }
if (-not $exited) { Write-Error "the app did not close within $Limit s (P32)"; exit 1 } if (-not $exited) { Write-Error "the app did not close within $Limit s (P32)"; exit 1 }
if ($left) { Write-Error "a starsync process was left behind (P32)"; exit 1 } if ($left) { Write-Error "a starsync process was left behind (P32)"; exit 1 }
Write-Host "smoke ok: opened and closed in $secs s, no process left" Write-Host "smoke ok: opened and closed in $secs s, no process left"