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
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
use ;
use ;
use EventItem;
pub const NON_INPUT_EVENT: &str = "dora/non_input_event";
/// This scheduler will make sure that there is fairness between inputs.
///
/// The scheduler reorders events in the following way:
///
/// - **Non-input events are prioritized**
///
/// If the node received any events that are not input events, they are returned first. The
/// intention of this reordering is that the nodes can react quickly to dataflow-related events
/// even when their input queues are very full.
///
/// This reordering has some side effects that might be unexpected:
/// - An [`InputClosed`][super::Event::InputClosed] event might be yielded before the last
/// input events of that ID.
///
/// Usually, an `InputClosed` event indicates that there won't be any subsequent inputs
/// of a certain ID. This invariant does not hold anymore for a scheduled event stream.
/// - The [`Stop`][super::Event::Stop] event might not be the last event of the stream anymore.
///
/// Usually, the `Stop` event is the last event that is sent to a node before the event stream
/// is closed. Because of the reordering, the stream might return more events after a `Stop`
/// event.
/// - **Input events are grouped by ID** and yielded in a **least-recently used order (by ID)**.
///
/// The scheduler keeps a separate queue for each input ID, where the incoming input events are
/// placed in their chronological order. When yielding the next event, the scheduler iterates over
/// these queues in least-recently used order. This means that the queue corresponding to the
/// last yielded event will be checked last. The scheduler will return the oldest event from the
/// first non-empty queue.
///
/// The side effect of this change is that inputs events of different IDs are no longer in their
/// chronological order. This might lead to unexpected results for input events that are caused by
/// each other.
///
/// ## Example 1
/// Consider the case that one input has a very high frequency and another one with a very slow
/// frequency. The event stream will always alternate between the two inputs when each input is
/// available.
/// Without the scheduling, the high-frequency input would be returned much more often.
///
/// ## Example 2
/// Again, let's consider the case that one input has a very high frequency and the other has a
/// very slow frequency. This time, we define a small maximum queue sizes for the low-frequency
/// input, but a large queue size for the high-frequency one.
/// Using the scheduler, the event stream will always alternate between high and low-frequency
/// inputs as long as inputs of both types are available.
///
/// Without scheduling, the low-frequency input might never be yielded before
/// it's dropped because there is almost always an older high-frequency input available that is
/// yielded first. Once the low-frequency input would be the next one chronologically, it might
/// have been dropped already because the node received newer low-frequency inputs in the
/// meantime (the queue length is small). At this point, the next-oldest input is a high-frequency
/// input again.
///
/// ## Example 3
/// Consider a high-frequency camera input and a low-frequency bounding box input, which is based
/// on the latest camera image. The dataflow YAML file specifies a large queue size for the camera
/// input and a small queue size for the bounding box input.
///
/// With scheduling, the number of
/// buffered camera inputs might grow over time. As a result the camera inputs yielded from the
/// stream (in oldest-first order) are not synchronized with the bounding box inputs anymore. So
/// the node receives an up-to-date bounding box, but a considerably outdated image.
///
/// Without scheduling, the events are returned in chronological order. This time, the bounding
/// box might be slightly outdated if the camera sent new images before the bounding box was
/// ready. However, the time difference between the two input types is independent of the
/// queue size this time.
///
/// (If a perfect matching bounding box is required, we recommend to forward the input image as
/// part of the bounding box output. This way, the receiving node only needs to subscribe to one
/// input so no mismatches can happen.)