Release v4.8: fix rare crash from endpoint thrash on a dual-address peer (#16)
The same singer hit a rare crash: their transmitter (COMP3) was reachable at two addresses at once — the VPN address 10.8.0.1 they chose and that machine's wireless 192.168.3.245 — and the discovery-driven endpoint-follow ping-ponged the connection between the two (the log shows four moves in 58 ms) right where the process died with no shutdown line, no managed exception, no dialog: a hard crash from the receiver audio-session teardown/rebuild churn the thrash caused. Fix: the follow loop now never moves off an endpoint that's still answering heartbeats, only follows once the current one has been unreachable for a sustained grace period (6 s), only to an address that is itself answering, and never more than once per cooldown (15 s) — so it can't thrash, and a peer reached on a working address stays put (honours the singer's "just stay on 10.8.0.1"). New endpointUnreachableSinceUtc + lastEndpointMoveUtc state; genuine DHCP/network moves are still followed a few seconds later. Also: a global crash handler (Program.WriteCrashReport on AppDomain.UnhandledException + TaskScheduler.UnobservedTaskException) writes a crash-*.txt into the logs folder, so a future "RemSound just vanished" report leaves a stack behind. Removed a stale doc comment left over from the v4.7 adoption removal. Manual gains a "RemSound closed unexpectedly" troubleshooting entry. Version 4.8; About + RELEASE_NOTES updated. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.8
parent
64d5cb5a86
commit
0df2a576fc
@@ -12,6 +12,27 @@ internal static class Program
|
||||
// window. volatile for cross-thread visibility; RestoreFromTray marshals to the UI thread.
|
||||
private static volatile MainForm? activeMainForm;
|
||||
|
||||
// Writes an otherwise-fatal exception to a timestamped crash file in the logs folder, so a
|
||||
// "RemSound just disappeared, no dialog" report (#16) leaves a stack behind to diagnose instead
|
||||
// of nothing. Best-effort and self-contained — a crash handler must never throw.
|
||||
private static void WriteCrashReport(string source, Exception? ex)
|
||||
{
|
||||
try
|
||||
{
|
||||
var dir = AppConfig.LogsDirectory;
|
||||
Directory.CreateDirectory(dir);
|
||||
var path = Path.Combine(dir, $"crash-{DateTime.Now:yyyyMMdd-HHmmss-fff}.txt");
|
||||
var report =
|
||||
$"RemSound crash report{Environment.NewLine}" +
|
||||
$"Version: {typeof(Program).Assembly.GetName().Version}{Environment.NewLine}" +
|
||||
$"Time: {DateTime.Now:O}{Environment.NewLine}" +
|
||||
$"Source: {source}{Environment.NewLine}{Environment.NewLine}" +
|
||||
(ex?.ToString() ?? "(no exception object)");
|
||||
File.WriteAllText(path, report);
|
||||
}
|
||||
catch { /* a crash handler must never throw */ }
|
||||
}
|
||||
|
||||
[STAThread]
|
||||
private static void Main(string[] args)
|
||||
{
|
||||
@@ -25,6 +46,16 @@ internal static class Program
|
||||
return;
|
||||
}
|
||||
|
||||
// Capture otherwise-fatal background-thread exceptions to a crash file, so a "RemSound just
|
||||
// vanished with no dialog" report (#16) leaves a stack behind instead of nothing. Best-effort.
|
||||
AppDomain.CurrentDomain.UnhandledException += (_, e) =>
|
||||
WriteCrashReport("AppDomain.UnhandledException", e.ExceptionObject as Exception);
|
||||
TaskScheduler.UnobservedTaskException += (_, e) =>
|
||||
{
|
||||
WriteCrashReport("TaskScheduler.UnobservedTaskException", e.Exception);
|
||||
e.SetObserved();
|
||||
};
|
||||
|
||||
// --config-dir <folder> (test / portable isolation): redirect ALL user state - config,
|
||||
// profiles, logs, cue sounds - to an explicit folder for THIS process only. Applied first,
|
||||
// before the layout migration and sound consolidation below read or write the default
|
||||
|
||||
Reference in New Issue
Block a user