fix(chat): Improve server message synchronization and streaming recovery
This commit is contained in:
@@ -5,6 +5,9 @@ import 'dart:async';
|
||||
/// Call [ping] whenever activity occurs. If no activity happens
|
||||
/// within [window], [onTimeout] fires. Optionally, an [absoluteCap]
|
||||
/// enforces a maximum total duration regardless of activity.
|
||||
///
|
||||
/// The [onTimeout] callback can be sync or async - if async, it will be
|
||||
/// awaited before the watchdog considers itself fully stopped.
|
||||
class InactivityWatchdog {
|
||||
InactivityWatchdog({
|
||||
required Duration window,
|
||||
@@ -13,16 +16,20 @@ class InactivityWatchdog {
|
||||
}) : _window = window,
|
||||
_absoluteCap = absoluteCap;
|
||||
|
||||
final void Function() onTimeout;
|
||||
final FutureOr<void> Function() onTimeout;
|
||||
|
||||
Duration _window;
|
||||
Duration? _absoluteCap;
|
||||
Timer? _timer;
|
||||
Timer? _absoluteTimer;
|
||||
bool _started = false;
|
||||
bool _firing = false;
|
||||
|
||||
Duration get window => _window;
|
||||
|
||||
/// Whether the timeout callback is currently executing.
|
||||
bool get isFiring => _firing;
|
||||
|
||||
void setWindow(Duration newWindow) {
|
||||
_window = newWindow;
|
||||
if (_started) {
|
||||
@@ -43,6 +50,8 @@ class InactivityWatchdog {
|
||||
|
||||
void start() {
|
||||
if (_started) return;
|
||||
// Prevent restart while callback is still executing to avoid double-fire
|
||||
if (_firing) return;
|
||||
_started = true;
|
||||
_restart();
|
||||
if (_absoluteCap != null) {
|
||||
@@ -51,6 +60,8 @@ class InactivityWatchdog {
|
||||
}
|
||||
|
||||
void ping() {
|
||||
// Prevent restart while callback is still executing to avoid double-fire
|
||||
if (_firing) return;
|
||||
if (!_started) {
|
||||
start();
|
||||
return;
|
||||
@@ -73,10 +84,24 @@ class InactivityWatchdog {
|
||||
_timer = Timer(_window, _fire);
|
||||
}
|
||||
|
||||
/// Synchronous entry point called by Timer. Kicks off async work.
|
||||
void _fire() {
|
||||
if (_firing) return; // Prevent re-entry
|
||||
_firing = true;
|
||||
stop();
|
||||
// Execute the callback asynchronously. We don't await because Timer
|
||||
// expects a sync callback, but the async work will complete in background.
|
||||
_executeCallback();
|
||||
}
|
||||
|
||||
/// Executes the timeout callback asynchronously.
|
||||
Future<void> _executeCallback() async {
|
||||
try {
|
||||
onTimeout();
|
||||
} catch (_) {}
|
||||
await onTimeout();
|
||||
} catch (_) {
|
||||
// Swallow errors to prevent unhandled exceptions
|
||||
} finally {
|
||||
_firing = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user