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 : }