1use std::collections::VecDeque;
8use std::fmt;
9use std::ops::Range;
10
11use rustc_hash::FxHashMap;
12use smallvec::SmallVec;
13use vortex_array::ArrayRef;
14use vortex_array::ExecutionCtx;
15use vortex_array::VortexSessionExecute;
16use vortex_array::buffer::BufferHandle;
17use vortex_error::VortexResult;
18use vortex_error::vortex_bail;
19use vortex_error::vortex_ensure;
20use vortex_error::vortex_err;
21use vortex_mask::Mask;
22use vortex_session::VortexSession;
23
24use super::DEFAULT_CAPACITY;
25use super::compile::Chain;
26use super::compile::Shares;
27use super::port::Arena;
28use super::port::PipelineId;
29use super::port::PortId;
30use super::port::Slab;
31use super::port::SourceEnd;
32use super::values::ValueCache;
33use crate::plan::physical::PlanRef;
34use crate::plan::physical::operator::Cx;
35use crate::plan::physical::operator::InletSet;
36use crate::plan::physical::operator::Input;
37use crate::plan::physical::operator::Operator;
38use crate::plan::physical::operator::Source;
39use crate::plan::physical::operator::SourceBlocked;
40use crate::plan::physical::operator::SourceReady;
41use crate::plan::physical::operator::Step;
42use crate::segments::SegmentId;
43
44#[cfg(debug_assertions)]
45mod validate;
46
47#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
49pub struct ReadId(u64);
50
51#[derive(Clone, Debug)]
53pub struct ReadRequest {
54 pub id: ReadId,
56 pub segment_id: SegmentId,
58}
59
60#[derive(Clone, Debug)]
62pub struct Split {
63 pub rows: Range<u64>,
65 pub mask: Mask,
67}
68
69impl Split {
70 pub fn all(rows: Range<u64>) -> Self {
72 let len = usize::try_from(rows.end - rows.start).unwrap_or(usize::MAX);
73 Self {
74 mask: Mask::new_true(len),
75 rows,
76 }
77 }
78}
79
80#[derive(Debug)]
82pub enum Turn {
83 Read(ReadRequest),
85 Output(usize, ArrayRef),
87 Waiting,
89 Done,
91}
92
93#[derive(Clone, Copy, Debug, PartialEq, Eq)]
95enum State {
96 Runnable,
97 Blocked(SourceBlocked),
99 Outlet,
101}
102
103pub(crate) struct Pipeline {
105 source: Box<dyn Source>,
106 stages: Vec<Box<dyn Operator>>,
107 inlets: Vec<PortId>,
108 outlets: SmallVec<[PortId; 1]>,
109 bytes: VecDeque<(SegmentId, BufferHandle)>,
111 pending: usize,
114 finishing: Option<usize>,
117 continuing: Vec<usize>,
119 source_more: bool,
121 touched: InletSet,
122 changed: InletSet,
123 state: State,
124 queued: bool,
125}
126
127enum Progress {
129 Blocked(SourceBlocked),
131 Outlet,
133 Done,
135}
136
137fn drive(
139 pipe: &mut Pipeline,
140 arena: &mut Arena,
141 session: &VortexSession,
142 exec: &mut ExecutionCtx,
143 reads: &mut SmallVec<[SegmentId; 1]>,
144) -> VortexResult<Progress> {
145 let Pipeline {
146 source,
147 stages,
148 inlets,
149 outlets,
150 bytes,
151 finishing,
152 continuing,
153 source_more,
154 touched,
155 changed,
156 ..
157 } = pipe;
158 let mut cx = Cx {
159 arena,
160 inlets,
161 bytes,
162 reads,
163 session,
164 exec,
165 touched,
166 changed,
167 };
168 loop {
169 if *finishing == Some(stages.len()) && continuing.is_empty() {
171 for &outlet in outlets.iter() {
172 cx.arena.close(outlet);
173 }
174 return Ok(Progress::Done);
175 }
176 if !outlets.iter().all(|&outlet| cx.arena.has_room(outlet)) {
177 return Ok(Progress::Outlet);
178 }
179 if let Some(stage) = continuing.pop() {
180 let step = stages[stage].compute(Input::None, &mut cx)?;
181 push(stages, stage, step, continuing, outlets, &mut cx)?;
182 continue;
183 }
184 if let Some(stage) = *finishing {
185 *finishing = Some(stage + 1);
186 let step = stages[stage].compute(Input::End, &mut cx)?;
187 push(stages, stage, step, continuing, outlets, &mut cx)?;
188 continue;
189 }
190 if !*source_more {
191 let ready = source.ready(&mut cx)?;
192 cx.changed.clear();
193 vortex_ensure!(
194 cx.reads.is_empty() || ready == SourceReady::Blocked(SourceBlocked::Io),
195 "A source that queues reads must block on IO"
196 );
197 match ready {
198 SourceReady::Ready => {}
199 SourceReady::Blocked(blocked) => return Ok(Progress::Blocked(blocked)),
200 }
201 }
202 let step = source.compute(Input::None, &mut cx)?;
203 *source_more = matches!(&step, Step::More(_));
204 match step {
205 Step::More(batch) | Step::Final(batch) => {
206 push_batch(stages, 0, batch, continuing, outlets, &mut cx)?;
207 }
208 Step::End => *finishing = Some(0),
209 }
210 }
211}
212
213fn push(
215 stages: &mut [Box<dyn Operator>],
216 stage: usize,
217 step: Step,
218 continuing: &mut Vec<usize>,
219 outlets: &[PortId],
220 cx: &mut Cx<'_>,
221) -> VortexResult<()> {
222 let batch = match step {
223 Step::End => return Ok(()),
224 Step::More(batch) => {
225 continuing.push(stage);
226 batch
227 }
228 Step::Final(batch) => batch,
229 };
230 push_batch(stages, stage + 1, batch, continuing, outlets, cx)
231}
232
233fn push_batch(
235 stages: &mut [Box<dyn Operator>],
236 start: usize,
237 mut batch: ArrayRef,
238 continuing: &mut Vec<usize>,
239 outlets: &[PortId],
240 cx: &mut Cx<'_>,
241) -> VortexResult<()> {
242 for (index, stage) in stages.iter_mut().enumerate().skip(start) {
243 batch = match stage.compute(Input::Batch(batch), cx)? {
244 Step::More(batch) => {
245 continuing.push(index);
246 batch
247 }
248 Step::Final(batch) => batch,
249 Step::End => return Ok(()),
250 };
251 }
252 push_out(batch, outlets, cx.arena);
253 Ok(())
254}
255
256fn push_out(batch: ArrayRef, outlets: &[PortId], arena: &mut Arena) {
258 if let Some((&last, rest)) = outlets.split_last() {
259 for &outlet in rest {
260 arena.push(outlet, batch.clone());
261 }
262 arena.push(last, batch);
263 }
264}
265
266pub(crate) struct Core {
269 pub(crate) session: VortexSession,
270 pub(crate) exec: ExecutionCtx,
271 pub(crate) row_offset: u64,
273 pub(crate) arena: Arena,
274 pipelines: Slab<Option<Pipeline>>,
275 free_pipelines: Vec<PipelineId>,
276 ready: Vec<PipelineId>,
279 reads: FxHashMap<ReadId, (Option<PipelineId>, SegmentId)>,
282 next_read: u64,
283 new_reads: VecDeque<ReadRequest>,
284 dirty: Vec<usize>,
287 dirty_flags: Vec<bool>,
288 pub(crate) shares: Shares,
289 pub(crate) split: usize,
292 pub(crate) values: ValueCache,
294}
295
296impl Core {
297 fn new(session: VortexSession, row_offset: u64, splits: Vec<Range<u64>>) -> Self {
298 Self {
299 exec: session.create_execution_ctx(),
300 session,
301 row_offset,
302 arena: Arena::default(),
303 pipelines: Slab::default(),
304 free_pipelines: Vec::new(),
305 ready: Vec::new(),
306 reads: FxHashMap::default(),
307 next_read: 0,
308 new_reads: VecDeque::new(),
309 dirty: Vec::new(),
310 dirty_flags: Vec::new(),
311 shares: Shares::new(splits),
312 split: usize::MAX,
313 values: ValueCache::default(),
314 }
315 }
316
317 pub(crate) fn add_pipeline(
319 &mut self,
320 chain: Chain,
321 outlets: SmallVec<[PortId; 1]>,
322 ) -> PipelineId {
323 let touched = InletSet::new(chain.inlets.len());
324 let changed = InletSet::all(chain.inlets.len());
325 let pipeline = Pipeline {
326 continuing: Vec::new(),
327 source_more: false,
328 source: chain.source,
329 stages: chain.stages,
330 inlets: chain.inlets,
331 outlets,
332 bytes: VecDeque::new(),
333 pending: 0,
334 finishing: None,
335 touched,
336 changed,
337 state: State::Runnable,
338 queued: true,
339 };
340 let id = match self.free_pipelines.pop() {
341 Some(id) => id,
342 None => self.pipelines.push(None),
343 };
344 for (index, &inlet) in pipeline.inlets.iter().enumerate() {
345 self.arena.get_mut(inlet).source = SourceEnd::Pipeline { id, inlet: index };
346 }
347 for &outlet in &pipeline.outlets {
348 self.arena.get_mut(outlet).sink = Some(id);
349 }
350 self.pipelines[id] = Some(pipeline);
351 self.ready.push(id);
352 id
353 }
354
355 pub(crate) fn compile_stage(
359 &mut self,
360 plan: &PlanRef,
361 rows: Range<u64>,
362 mask: &Mask,
363 (slot, split): (usize, usize),
364 ) -> VortexResult<Option<PortId>> {
365 self.split = split;
366 let chain = self.compile(plan, rows, mask);
367 self.split = usize::MAX;
368 let Some(chain) = chain? else {
369 return Ok(None);
370 };
371 let port = self
372 .arena
373 .create(DEFAULT_CAPACITY, None, SourceEnd::Scan(slot));
374 self.add_pipeline(chain, smallvec::smallvec![port]);
375 #[cfg(debug_assertions)]
376 self.validate();
377 Ok(Some(port))
378 }
379
380 fn pipeline(&self, id: PipelineId) -> &Pipeline {
381 self.pipelines[id]
382 .as_ref()
383 .unwrap_or_else(|| unreachable!("pipeline {id} is not live"))
384 }
385
386 fn pipeline_mut(&mut self, id: PipelineId) -> &mut Pipeline {
387 self.pipelines[id]
388 .as_mut()
389 .unwrap_or_else(|| unreachable!("pipeline {id} is not live"))
390 }
391
392 fn make_runnable(&mut self, id: PipelineId) {
393 let pipe = self.pipeline_mut(id);
394 pipe.state = State::Runnable;
395 if !pipe.queued {
396 pipe.queued = true;
397 self.ready.push(id);
398 }
399 }
400
401 fn run(&mut self, id: PipelineId) -> VortexResult<()> {
403 let pipe = self.pipelines[id]
404 .as_mut()
405 .ok_or_else(|| vortex_err!("Pipeline {id} is not live"))?;
406 pipe.queued = false;
407 let mut reads = SmallVec::new();
408 let progress = drive(
409 pipe,
410 &mut self.arena,
411 &self.session,
412 &mut self.exec,
413 &mut reads,
414 )?;
415 for segment_id in reads {
416 self.ask(id, segment_id);
417 }
418 #[cfg(debug_assertions)]
419 self.validate_progress(id, &progress);
420 match &progress {
421 Progress::Blocked(SourceBlocked::Io) if !self.pipeline(id).bytes.is_empty() => {
423 self.make_runnable(id);
424 }
425 Progress::Blocked(blocked) => self.pipeline_mut(id).state = State::Blocked(*blocked),
428 Progress::Outlet => self.pipeline_mut(id).state = State::Outlet,
429 Progress::Done => {}
430 }
431 self.wake_neighbours(id);
432 if matches!(progress, Progress::Done) {
433 self.retire(id);
434 }
435 Ok(())
436 }
437
438 fn ask(&mut self, id: PipelineId, segment_id: SegmentId) {
440 self.read(id, segment_id);
441 self.pipeline_mut(id).pending += 1;
442 }
443
444 fn wake_neighbours(&mut self, id: PipelineId) {
446 for index in 0..self.pipeline(id).outlets.len() {
447 let port = self.pipeline(id).outlets[index];
448 let queue = self.arena.get_mut(port);
449 if !queue.take_changed() {
450 continue;
451 }
452 match queue.source {
453 SourceEnd::Pipeline { id: source, inlet } => {
454 let source_pipe = self.pipeline_mut(source);
456 source_pipe.changed.insert(inlet);
457 if source_pipe.state == State::Blocked(SourceBlocked::Inlet(inlet)) {
458 self.make_runnable(source);
459 }
460 }
461 SourceEnd::Scan(slot) => self.mark_dirty(slot),
462 SourceEnd::Unclaimed => {}
463 }
464 }
465 let mut touched = std::mem::take(&mut self.pipeline_mut(id).touched);
468 for &index in touched.indices() {
469 if let Some(sink) = self.arena.get(self.pipeline(id).inlets[index]).sink
470 && sink != id
471 && self.sink_has_room(sink)
472 {
473 self.make_runnable(sink);
474 }
475 }
476 touched.clear();
477 self.pipeline_mut(id).touched = touched;
478 }
479
480 fn mark_dirty(&mut self, slot: usize) {
482 if !self.dirty_flags[slot] {
483 self.dirty_flags[slot] = true;
484 self.dirty.push(slot);
485 }
486 }
487
488 fn sink_has_room(&self, sink: PipelineId) -> bool {
490 let pipe = self.pipeline(sink);
491 pipe.state == State::Outlet
492 && pipe
493 .outlets
494 .iter()
495 .all(|&outlet| self.arena.has_room(outlet))
496 }
497
498 pub(crate) fn finish_split(&mut self, index: usize) {
501 for port in self.shares.finish(index) {
502 if let Some(sink) = self.arena.get(port).sink {
504 let pipe = self.pipeline_mut(sink);
505 pipe.outlets.retain(|outlet| *outlet != port);
506 debug_assert!(
509 !pipe.outlets.is_empty(),
510 "pipeline {sink} shares its output with no split"
511 );
512 self.arena.get_mut(port).sink = None;
513 if self.sink_has_room(sink) {
514 self.make_runnable(sink);
515 }
516 }
517 self.arena.discard(port);
518 }
519 }
520
521 fn retire(&mut self, id: PipelineId) {
525 if let Some(pipe) = self.pipelines[id].take() {
526 for inlet in pipe.inlets {
527 self.arena.drop_source(inlet);
528 }
529 if pipe.pending > 0 {
530 self.cancel_reads(id);
531 }
532 self.free_pipelines.push(id);
533 }
534 }
535
536 fn cancel_reads(&mut self, id: PipelineId) {
539 for (owner, _) in self.reads.values_mut() {
540 if *owner == Some(id) {
541 *owner = None;
542 }
543 }
544 }
545
546 fn read_output(&mut self, port: PortId) {
548 if let Some(sink) = self.arena.get(port).sink
549 && self.sink_has_room(sink)
550 {
551 self.make_runnable(sink);
552 }
553 }
554
555 fn deliver(&mut self, read: ReadId, bytes: BufferHandle) -> VortexResult<()> {
556 let (owner, segment) = self
557 .reads
558 .remove(&read)
559 .ok_or_else(|| vortex_err!("Unknown read {read:?}"))?;
560 if let Some(id) = owner {
561 let pipe = self.pipeline_mut(id);
562 pipe.pending -= 1;
563 pipe.bytes.push_back((segment, bytes));
564 self.make_runnable(id);
565 }
566 Ok(())
567 }
568
569 fn read(&mut self, owner: PipelineId, segment_id: SegmentId) {
571 let read = ReadId(self.next_read);
572 self.next_read += 1;
573 self.reads.insert(read, (Some(owner), segment_id));
574 self.new_reads.push_back(ReadRequest {
575 id: read,
576 segment_id,
577 });
578 }
579
580 fn describe_blocked(&self) -> String {
582 self.pipelines
583 .iter()
584 .enumerate()
585 .filter_map(|(id, pipe)| {
586 let pipe = pipe.as_ref()?;
587 Some(match pipe.state {
588 State::Blocked(SourceBlocked::Inlet(index)) => {
589 let port = pipe.inlets[index];
590 let queue = self.arena.get(port);
591 format!(
592 "{id}: {:?} (port {port}: {} queued, closed {}, sink {:?})",
593 pipe.state,
594 queue.len(),
595 queue.closed(),
596 queue.sink.map(|sink| (sink, self.pipeline(sink).state)),
597 )
598 }
599 state => format!("{id}: {state:?}"),
600 })
601 })
602 .collect::<Vec<_>>()
603 .join(", ")
604 }
605}
606
607#[derive(Clone)]
609enum Root {
610 Plan(PlanRef),
612}
613
614struct Active {
616 index: usize,
617 output: Option<PortId>,
619}
620
621pub struct Scan {
628 core: Core,
629 root: Option<Root>,
630 splits: Vec<Split>,
631 next: usize,
632 active: Vec<Option<Active>>,
633 live: usize,
634 outputs: VecDeque<(usize, ArrayRef)>,
635}
636
637impl fmt::Debug for Scan {
638 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
639 f.debug_struct("Scan")
640 .field("splits", &self.splits.len())
641 .field("next", &self.next)
642 .field("live", &self.live)
643 .finish()
644 }
645}
646
647impl Scan {
648 pub fn try_new(
653 session: VortexSession,
654 plan: PlanRef,
655 splits: Vec<Split>,
656 ) -> VortexResult<Self> {
657 for split in &splits {
658 vortex_ensure!(
659 split.rows.start <= split.rows.end
660 && split.rows.end <= plan.row_count()
661 && split.mask.len() as u64 == split.rows.end - split.rows.start,
662 "Split {:?} with a mask of {} rows does not fit a plan of {} rows",
663 split.rows,
664 split.mask.len(),
665 plan.row_count()
666 );
667 }
668 let root = Root::Plan(plan);
669 let mut core = Core::new(
670 session,
671 0,
672 splits.iter().map(|split| split.rows.clone()).collect(),
673 );
674 let start = splits
676 .iter()
677 .map(|split| split.rows.start)
678 .min()
679 .unwrap_or(0);
680 let end = splits.iter().map(|split| split.rows.end).max().unwrap_or(0);
681 match &root {
682 Root::Plan(plan) => core.shares.add(plan, start..end)?,
683 }
684 core.shares.retain_shared();
685 core.dirty_flags = vec![false; 1];
686 Ok(Self {
687 core,
688 root: Some(root),
689 splits,
690 next: 0,
691 active: vec![None],
692 live: 0,
693 outputs: VecDeque::new(),
694 })
695 }
696
697 pub fn with_max_active(mut self, max_active: usize) -> Self {
699 self.active.resize_with(max_active.max(1), || None);
700 self.core.dirty_flags = vec![false; self.active.len()];
701 self.core.dirty = Vec::with_capacity(self.active.len());
702 self
703 }
704
705 pub fn with_row_offset(mut self, row_offset: u64) -> Self {
707 self.core.row_offset = row_offset;
708 self
709 }
710
711 pub fn with_value_cache(mut self, values: ValueCache) -> Self {
713 self.core.values = values;
714 self
715 }
716
717 pub fn step(&mut self) -> VortexResult<Turn> {
719 loop {
720 if let Some((split, array)) = self.outputs.pop_front() {
721 return Ok(Turn::Output(split, array));
722 }
723 if let Some(read) = self.core.new_reads.pop_front() {
724 return Ok(Turn::Read(read));
725 }
726 if let Some(slot) = self.core.dirty.pop() {
727 self.core.dirty_flags[slot] = false;
728 self.advance(slot)?;
729 continue;
730 }
731 if self.live < self.active.len() && self.next < self.splits.len() {
732 self.activate()?;
733 continue;
734 }
735 if let Some(id) = self.core.ready.pop() {
736 self.core.run(id)?;
737 continue;
738 }
739 if self.live == 0 {
742 return Ok(Turn::Done);
743 }
744 if !self.core.reads.is_empty() {
745 return Ok(Turn::Waiting);
746 }
747 vortex_bail!(
748 "Scan is stuck with nothing runnable and no read in flight: {}",
749 self.core.describe_blocked()
750 );
751 }
752 }
753
754 pub fn deliver(&mut self, read: ReadId, bytes: BufferHandle) -> VortexResult<()> {
756 self.core.deliver(read, bytes)
757 }
758
759 fn activate(&mut self) -> VortexResult<()> {
761 let slot = self
762 .active
763 .iter()
764 .position(Option::is_none)
765 .ok_or_else(|| vortex_err!("No free split slot"))?;
766 let index = self.next;
767 self.next += 1;
768 let split = self.splits[index].clone();
769 let output = match self
770 .root
771 .as_ref()
772 .ok_or_else(|| vortex_err!("Scan has no plan for its next split"))?
773 {
774 Root::Plan(plan) => {
775 self.core
776 .compile_stage(plan, split.rows.clone(), &split.mask, (slot, index))?
777 }
778 };
779 if self.next == self.splits.len() {
780 self.root = None;
783 }
784 if output.is_none() {
785 self.core.finish_split(index);
786 } else {
787 self.active[slot] = Some(Active { index, output });
788 self.live += 1;
789 }
790 Ok(())
791 }
792
793 fn advance(&mut self, slot: usize) -> VortexResult<()> {
796 let Some(active) = self.active[slot].as_mut() else {
797 return Ok(());
798 };
799 while let Some(port) = active.output {
800 while let Some(batch) = self.core.arena.get_mut(port).pop() {
801 self.outputs.push_back((active.index, batch));
802 }
803 self.core.read_output(port);
804 if !self.core.arena.get(port).closed() {
805 return Ok(());
806 }
807 self.core.arena.drop_source(port);
808 active.output = None;
809 }
810 self.core.finish_split(active.index);
811 self.active[slot] = None;
812 self.live -= 1;
813 Ok(())
814 }
815
816 #[cfg(test)]
818 pub(crate) fn live_ports(&self) -> usize {
819 self.core.arena.live()
820 }
821
822 #[cfg(test)]
824 pub(crate) fn awaited_reads(&self) -> usize {
825 self.core
826 .reads
827 .values()
828 .filter(|(owner, _)| owner.is_some())
829 .count()
830 }
831
832 #[cfg(test)]
834 pub(crate) fn pending_shares(&self) -> usize {
835 self.core.shares.pending()
836 }
837}