LCOV - code coverage report
Current view: top level - lib/src/utils - multilock.dart (source / functions) Coverage Total Hit
Test: merged.info Lines: 100.0 % 20 20
Test Date: 2025-01-14 13:39:53 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 2.0-1