feat: inactivity watchdog for sockets
This commit is contained in:
@@ -333,7 +333,9 @@ class PersistentStreamingService with WidgetsBindingObserver {
|
||||
final lastUpdate = metadata['lastUpdate'] as DateTime?;
|
||||
if (lastUpdate != null) {
|
||||
final timeSinceUpdate = DateTime.now().difference(lastUpdate);
|
||||
return timeSinceUpdate > const Duration(minutes: 1);
|
||||
// Align with app-side watchdogs: be less aggressive than UI guard
|
||||
// but still attempt recovery before server timeouts become likely.
|
||||
return timeSinceUpdate > const Duration(minutes: 2);
|
||||
}
|
||||
|
||||
return false;
|
||||
|
||||
83
lib/core/utils/inactivity_watchdog.dart
Normal file
83
lib/core/utils/inactivity_watchdog.dart
Normal file
@@ -0,0 +1,83 @@
|
||||
import 'dart:async';
|
||||
|
||||
/// A simple activity-based watchdog.
|
||||
///
|
||||
/// Call [ping] whenever activity occurs. If no activity happens
|
||||
/// within [window], [onTimeout] fires. Optionally, an [absoluteCap]
|
||||
/// enforces a maximum total duration regardless of activity.
|
||||
class InactivityWatchdog {
|
||||
InactivityWatchdog({
|
||||
required Duration window,
|
||||
required this.onTimeout,
|
||||
Duration? absoluteCap,
|
||||
}) : _window = window,
|
||||
_absoluteCap = absoluteCap;
|
||||
|
||||
final void Function() onTimeout;
|
||||
|
||||
Duration _window;
|
||||
Duration? _absoluteCap;
|
||||
Timer? _timer;
|
||||
Timer? _absoluteTimer;
|
||||
bool _started = false;
|
||||
|
||||
Duration get window => _window;
|
||||
|
||||
void setWindow(Duration newWindow) {
|
||||
_window = newWindow;
|
||||
if (_started) {
|
||||
// Restart timer with new window
|
||||
_restart();
|
||||
}
|
||||
}
|
||||
|
||||
void setAbsoluteCap(Duration? cap) {
|
||||
_absoluteCap = cap;
|
||||
if (_started) {
|
||||
_absoluteTimer?.cancel();
|
||||
if (_absoluteCap != null) {
|
||||
_absoluteTimer = Timer(_absoluteCap!, _fire);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void start() {
|
||||
if (_started) return;
|
||||
_started = true;
|
||||
_restart();
|
||||
if (_absoluteCap != null) {
|
||||
_absoluteTimer = Timer(_absoluteCap!, _fire);
|
||||
}
|
||||
}
|
||||
|
||||
void ping() {
|
||||
if (!_started) {
|
||||
start();
|
||||
return;
|
||||
}
|
||||
_restart();
|
||||
}
|
||||
|
||||
void stop() {
|
||||
_timer?.cancel();
|
||||
_timer = null;
|
||||
_absoluteTimer?.cancel();
|
||||
_absoluteTimer = null;
|
||||
_started = false;
|
||||
}
|
||||
|
||||
void dispose() => stop();
|
||||
|
||||
void _restart() {
|
||||
_timer?.cancel();
|
||||
_timer = Timer(_window, _fire);
|
||||
}
|
||||
|
||||
void _fire() {
|
||||
stop();
|
||||
try {
|
||||
onTimeout();
|
||||
} catch (_) {}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user