vortex_layout/plan/physical/pipeline/
port.rs1use std::collections::VecDeque;
12
13use vortex_array::ArrayRef;
14
15use crate::plan::physical::pipeline::DEFAULT_CAPACITY;
16
17pub(crate) type PortId = usize;
19
20pub(crate) type PipelineId = usize;
22
23#[derive(Clone, Copy, Debug, PartialEq, Eq)]
25pub(crate) enum SourceEnd {
26 Unclaimed,
29 Pipeline { id: PipelineId, inlet: usize },
31 Scan(usize),
33}
34
35pub(crate) struct Queue {
40 front: Option<ArrayRef>,
41 batches: VecDeque<ArrayRef>,
42 capacity: usize,
44 closed: bool,
46 changed: bool,
48 pub(crate) sink: Option<PipelineId>,
50 pub(crate) source: SourceEnd,
51 source_gone: bool,
54 free: bool,
56}
57
58impl Queue {
59 fn new(capacity: usize, sink: Option<PipelineId>, source: SourceEnd) -> Self {
60 Self {
61 front: None,
62 batches: VecDeque::new(),
63 capacity: capacity.max(1),
64 closed: false,
65 changed: false,
66 sink,
67 source,
68 source_gone: false,
69 free: false,
70 }
71 }
72
73 pub(crate) fn has_room(&self) -> bool {
74 self.len() < self.capacity
75 }
76
77 pub(crate) fn closed(&self) -> bool {
78 self.closed
79 }
80
81 #[cfg(debug_assertions)]
83 pub(crate) fn unbounded(&self) -> bool {
84 self.capacity == usize::MAX
85 }
86
87 pub(crate) fn len(&self) -> usize {
88 usize::from(self.front.is_some()) + self.batches.len()
89 }
90
91 #[cfg(debug_assertions)]
92 pub(crate) fn is_free(&self) -> bool {
93 self.free
94 }
95
96 pub(crate) fn take_changed(&mut self) -> bool {
97 std::mem::take(&mut self.changed)
98 }
99
100 pub(crate) fn pop(&mut self) -> Option<ArrayRef> {
101 std::mem::replace(&mut self.front, self.batches.pop_front())
102 }
103}
104
105pub struct Inlet<'a> {
107 queue: &'a mut Queue,
108}
109
110impl Inlet<'_> {
111 pub fn peek_mut(&mut self) -> Option<&mut ArrayRef> {
113 self.queue.front.as_mut()
114 }
115
116 pub fn take(&mut self) -> Option<ArrayRef> {
118 self.queue.pop()
119 }
120
121 pub fn closed(&self) -> bool {
123 self.queue.closed
124 }
125
126 pub fn is_empty(&self) -> bool {
128 self.queue.front.is_none()
129 }
130
131 pub fn len(&self) -> usize {
133 self.queue.len()
134 }
135
136 pub fn full(&self) -> bool {
139 self.queue.len() >= self.queue.capacity.min(DEFAULT_CAPACITY)
140 }
141
142 pub fn rows(&self) -> usize {
144 self.queue
145 .front
146 .iter()
147 .chain(self.queue.batches.iter())
148 .map(|batch| batch.len())
149 .sum()
150 }
151}
152
153pub(crate) struct Slab<T> {
156 blocks: Vec<Vec<T>>,
157 len: usize,
158}
159
160const BLOCK: usize = 64;
161
162impl<T> Default for Slab<T> {
163 fn default() -> Self {
164 Self {
165 blocks: Vec::new(),
166 len: 0,
167 }
168 }
169}
170
171impl<T> Slab<T> {
172 #[cfg(test)]
173 pub(crate) fn len(&self) -> usize {
174 self.len
175 }
176
177 pub(crate) fn push(&mut self, value: T) -> usize {
179 if self.len.is_multiple_of(BLOCK) {
180 self.blocks.push(Vec::with_capacity(BLOCK));
181 }
182 let index = self.len;
183 self.blocks[index / BLOCK].push(value);
184 self.len += 1;
185 index
186 }
187
188 pub(crate) fn iter(&self) -> impl Iterator<Item = &T> {
189 self.blocks.iter().flatten()
190 }
191}
192
193impl<T> std::ops::Index<usize> for Slab<T> {
194 type Output = T;
195
196 fn index(&self, index: usize) -> &T {
197 &self.blocks[index / BLOCK][index % BLOCK]
198 }
199}
200
201impl<T> std::ops::IndexMut<usize> for Slab<T> {
202 fn index_mut(&mut self, index: usize) -> &mut T {
203 &mut self.blocks[index / BLOCK][index % BLOCK]
204 }
205}
206
207#[derive(Default)]
210pub(crate) struct Arena {
211 queues: Slab<Queue>,
212 free: Vec<PortId>,
213}
214
215impl Arena {
216 pub(crate) fn create(
217 &mut self,
218 capacity: usize,
219 sink: Option<PipelineId>,
220 source: SourceEnd,
221 ) -> PortId {
222 match self.free.pop() {
223 Some(id) => {
224 let queue = &mut self.queues[id];
226 let capacity = capacity.max(1);
227 queue.capacity = capacity;
228 queue.closed = false;
229 queue.changed = false;
230 queue.sink = sink;
231 queue.source = source;
232 queue.source_gone = false;
233 queue.free = false;
234 id
235 }
236 None => self.queues.push(Queue::new(capacity, sink, source)),
237 }
238 }
239
240 pub(crate) fn get(&self, id: PortId) -> &Queue {
241 &self.queues[id]
242 }
243
244 pub(crate) fn get_mut(&mut self, id: PortId) -> &mut Queue {
245 &mut self.queues[id]
246 }
247
248 pub(crate) fn inlet(&mut self, id: PortId) -> Inlet<'_> {
249 Inlet {
250 queue: &mut self.queues[id],
251 }
252 }
253
254 pub(crate) fn has_room(&self, id: PortId) -> bool {
255 self.queues[id].has_room()
256 }
257
258 pub(crate) fn push(&mut self, id: PortId, batch: ArrayRef) {
260 let queue = &mut self.queues[id];
261 debug_assert!(
262 !queue.source_gone && !queue.closed,
263 "push into port {id} with no consumer"
264 );
265 if !batch.is_empty() {
266 if queue.front.is_none() {
267 queue.front = Some(batch);
268 } else {
269 queue.batches.push_back(batch);
270 }
271 queue.changed = true;
272 }
273 }
274
275 pub(crate) fn close(&mut self, id: PortId) {
277 let queue = &mut self.queues[id];
278 queue.changed |= !queue.closed;
279 queue.closed = true;
280 queue.sink = None;
281 self.maybe_free(id);
282 }
283
284 pub(crate) fn drop_source(&mut self, id: PortId) {
288 let queue = &mut self.queues[id];
289 queue.source_gone = true;
290 queue.source = SourceEnd::Unclaimed;
291 queue.front = None;
292 queue.batches.clear();
293 self.maybe_free(id);
294 }
295
296 pub(crate) fn discard(&mut self, id: PortId) {
299 let queue = &mut self.queues[id];
300 debug_assert!(
301 queue.sink.is_none() && queue.source == SourceEnd::Unclaimed,
302 "discarding port {id} still attached to a pipeline"
303 );
304 queue.closed = true;
305 queue.source_gone = true;
306 queue.front = None;
307 queue.batches.clear();
308 self.maybe_free(id);
309 }
310
311 fn maybe_free(&mut self, id: PortId) {
312 let queue = &mut self.queues[id];
313 if queue.closed && queue.source_gone && !queue.free {
314 queue.free = true;
315 queue.front = None;
316 queue.batches.clear();
317 self.free.push(id);
318 }
319 }
320
321 #[cfg(debug_assertions)]
323 pub(crate) fn iter(&self) -> impl Iterator<Item = (PortId, &Queue)> {
324 self.queues.iter().enumerate()
325 }
326
327 #[cfg(test)]
329 pub(crate) fn live(&self) -> usize {
330 self.queues.len() - self.free.len()
331 }
332}
333
334#[cfg(test)]
335mod tests {
336 use rstest::rstest;
337 use vortex_array::ArrayRef;
338 use vortex_array::IntoArray;
339 use vortex_buffer::buffer;
340
341 use super::Arena;
342 use super::SourceEnd;
343
344 #[rstest]
345 #[case::single(1)]
346 #[case::bounded(4)]
347 #[case::unbounded(usize::MAX)]
348 fn queued_batches_keep_order_and_reused_ports_are_empty(#[case] capacity: usize) {
349 let mut arena = Arena::default();
350 let port = arena.create(capacity, None, SourceEnd::Scan(0));
351 let arrays: Vec<_> = (0..capacity.min(4))
352 .map(|i| buffer![i as u64].into_array())
353 .collect();
354 for array in &arrays {
355 assert!(arena.has_room(port));
356 arena.push(port, array.clone());
357 }
358 assert_eq!(arena.inlet(port).len(), arrays.len());
359 assert_eq!(arena.inlet(port).rows(), arrays.len());
360 arena.close(port);
361 for expected in arrays {
362 let actual = arena.inlet(port).take().expect("queued batch");
363 assert!(ArrayRef::ptr_eq(&actual, &expected));
364 }
365 assert!(arena.inlet(port).is_empty());
366 arena.drop_source(port);
367 let reused = arena.create(1, None, SourceEnd::Scan(0));
368 assert_eq!(reused, port);
369 assert!(arena.inlet(reused).is_empty());
370 assert!(arena.has_room(reused));
371 }
372}