ruda_runtime/runtime/stream/
scheduler.rs1use crate::runtime::{
2 config::streaming::StreamingLogLevel,
3 logging::ServerLogger,
4 stream::{StreamFactory, StreamPool},
5};
6use alloc::{format, sync::Arc, vec, vec::Vec};
7use ruda_core::stream_id::StreamId;
8
9pub trait SchedulerStreamBackend {
11 type Task: core::fmt::Debug;
13 type Stream: core::fmt::Debug;
15 type Factory: StreamFactory<Stream = Self::Stream>;
17
18 fn enqueue(task: Self::Task, stream: &mut Self::Stream);
20 fn flush(stream: &mut Self::Stream);
22 fn factory(&mut self) -> &mut Self::Factory;
24}
25
26#[derive(Debug)]
28pub struct SchedulerMultiStream<B: SchedulerStreamBackend> {
29 pool: StreamPool<SchedulerPoolMarker<B>>,
31 strategy: SchedulerStrategy,
33 max_tasks: usize,
35 pub logger: Arc<ServerLogger>,
37}
38
39#[derive(Debug)]
41pub enum SchedulerStrategy {
42 Interleave,
44 Sequential,
46}
47
48#[derive(Debug)]
50pub struct Stream<B: SchedulerStreamBackend> {
51 tasks: Vec<B::Task>,
53 stream: B::Stream,
55}
56
57impl<B: SchedulerStreamBackend> Stream<B> {
58 fn flush(&mut self) -> Vec<B::Task> {
60 if self.tasks.is_empty() {
61 return Vec::new();
62 }
63 let mut returned = Vec::with_capacity(self.tasks.capacity());
64 core::mem::swap(&mut returned, &mut self.tasks);
65 returned
66 }
67}
68
69#[derive(Debug)]
70struct SchedulerPoolMarker<B: SchedulerStreamBackend> {
71 backend: B,
72}
73
74impl<B: SchedulerStreamBackend> StreamFactory for SchedulerPoolMarker<B> {
75 type Stream = Stream<B>;
77
78 fn create(&mut self) -> Self::Stream {
80 Stream {
81 tasks: Vec::new(),
82 stream: self.backend.factory().create(),
84 }
85 }
86}
87
88#[derive(Debug)]
90pub struct SchedulerMultiStreamOptions {
91 pub max_streams: u8,
93 pub max_tasks: usize,
95 pub strategy: SchedulerStrategy,
97}
98
99impl<B: SchedulerStreamBackend> SchedulerMultiStream<B> {
100 pub fn new(
102 logger: Arc<ServerLogger>,
103 backend: B,
104 options: SchedulerMultiStreamOptions,
105 ) -> Self {
106 Self {
107 pool: StreamPool::new(SchedulerPoolMarker { backend }, options.max_streams, 0),
108 max_tasks: options.max_tasks,
109 strategy: options.strategy,
110 logger,
111 }
112 }
113
114 pub fn stream(&mut self, stream_id: &StreamId) -> &mut B::Stream {
116 let stream = self.pool.get_mut(stream_id);
117 &mut stream.stream
118 }
119
120 pub fn register(&mut self, stream_id: StreamId, task: B::Task, args_streams: &[StreamId]) {
122 self.align_streams(stream_id, args_streams);
124
125 let current = self.pool.get_mut(&stream_id);
127 current.tasks.push(task);
128
129 if current.tasks.len() >= self.max_tasks {
131 self.execute_streams(vec![stream_id]);
132 }
133 }
134
135 pub(crate) fn align_streams(&mut self, stream_id: StreamId, args_streams: &[StreamId]) {
137 let mut to_flush = Vec::new();
138 let index = self.pool.stream_index(&stream_id);
140
141 for arg_stream in args_streams {
143 let index_stream = self.pool.stream_index(arg_stream);
144 if index != index_stream {
145 to_flush.push(*arg_stream);
146
147 self.logger.log_streaming(
148 |level| matches!(level, StreamingLogLevel::Full),
149 || format!("Binding on {} is shared on {}", arg_stream, stream_id),
150 );
151 }
152 }
153
154 if to_flush.is_empty() {
156 return;
157 }
158
159 self.logger.log_streaming(
160 |level| !matches!(level, StreamingLogLevel::Disabled),
161 || {
162 format!(
163 "Flushing streams {to_flush:?} before registering more tasks on {stream_id}"
164 )
165 },
166 );
167 self.execute_streams(to_flush);
169 }
170
171 pub fn execute_streams(&mut self, stream_ids: Vec<StreamId>) {
173 let mut indices = Vec::with_capacity(stream_ids.len());
174
175 for id in stream_ids {
177 let index = self.pool.stream_index(&id);
178 if !indices.contains(&index) {
179 indices.push(index);
180 }
181 }
182
183 let mut schedules = Vec::new();
185 for index in indices {
186 let stream = unsafe { self.pool.get_mut_index(index) }; let tasks = stream.flush();
188 let num_tasks = tasks.len();
189
190 schedules.push(Schedule {
191 tasks: tasks.into_iter(),
192 num_tasks,
193 stream_index: index,
194 });
195 }
196
197 if schedules.is_empty() {
199 return;
200 }
201
202 match self.strategy {
204 SchedulerStrategy::Interleave => self.execute_schedules_interleave(schedules),
205 SchedulerStrategy::Sequential => self.execute_schedules_sequence(schedules),
206 }
207 }
208
209 fn execute_schedules_sequence(&mut self, schedules: Vec<Schedule<B>>) {
211 for schedule in schedules {
212 let stream = unsafe { self.pool.get_mut_index(schedule.stream_index) }; for task in schedule.tasks {
214 B::enqueue(task, &mut stream.stream);
216 }
217
218 B::flush(&mut stream.stream);
220 }
221 }
222
223 fn execute_schedules_interleave(&mut self, mut schedules: Vec<Schedule<B>>) {
230 for schedule in schedules.iter_mut().skip(1) {
232 let stream = unsafe { self.pool.get_mut_index(schedule.stream_index) };
233 B::flush(&mut stream.stream);
234 }
235
236 let execution_index = schedules.first().expect("At least one stream").stream_index;
237 let stream = unsafe { self.pool.get_mut_index(execution_index) };
238
239 let num_tasks_max = schedules
241 .iter()
242 .map(|s| s.num_tasks)
243 .max()
244 .expect("At least one schedule");
245
246 for _ in 0..num_tasks_max {
248 for schedule in schedules.iter_mut() {
249 if let Some(task) = schedule.tasks.next() {
251 B::enqueue(task, &mut stream.stream);
252 }
253 }
254 }
255
256 B::flush(&mut stream.stream);
258 }
259}
260
261struct Schedule<B: SchedulerStreamBackend> {
263 tasks: alloc::vec::IntoIter<B::Task>,
265 num_tasks: usize,
267 stream_index: usize,
269}