1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
//! The behaviour a shard drives: the one trait every user of this crate writes.
use crateProcessError;
use crateWork;
use Disposition;
/// The affine key type reached through a processor's work type.
pub type KeyOf<P> = Key;
/// Processes work for keys owned by one shard.
///
/// # Threading
///
/// A processor instance belongs to exactly one shard, running on one pinned
/// core, and is never shared across threads. Its futures are therefore allowed
/// to be `!Send`, which is the point of the whole design: connection pools,
/// caches and per-key state can be held behind `Rc` and mutated through `Cell`
/// or `RefCell` with no synchronization at all. If you need `Send` futures and
/// work stealing, use an ordinary multi-threaded executor: this crate is
/// deliberately the other thing.
///
/// `Clone` is required because each dispatched item owns a handle for the
/// duration of its future, so the future can be `'static` and live in a
/// non-boxed `FuturesUnordered`. Clone should be cheap: hold shared
/// dependencies behind `Rc` and clone that.
///
/// # State ownership
///
/// While `process` runs, it holds the *only* copy of that key's resident
/// state. No other future for the same key can be in flight, so state needs no
/// locking. Returning [`Disposition::Keep`] hands it back for the next
/// dispatch, and [`Disposition::Drop`] declares it untrustworthy so the next
/// dispatch reloads from the authoritative source. Any operation whose outcome
/// is unknown: a timeout, a lost acknowledgement: must return `Drop`.
/// What a shard does when a [`Processor::process`] future panics.
///
/// The panic is always caught: otherwise it would unwind the shard's reactor
/// loop, killing every key that shard owns and leaving the scheduler's
/// in-flight accounting permanently wrong. The state that was moved into the
/// panicking future is gone either way, so the key is always treated as
/// [`Disposition::Drop`] and reloads on its next dispatch.