Line data Source code
1 : import 'dart:async'; 2 : 3 : // we want transactions to lock, however NOT if transactoins are run inside of each other. 4 : // to be able to do this, we use dart zones (https://dart.dev/articles/archive/zones). 5 : // _transactionZones holds a set of all zones which are currently running a transaction. 6 : // _transactionLock holds the lock. 7 : mixin ZoneTransactionMixin { 8 : Completer<void>? _transactionLock; 9 : final _transactionZones = <Zone>{}; 10 : 11 36 : Future<void> zoneTransaction(Future<void> Function() action) async { 12 : // first we try to determine if we are inside of a transaction currently 13 : var isInTransaction = false; 14 36 : Zone? zone = Zone.current; 15 : // for that we keep on iterating to the parent zone until there is either no zone anymore 16 : // or we have found a zone inside of _transactionZones. 17 : while (zone != null) { 18 72 : if (_transactionZones.contains(zone)) { 19 : isInTransaction = true; 20 : break; 21 : } 22 36 : zone = zone.parent; 23 : } 24 : // if we are inside a transaction....just run the action 25 : if (isInTransaction) { 26 2 : return await action(); 27 : } 28 : // if we are *not* in a transaction, time to wait for the lock! 29 36 : while (_transactionLock != null) { 30 4 : await _transactionLock!.future; 31 : } 32 : // claim the lock 33 36 : final lock = Completer<void>(); 34 36 : _transactionLock = lock; 35 : try { 36 : // run the action inside of a new zone 37 72 : return await runZoned(() async { 38 : try { 39 : // don't forget to add the new zone to _transactionZones! 40 108 : _transactionZones.add(Zone.current); 41 : 42 36 : await action(); 43 : return; 44 : } finally { 45 : // aaaand remove the zone from _transactionZones again 46 108 : _transactionZones.remove(Zone.current); 47 : } 48 : }); 49 : } finally { 50 : // aaaand finally release the lock 51 36 : _transactionLock = null; 52 36 : lock.complete(); 53 : } 54 : } 55 : }