import 'dart:async'; import 'dart:collection'; import 'package:flutter/foundation.dart'; class SCRoomEffectScheduler { static const Duration _deferredDrainInterval = Duration(milliseconds: 60); static final SCRoomEffectScheduler _instance = SCRoomEffectScheduler._internal(); factory SCRoomEffectScheduler() => _instance; SCRoomEffectScheduler._internal(); final Queue<_DeferredRoomEffectTask> _deferredTasks = Queue<_DeferredRoomEffectTask>(); Timer? _deferredDrainTimer; int _pendingHighCostTaskCount = 0; int _nextTaskId = 0; bool get hasActiveHighCostEffects => _pendingHighCostTaskCount > 0; void registerHighCostTaskQueued({String? debugLabel}) { _pendingHighCostTaskCount += 1; _cancelDeferredDrain(); _log( 'register_high_cost task=${debugLabel ?? "unknown"} ' 'pending=$_pendingHighCostTaskCount deferred=${_deferredTasks.length}', ); } void completeHighCostTask({String? debugLabel}) { if (_pendingHighCostTaskCount > 0) { _pendingHighCostTaskCount -= 1; } _log( 'complete_high_cost task=${debugLabel ?? "unknown"} ' 'pending=$_pendingHighCostTaskCount deferred=${_deferredTasks.length}', ); _scheduleDeferredDrainIfNeeded(); } void clearHighCostTasks({String reason = 'unknown'}) { if (_pendingHighCostTaskCount == 0) { _scheduleDeferredDrainIfNeeded(); return; } _pendingHighCostTaskCount = 0; _log( 'clear_high_cost reason=$reason ' 'pending=$_pendingHighCostTaskCount deferred=${_deferredTasks.length}', ); _scheduleDeferredDrainIfNeeded(); } void scheduleDeferredEffect({ required String debugLabel, required VoidCallback action, }) { if (!hasActiveHighCostEffects) { action(); return; } final task = _DeferredRoomEffectTask( id: _nextTaskId++, debugLabel: debugLabel, action: action, ); _deferredTasks.add(task); _log( 'defer task=${task.debugLabel} id=${task.id} ' 'pending=$_pendingHighCostTaskCount deferred=${_deferredTasks.length}', ); } void clearDeferredTasks({String reason = 'unknown'}) { if (_deferredTasks.isEmpty) { _cancelDeferredDrain(); return; } _deferredTasks.clear(); _cancelDeferredDrain(); _log('clear_deferred reason=$reason'); } void _scheduleDeferredDrainIfNeeded() { if (hasActiveHighCostEffects || _deferredTasks.isEmpty || _deferredDrainTimer != null) { return; } _deferredDrainTimer = Timer(_deferredDrainInterval, _drainNextDeferredTask); } void _drainNextDeferredTask() { _deferredDrainTimer = null; if (hasActiveHighCostEffects || _deferredTasks.isEmpty) { return; } final task = _deferredTasks.removeFirst(); _log( 'drain_deferred task=${task.debugLabel} id=${task.id} ' 'remaining=${_deferredTasks.length}', ); try { task.action(); } catch (error, stackTrace) { debugPrint( '[RoomEffectScheduler] deferred task failed: $error\n$stackTrace', ); } _scheduleDeferredDrainIfNeeded(); } void _cancelDeferredDrain() { _deferredDrainTimer?.cancel(); _deferredDrainTimer = null; } void _log(String message) { debugPrint('[RoomEffectScheduler] $message'); } } class _DeferredRoomEffectTask { const _DeferredRoomEffectTask({ required this.id, required this.debugLabel, required this.action, }); final int id; final String debugLabel; final VoidCallback action; }