Skip to content

Commit 29c8301

Browse files
committed
Make re-entrant in btree now that cache insert returns IO
1 parent 93b1e55 commit 29c8301

File tree

1 file changed

+76
-13
lines changed

1 file changed

+76
-13
lines changed

core/storage/btree.rs

Lines changed: 76 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -506,6 +506,14 @@ pub enum CursorValidState {
506506
RequireAdvance(IterationDirection),
507507
}
508508

509+
#[derive(Debug, Clone)]
510+
struct PendingChild {
511+
page_id: i64,
512+
push_backwards: bool,
513+
set_cell_index: Option<i32>,
514+
retreat_parent: bool,
515+
}
516+
509517
#[derive(Debug)]
510518
/// State used for seeking
511519
pub enum CursorSeekState {
@@ -674,6 +682,8 @@ pub struct BTreeCursor {
674682
/// Advancing is only skipped if the cursor is currently pointing to a valid record
675683
/// when next() is called.
676684
pub skip_advance: Cell<bool>,
685+
/// Pending child page read that must complete before mutating the stack (keeps loops re-entrant on IO).
686+
pending_child: RefCell<Option<PendingChild>>,
677687
}
678688

679689
/// We store the cell index and cell count for each page in the stack.
@@ -699,6 +709,40 @@ impl BTreeNodeState {
699709
}
700710

701711
impl BTreeCursor {
712+
/// Load a pending child page if present, applying deferred stack mutations only after the read completes.
713+
fn load_pending_child(&mut self) -> Result<IOResult<()>> {
714+
let Some(pending) = self.pending_child.borrow_mut().take() else {
715+
return Ok(IOResult::Done(()));
716+
};
717+
718+
match self.read_page(pending.page_id)? {
719+
IOResult::Done((page, c)) => {
720+
if let Some(c) = c {
721+
if !c.succeeded() {
722+
*self.pending_child.borrow_mut() = Some(pending);
723+
return Ok(IOResult::IO(IOCompletions::Single(c)));
724+
}
725+
}
726+
if pending.retreat_parent {
727+
self.stack.retreat();
728+
}
729+
if let Some(idx) = pending.set_cell_index {
730+
self.stack.set_cell_index(idx);
731+
}
732+
if pending.push_backwards {
733+
self.stack.push_backwards(page);
734+
} else {
735+
self.stack.push(page);
736+
}
737+
Ok(IOResult::Done(()))
738+
}
739+
IOResult::IO(c) => {
740+
*self.pending_child.borrow_mut() = Some(pending);
741+
Ok(IOResult::IO(c))
742+
}
743+
}
744+
}
745+
702746
pub fn new(pager: Arc<Pager>, root_page: i64, num_columns: usize) -> Self {
703747
let valid_state = if root_page == 1 && !pager.db_state.get().is_initialized() {
704748
CursorValidState::Invalid
@@ -741,6 +785,7 @@ impl BTreeCursor {
741785
seek_end_state: SeekEndState::Start,
742786
move_to_state: MoveToState::Start,
743787
skip_advance: Cell::new(false),
788+
pending_child: RefCell::new(None),
744789
}
745790
}
746791

@@ -802,6 +847,9 @@ impl BTreeCursor {
802847
#[instrument(skip(self), level = Level::DEBUG, name = "prev")]
803848
pub fn get_prev_record(&mut self) -> Result<IOResult<bool>> {
804849
loop {
850+
if let IOResult::IO(c) = self.load_pending_child()? {
851+
return Ok(IOResult::IO(c));
852+
}
805853
let (old_top_idx, page_type, is_index, is_leaf, cell_count) = {
806854
let page = self.stack.top_ref();
807855
let contents = page.get_contents();
@@ -822,12 +870,12 @@ impl BTreeCursor {
822870
let rightmost_pointer = self.stack.top_ref().get_contents().rightmost_pointer();
823871
if let Some(rightmost_pointer) = rightmost_pointer {
824872
let past_rightmost_pointer = cell_count as i32 + 1;
825-
self.stack.set_cell_index(past_rightmost_pointer);
826-
let (page, c) = return_if_io!(self.read_page(rightmost_pointer as i64));
827-
self.stack.push_backwards(page);
828-
if let Some(c) = c {
829-
io_yield_one!(c);
830-
}
873+
*self.pending_child.borrow_mut() = Some(PendingChild {
874+
page_id: rightmost_pointer as i64,
875+
push_backwards: true,
876+
set_cell_index: Some(past_rightmost_pointer),
877+
retreat_parent: false,
878+
});
831879
continue;
832880
}
833881
}
@@ -892,7 +940,14 @@ impl BTreeCursor {
892940
// this parent: key 666
893941
// left child has: key 663, key 664, key 665
894942
// we need to move to the previous parent (with e.g. key 662) when iterating backwards.
895-
self.stack.retreat();
943+
// Defer retreat until after the child page load completes to keep this re-entrant.
944+
self.pending_child.borrow_mut().replace(PendingChild {
945+
page_id: left_child_page as i64,
946+
push_backwards: true,
947+
set_cell_index: None,
948+
retreat_parent: true,
949+
});
950+
continue;
896951
}
897952

898953
let (mem_page, c) = return_if_io!(self.read_page(left_child_page as i64));
@@ -946,13 +1001,21 @@ impl BTreeCursor {
9461001
*remaining_to_read -= to_read;
9471002

9481003
if *remaining_to_read != 0 && next != 0 {
949-
let (new_page, c) = return_if_io!(self.pager.read_page(next as i64));
950-
*page = new_page;
951-
*next_page = next;
952-
if let Some(c) = c {
953-
io_yield_one!(c);
1004+
match self.pager.read_page(next as i64)? {
1005+
IOResult::Done((new_page, c)) => {
1006+
*page = new_page;
1007+
*next_page = next;
1008+
if let Some(c) = c {
1009+
io_yield_one!(c);
1010+
}
1011+
continue;
1012+
}
1013+
IOResult::IO(c) => {
1014+
// Preserve progress and retry after IO completes.
1015+
*next_page = next;
1016+
return Ok(IOResult::IO(c));
1017+
}
9541018
}
955-
continue;
9561019
}
9571020
turso_assert!(
9581021
*remaining_to_read == 0 && next == 0,

0 commit comments

Comments
 (0)