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 1 : return await action();
27 : }
28 : // if we are *not* in a transaction, time to wait for the lock!
29 36 : while (_transactionLock != null) {
30 6 : 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 : }
|