import 'dart:async'; import 'package:logging/logging.dart'; /// Mutex to prevent concurrent token refresh operations /// This ensures only one refresh operation happens at a time, /// preventing race conditions during app startup when multiple /// providers try to refresh tokens simultaneously. class TokenRefreshMutex { static final _instance = TokenRefreshMutex._(); factory TokenRefreshMutex() => _instance; TokenRefreshMutex._(); static final _logger = Logger('service.authorization.token_mutex'); Completer? _currentRefresh; Completer? _currentRotation; /// Execute a token refresh operation with mutex protection /// If another refresh is in progress, wait for it to complete Future executeRefresh(Future Function() refreshOperation) async { if (_currentRefresh != null) { _logger.fine('Token refresh already in progress, waiting for completion'); return await _currentRefresh!.future; } _logger.fine('Starting new token refresh operation'); _currentRefresh = Completer(); try { final result = await refreshOperation(); if (_currentRefresh != null) { _currentRefresh!.complete(result); _logger.fine('Token refresh completed successfully'); } return result; } catch (e, st) { _logger.warning('Token refresh failed', e, st); if (_currentRefresh != null) { _currentRefresh!.completeError(e, st); } rethrow; } finally { _currentRefresh = null; } } /// Execute a token rotation operation with mutex protection /// If another rotation is in progress, wait for it to complete Future executeRotation(Future Function() rotationOperation) async { if (_currentRotation != null) { _logger.fine('Token rotation already in progress, waiting for completion'); return await _currentRotation!.future; } _logger.fine('Starting new token rotation operation'); _currentRotation = Completer(); try { await rotationOperation(); if (_currentRotation != null) { _currentRotation!.complete(); _logger.fine('Token rotation completed successfully'); } } catch (e, st) { _logger.warning('Token rotation failed', e, st); if (_currentRotation != null) { _currentRotation!.completeError(e, st); } rethrow; } finally { _currentRotation = null; } } /// Check if a refresh operation is currently in progress bool get isRefreshInProgress => _currentRefresh != null; /// Check if a rotation operation is currently in progress bool get isRotationInProgress => _currentRotation != null; /// Get current status for debugging Map getStatus() { return { 'refreshInProgress': isRefreshInProgress, 'rotationInProgress': isRotationInProgress, 'timestamp': DateTime.now().toIso8601String(), }; } /// Force reset the mutex (for testing or emergency situations) void forceReset() { _logger.warning('Force resetting token refresh mutex'); if (_currentRefresh != null && !_currentRefresh!.isCompleted) { _currentRefresh!.completeError(Exception('Mutex force reset')); } if (_currentRotation != null && !_currentRotation!.isCompleted) { _currentRotation!.completeError(Exception('Mutex force reset')); } _currentRefresh = null; _currentRotation = null; } }