LCOV - code coverage report
Current view: top level - lib/src/utils - multilock.dart (source / functions) Hit Total Coverage
Test: merged.info Lines: 20 20 100.0 %
Date: 2024-09-04 20:26:16 Functions: 0 0 -

          Line data    Source code
       1             : /*
       2             :  *   Famedly Matrix SDK
       3             :  *   Copyright (C) 2019, 2020, 2021 Famedly GmbH
       4             :  *
       5             :  *   This program is free software: you can redistribute it and/or modify
       6             :  *   it under the terms of the GNU Affero General Public License as
       7             :  *   published by the Free Software Foundation, either version 3 of the
       8             :  *   License, or (at your option) any later version.
       9             :  *
      10             :  *   This program is distributed in the hope that it will be useful,
      11             :  *   but WITHOUT ANY WARRANTY; without even the implied warranty of
      12             :  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
      13             :  *   GNU Affero General Public License for more details.
      14             :  *
      15             :  *   You should have received a copy of the GNU Affero General Public License
      16             :  *   along with this program.  If not, see <https://www.gnu.org/licenses/>.
      17             :  */
      18             : 
      19             : import 'dart:async';
      20             : 
      21             : /// Lock management class. It allows to lock and unlock multiple keys at once. The keys have
      22             : /// the type [T]
      23             : class MultiLock<T> {
      24             :   final Map<T, Completer<void>> _completers = {};
      25             : 
      26             :   /// Set a number of [keys] locks, awaiting them to be released previously.
      27          11 :   Future<void> lock(Iterable<T> keys) async {
      28             :     // An iterable might have duplicate entries. A set is guaranteed not to, and we need
      29             :     // unique entries, as else a lot of things might go bad.
      30          11 :     final uniqueKeys = keys.toSet();
      31             :     // we want to make sure that there are no existing completers for any of the locks
      32             :     // we are trying to set. So, we await all the completers until they are all gone.
      33             :     // We can't just assume they are all gone after one go, due to rare race conditions
      34             :     // which could then result in a deadlock.
      35          39 :     while (_completers.keys.any((k) => uniqueKeys.contains(k))) {
      36             :       // Here we try to build all the futures to wait for single completers and then await
      37             :       // them at the same time, in parallel
      38           3 :       final futures = <Future<void>>[];
      39           6 :       for (final key in uniqueKeys) {
      40           6 :         if (_completers[key] != null) {
      41           6 :           futures.add(() async {
      42           6 :             while (_completers[key] != null) {
      43           9 :               await _completers[key]!.future;
      44             :             }
      45           3 :           }());
      46             :         }
      47             :       }
      48           3 :       await Future.wait(futures);
      49             :     }
      50             :     // And finally set all the completers
      51          22 :     for (final key in uniqueKeys) {
      52          33 :       _completers[key] = Completer<void>();
      53             :     }
      54             :   }
      55             : 
      56             :   /// Unlock all [keys] locks. Typically these should be the same keys as called
      57             :   /// in `.lock(keys)``
      58          11 :   void unlock(Iterable<T> keys) {
      59          11 :     final uniqueKeys = keys.toSet();
      60             :     // we just have to simply unlock all the completers
      61          22 :     for (final key in uniqueKeys) {
      62          22 :       if (_completers[key] != null) {
      63          22 :         final completer = _completers[key]!;
      64          22 :         _completers.remove(key);
      65          11 :         completer.complete();
      66             :       }
      67             :     }
      68             :   }
      69             : }

Generated by: LCOV version 1.14