import 'dart:async'; import 'dart:collection'; import 'package:meta/meta.dart'; import 'package:synchronized/synchronized.dart'; extension UtilAllMethodsList on List { void removeAll(List values) { for (final value in values) { remove(value); } } bool containsAll(List values) { for (final value in values) { if (!contains(value)) { return false; } } return true; } } class _RatchetAccessQueueEntry { _RatchetAccessQueueEntry( this.jids, this.completer, ); final List jids; final Completer completer; } class RatchetAccessQueue { final Queue<_RatchetAccessQueueEntry> _queue = Queue(); @visibleForTesting final List runningOperations = List.empty(growable: true); final Lock lock = Lock(); bool canBypass(List jids) { for (final jid in jids) { if (runningOperations.contains(jid)) { return false; } } return true; } Future enterCriticalSection(List jids) async { final completer = await lock.synchronized?>(() { if (canBypass(jids)) { runningOperations.addAll(jids); return null; } final completer = Completer(); _queue.add( _RatchetAccessQueueEntry( jids, completer, ), ); return completer; }); await completer?.future; } Future leaveCriticalSection(List jids) async { await lock.synchronized(() { runningOperations.removeAll(jids); while (_queue.isNotEmpty) { if (canBypass(_queue.first.jids)) { final head = _queue.removeFirst(); runningOperations.addAll(head.jids); head.completer.complete(); } else { break; } } }); } Future synchronized( List jids, Future Function() function, ) async { await enterCriticalSection(jids); final result = await function(); await leaveCriticalSection(jids); return result; } }