69 lines
		
	
	
		
			2.6 KiB
		
	
	
	
		
			Dart
		
	
	
	
			
		
		
	
	
			69 lines
		
	
	
		
			2.6 KiB
		
	
	
	
		
			Dart
		
	
	
	
| /*
 | |
|  *   Famedly Matrix SDK
 | |
|  *   Copyright (C) 2019, 2020, 2021 Famedly GmbH
 | |
|  *
 | |
|  *   This program is free software: you can redistribute it and/or modify
 | |
|  *   it under the terms of the GNU Affero General Public License as
 | |
|  *   published by the Free Software Foundation, either version 3 of the
 | |
|  *   License, or (at your option) any later version.
 | |
|  *
 | |
|  *   This program is distributed in the hope that it will be useful,
 | |
|  *   but WITHOUT ANY WARRANTY; without even the implied warranty of
 | |
|  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 | |
|  *   GNU Affero General Public License for more details.
 | |
|  *
 | |
|  *   You should have received a copy of the GNU Affero General Public License
 | |
|  *   along with this program.  If not, see <https://www.gnu.org/licenses/>.
 | |
|  */
 | |
| import 'dart:async';
 | |
| 
 | |
| /// Lock management class. It allows to lock and unlock multiple keys at once. The keys have
 | |
| /// the type [T]
 | |
| class MultiLock<T> {
 | |
|   final Map<T, Completer<void>> _completers = {};
 | |
| 
 | |
|   /// Set a number of [keys] locks, awaiting them to be released previously.
 | |
|   Future<void> lock(Iterable<T> keys) async {
 | |
|     // An iterable might have duplicate entries. A set is guaranteed not to, and we need
 | |
|     // unique entries, as else a lot of things might go bad.
 | |
|     final uniqueKeys = keys.toSet();
 | |
|     // we want to make sure that there are no existing completers for any of the locks
 | |
|     // we are trying to set. So, we await all the completers until they are all gone.
 | |
|     // We can't just assume they are all gone after one go, due to rare race conditions
 | |
|     // which could then result in a deadlock.
 | |
|     while (_completers.keys.any((k) => uniqueKeys.contains(k))) {
 | |
|       // Here we try to build all the futures to wait for single completers and then await
 | |
|       // them at the same time, in parallel
 | |
|       final futures = <Future<void>>[];
 | |
|       for (final key in uniqueKeys) {
 | |
|         if (_completers[key] != null) {
 | |
|           futures.add(() async {
 | |
|             while (_completers[key] != null) {
 | |
|               await _completers[key]!.future;
 | |
|             }
 | |
|           }());
 | |
|         }
 | |
|       }
 | |
|       await Future.wait(futures);
 | |
|     }
 | |
|     // And finally set all the completers
 | |
|     for (final key in uniqueKeys) {
 | |
|       _completers[key] = Completer<void>();
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   /// Unlock all [keys] locks. Typically these should be the same keys as called
 | |
|   /// in `.lock(keys)``
 | |
|   void unlock(Iterable<T> keys) {
 | |
|     final uniqueKeys = keys.toSet();
 | |
|     // we just have to simply unlock all the completers
 | |
|     for (final key in uniqueKeys) {
 | |
|       if (_completers[key] != null) {
 | |
|         final completer = _completers[key]!;
 | |
|         _completers.remove(key);
 | |
|         completer.complete();
 | |
|       }
 | |
|     }
 | |
|   }
 | |
| }
 |