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