@@ -39,11 +39,10 @@ impl Scheduler for Box<dyn Scheduler> {
39
39
/// Allocation-free activation tracker.
40
40
#[ derive( Debug ) ]
41
41
pub struct Activations {
42
- clean : usize ,
43
- /// `(offset, length)`
44
- bounds : Vec < ( usize , usize ) > ,
45
- slices : Vec < usize > ,
46
- buffer : Vec < usize > ,
42
+ /// Current activations that are being served.
43
+ current : ActivationsBuffer ,
44
+ /// Upcoming activations that may soon be served.
45
+ pending : ActivationsBuffer ,
47
46
48
47
// Inter-thread activations.
49
48
tx : Sender < Vec < usize > > ,
@@ -60,10 +59,8 @@ impl Activations {
60
59
pub fn new ( timer : Instant ) -> Self {
61
60
let ( tx, rx) = crossbeam_channel:: unbounded ( ) ;
62
61
Self {
63
- clean : 0 ,
64
- bounds : Vec :: new ( ) ,
65
- slices : Vec :: new ( ) ,
66
- buffer : Vec :: new ( ) ,
62
+ current : ActivationsBuffer :: new ( ) ,
63
+ pending : ActivationsBuffer :: new ( ) ,
67
64
tx,
68
65
rx,
69
66
timer,
@@ -73,8 +70,7 @@ impl Activations {
73
70
74
71
/// Activates the task addressed by `path`.
75
72
pub fn activate ( & mut self , path : & [ usize ] ) {
76
- self . bounds . push ( ( self . slices . len ( ) , path. len ( ) ) ) ;
77
- self . slices . extend ( path) ;
73
+ self . pending . activate ( path) ;
78
74
}
79
75
80
76
/// Schedules a future activation for the task addressed by `path`.
@@ -89,6 +85,20 @@ impl Activations {
89
85
}
90
86
}
91
87
88
+ /// Reactivates the task addressed by `path`, ideally within `delay`.
89
+ ///
90
+ /// The task may be activated before `delay`, in which case the task should reactivate itself if
91
+ /// it requires further reactivation, as this specific `delay` may no longer be in effect.
92
+ pub fn activate_within ( & mut self , path : & [ usize ] , delay : Duration ) {
93
+ if delay == Duration :: new ( 0 , 0 ) {
94
+ self . activate ( path)
95
+ }
96
+ else {
97
+ let moment = self . timer . elapsed ( ) + delay;
98
+ self . pending . activate_by ( path, moment)
99
+ }
100
+ }
101
+
92
102
/// Discards the current active set and presents the next active set.
93
103
pub fn advance ( & mut self ) {
94
104
@@ -104,37 +114,135 @@ impl Activations {
104
114
self . activate ( & path[ ..] ) ;
105
115
}
106
116
107
- self . bounds . drain ( .. self . clean ) ;
117
+ self . current . clear ( ) ;
118
+ self . pending . compact ( ) ;
119
+ self . pending . extract_through ( & mut self . current , now) ;
120
+
121
+ }
122
+
123
+ /// Maps a function across activated paths.
124
+ pub fn map_active ( & self , logic : impl Fn ( & [ usize ] ) ) {
125
+ self . current . map_active ( logic)
126
+ }
127
+
128
+ /// Sets as active any symbols that follow `path`.
129
+ pub fn for_extensions ( & self , path : & [ usize ] , action : impl FnMut ( usize ) ) {
130
+ self . current . for_extensions ( path, action)
131
+ }
132
+
133
+ /// Constructs a thread-safe `SyncActivations` handle to this activator.
134
+ pub fn sync ( & self ) -> SyncActivations {
135
+ SyncActivations {
136
+ tx : self . tx . clone ( ) ,
137
+ thread : std:: thread:: current ( ) ,
138
+ }
139
+ }
140
+
141
+ /// Time until next scheduled event.
142
+ ///
143
+ /// This method should be used before putting a worker thread to sleep, as it
144
+ /// indicates the amount of time before the thread should be unparked for the
145
+ /// next scheduled activation.
146
+ pub fn empty_for ( & self ) -> Option < Duration > {
147
+ if !self . current . is_empty ( ) || !self . pending . is_empty ( ) {
148
+ Some ( Duration :: new ( 0 , 0 ) )
149
+ }
150
+ else {
151
+ self . queue . peek ( ) . map ( |Reverse ( ( t, _a) ) | {
152
+ let elapsed = self . timer . elapsed ( ) ;
153
+ if t < & elapsed { Duration :: new ( 0 , 0 ) }
154
+ else { * t - elapsed }
155
+ } )
156
+ }
157
+ }
158
+ }
159
+
160
+ /// Manages delayed activations for path-named tasks.
161
+ #[ derive( Debug ) ]
162
+ pub struct ActivationsBuffer {
163
+ /// `(offset, length)`, and an elapsed timer duration.
164
+ /// The zero duration can be used to indicate "immediately".
165
+ bounds : Vec < ( usize , usize , Duration ) > ,
166
+ /// Elements of path slices.
167
+ slices : Vec < usize > ,
168
+ /// A spare buffer to copy into.
169
+ buffer : Vec < usize > ,
170
+ }
171
+
172
+ impl ActivationsBuffer {
173
+ /// Creates a new activation tracker.
174
+ pub fn new ( ) -> Self {
175
+ Self {
176
+ bounds : Vec :: new ( ) ,
177
+ slices : Vec :: new ( ) ,
178
+ buffer : Vec :: new ( ) ,
179
+ }
180
+ }
181
+
182
+ fn clear ( & mut self ) {
183
+ self . bounds . clear ( ) ;
184
+ self . slices . clear ( ) ;
185
+ self . buffer . clear ( ) ;
186
+ }
187
+
188
+ fn is_empty ( & self ) -> bool {
189
+ self . bounds . is_empty ( )
190
+ }
191
+
192
+ /// Activates the task addressed by `path`.
193
+ pub fn activate ( & mut self , path : & [ usize ] ) {
194
+ self . activate_by ( path, Duration :: new ( 0 , 0 ) ) ;
195
+ }
196
+
197
+ /// Activates the task addressed by `path`.
198
+ pub fn activate_by ( & mut self , path : & [ usize ] , duration : Duration ) {
199
+ self . bounds . push ( ( self . slices . len ( ) , path. len ( ) , duration) ) ;
200
+ self . slices . extend ( path) ;
201
+ }
202
+
203
+ /// Orders activations by their path, and retains only each's most immediate duration.
204
+ pub fn compact ( & mut self ) {
108
205
109
206
{ // Scoped, to allow borrow to drop.
110
207
let slices = & self . slices [ ..] ;
111
- self . bounds . sort_by_key ( |x| & slices[ x. 0 .. ( x. 0 + x. 1 ) ] ) ;
208
+ // Sort slices by path, and within each path by duration.
209
+ self . bounds . sort_by_key ( |x| ( & slices[ x. 0 .. ( x. 0 + x. 1 ) ] , x. 2 ) ) ;
210
+ // Deduplicate by path, retaining the least duration.
112
211
self . bounds . dedup_by_key ( |x| & slices[ x. 0 .. ( x. 0 + x. 1 ) ] ) ;
113
212
}
114
213
115
214
// Compact the slices.
116
215
self . buffer . clear ( ) ;
117
- for ( offset, length) in self . bounds . iter_mut ( ) {
216
+ for ( offset, length, _duration ) in self . bounds . iter_mut ( ) {
118
217
self . buffer . extend ( & self . slices [ * offset .. ( * offset + * length) ] ) ;
119
218
* offset = self . buffer . len ( ) - * length;
120
219
}
121
220
:: std:: mem:: swap ( & mut self . buffer , & mut self . slices ) ;
221
+ }
122
222
123
- self . clean = self . bounds . len ( ) ;
223
+ /// Extracts all activations less or equal to `threshold` into `other`.
224
+ pub fn extract_through ( & mut self , other : & mut Self , threshold : Duration ) {
225
+ for ( offset, length, duration) in self . bounds . iter_mut ( ) {
226
+ if * duration <= threshold {
227
+ other. activate_by ( & self . slices [ * offset .. ( * offset + * length) ] , * duration) ;
228
+ }
229
+ }
230
+ self . bounds . retain ( |( _off, _len, duration) | * duration > threshold) ;
231
+ // Could `self.compact()` here, but it will happen as part of future work.
124
232
}
125
233
126
234
/// Maps a function across activated paths.
127
235
pub fn map_active ( & self , logic : impl Fn ( & [ usize ] ) ) {
128
- for ( offset, length) in self . bounds . iter ( ) {
236
+ for ( offset, length, _duration ) in self . bounds . iter ( ) {
129
237
logic ( & self . slices [ * offset .. ( * offset + * length) ] ) ;
130
238
}
131
239
}
132
-
240
+
133
241
/// Sets as active any symbols that follow `path`.
134
242
pub fn for_extensions ( & self , path : & [ usize ] , mut action : impl FnMut ( usize ) ) {
135
243
136
244
let position =
137
- self . bounds [ .. self . clean ]
245
+ self . bounds
138
246
. binary_search_by_key ( & path, |x| & self . slices [ x. 0 .. ( x. 0 + x. 1 ) ] ) ;
139
247
let position = match position {
140
248
Ok ( x) => x,
@@ -146,7 +254,7 @@ impl Activations {
146
254
. iter ( )
147
255
. cloned ( )
148
256
. skip ( position)
149
- . map ( |( offset, length) | & self . slices [ offset .. ( offset + length) ] )
257
+ . map ( |( offset, length, _ ) | & self . slices [ offset .. ( offset + length) ] )
150
258
. take_while ( |x| x. starts_with ( path) )
151
259
. for_each ( |x| {
152
260
// push non-empty, non-duplicate extensions.
@@ -158,32 +266,6 @@ impl Activations {
158
266
}
159
267
} ) ;
160
268
}
161
-
162
- /// Constructs a thread-safe `SyncActivations` handle to this activator.
163
- pub fn sync ( & self ) -> SyncActivations {
164
- SyncActivations {
165
- tx : self . tx . clone ( ) ,
166
- thread : std:: thread:: current ( ) ,
167
- }
168
- }
169
-
170
- /// Time until next scheduled event.
171
- ///
172
- /// This method should be used before putting a worker thread to sleep, as it
173
- /// indicates the amount of time before the thread should be unparked for the
174
- /// next scheduled activation.
175
- pub fn empty_for ( & self ) -> Option < Duration > {
176
- if !self . bounds . is_empty ( ) {
177
- Some ( Duration :: new ( 0 , 0 ) )
178
- }
179
- else {
180
- self . queue . peek ( ) . map ( |Reverse ( ( t, _a) ) | {
181
- let elapsed = self . timer . elapsed ( ) ;
182
- if t < & elapsed { Duration :: new ( 0 , 0 ) }
183
- else { * t - elapsed }
184
- } )
185
- }
186
- }
187
269
}
188
270
189
271
/// A thread-safe handle to an `Activations`.
0 commit comments