1 | | | import 'cancellable_token.dart'; |
2 | | | import 'cancellation_token.dart'; |
3 | | | import 'squadron_error.dart'; |
4 | | | import 'worker_exception.dart'; |
5 | | |
|
6 | | | /// Composite token cancellation mode |
7 | | 1 | enum CompositeMode { |
8 | | | /// the [CompositeToken] is cancelled iif all tokens get cancelled |
9 | | | all, |
10 | | |
|
11 | | | /// the [CompositeToken] is cancelled as soon as one of the tokens gets cancelled |
12 | | | any |
13 | | 0 | } |
14 | | |
|
15 | | | /// Time-out cancellation tokens used by callers of worker services. The token is cancelled automatically after |
16 | | | /// a period of time indicated by [duration] with a countdown starting only when the task is assigned to a |
17 | | | /// platform worker. |
18 | | | class CompositeToken extends CancellableToken { |
19 | | 2 | CompositeToken(Iterable<CancellationToken> tokens, this.mode, |
20 | | | [String? message]) |
21 | | 3 | : assert(tokens.isNotEmpty), |
22 | | 1 | _tokens = tokens.toList(), |
23 | | | _signaled = 0, |
24 | | 1 | super(message) { |
25 | | 3 | for (var token in _tokens) { |
26 | | 1 | if (token.cancelled) _signaled++; |
27 | | 1 | _register(token); |
28 | | | } |
29 | | 1 | } |
30 | | |
|
31 | | | /// Throws an exception, composite tokens may not be cancelled programmatically. |
32 | | 1 | @override |
33 | | 2 | void cancel() => throw newSquadronError( |
34 | | 1 | 'CompositeToken cannot be cancelled programmatically'); |
35 | | |
|
36 | | | final CompositeMode mode; |
37 | | |
|
38 | | | final List<CancellationToken> _tokens; |
39 | | | int _signaled; |
40 | | |
|
41 | | | /// Called just before processing a [WorkerRequest]. The method actually calls the [start] method for all |
42 | | | /// tokens registered with this [CompositeToken]. The [onTimeout] callback is mandatory if one of these |
43 | | | /// tokens is a [TimeOutToken]. |
44 | | 1 | @override |
45 | | 4 | void start() => _tokens.forEach(_starter); |
46 | | |
|
47 | | 3 | void _starter(CancellationToken token) => token.start(); |
48 | | |
|
49 | | 2 | void _signal() { |
50 | | 3 | _signaled++; |
51 | | 2 | _check(); |
52 | | 1 | } |
53 | | |
|
54 | | 2 | void _check() { |
55 | | 2 | if (!cancelled) { |
56 | | 5 | if ((mode == CompositeMode.any && _signaled >= 1) || |
57 | | 7 | (mode == CompositeMode.all && _signaled >= _tokens.length)) { |
58 | | 2 | if (mode == CompositeMode.all) { |
59 | | 4 | setException(CancelledException(message: message ?? 'Cancelled')); |
60 | | | } else { |
61 | | 7 | setException(_tokens.map((e) => e.exception).firstWhere( |
62 | | 2 | (e) => e != null, |
63 | | 1 | orElse: () => |
64 | | 1 | CancelledException(message: message ?? 'Cancelled'))!); |
65 | | | } |
66 | | 4 | _tokens.forEach(_unregister); |
67 | | 2 | super.cancel(); |
68 | | | } |
69 | | | } |
70 | | 1 | } |
71 | | |
|
72 | | 4 | void _register(CancellationToken token) => token.addListener(_signal); |
73 | | |
|
74 | | 4 | void _unregister(CancellationToken token) => token.removeListener(_signal); |
75 | | | } |