Skip to content

fix: Remove nested async block causing Stacked Borrows violation in PushDecoderStreamState#21663

Merged
mbutrovich merged 2 commits intoapache:mainfrom
mbutrovich:miri_fix
Apr 16, 2026
Merged

fix: Remove nested async block causing Stacked Borrows violation in PushDecoderStreamState#21663
mbutrovich merged 2 commits intoapache:mainfrom
mbutrovich:miri_fix

Conversation

@mbutrovich
Copy link
Copy Markdown
Contributor

Which issue does this PR close?

Rationale for this change

Miri detects a Stacked Borrows violation in PushDecoderStreamState::transition. A nested async block captures &mut self as a single opaque mutable reference. At the .await on get_byte_ranges, the future yields, and the Unique tag on the borrow stack is invalidated by a SharedReadOnly retag. When the future resumes, push_ranges attempts a two-phase retag through the now-invalidated tag.

This was found by Apache DataFusion Comet, which runs Miri in CI.

What changes are included in this PR?

Remove the nested async block in the NeedsData arm of PushDecoderStreamState::transition and inline the IO (get_byte_ranges) and CPU (push_ranges) operations as separate statements. Since transition is already an async fn, the .await works directly in the loop body. Without the nested block, the compiler can split the borrows of self.reader and self.decoder into disjoint field borrows, keeping the borrow stack valid across the yield point.

Also removes the now-unused parquet::errors::ParquetError import.

Are these changes tested?

Covered by existing parquet reader tests. The original violation was caught by Miri, which DataFusion does not currently run in CI.

Are there any user-facing changes?

No.

@github-actions github-actions bot added the datasource Changes to the datasource crate label Apr 16, 2026
@mbutrovich mbutrovich requested a review from Dandandan April 16, 2026 02:36
Copy link
Copy Markdown
Contributor

@Dandandan Dandandan left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code looks fine before / after but agree that it would be annoying having to ignore the issue downstream.

@mbutrovich mbutrovich added this pull request to the merge queue Apr 16, 2026
Merged via the queue into apache:main with commit 8b47f45 Apr 16, 2026
31 checks passed
@mbutrovich mbutrovich deleted the miri_fix branch April 16, 2026 11:55
Copy link
Copy Markdown
Contributor

@alamb alamb left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you @mbutrovich and @Dandandan

@mbutrovich
Copy link
Copy Markdown
Contributor Author

Thank you @mbutrovich and @Dandandan

There's actually a followup: #21680 :(

github-merge-queue bot pushed a commit that referenced this pull request Apr 16, 2026
## Which issue does this PR close?

- Closes #21662.

## Rationale for this change

#21663 removed the nested async block in
`PushDecoderStreamState::transition` to fix a Stacked Borrows violation
under miri. However, miri still flags the same violation because `&mut
self` is still a single opaque borrow — inlining the async block alone
doesn't let miri split the disjoint field borrows across the `.await`
yield point.

Comet CI reproduces this reliably:
https://github.com/apache/datafusion-comet/actions/runs/24518967597/job/71671004017?pr=3916

## What changes are included in this PR?

Change `PushDecoderStreamState::transition` to take `self` by value
instead of `&mut self`. With `&mut self`, the generated future stores a
mutable reference, and when `unfold` pins and polls it, miri sees the
`&mut self` as a single opaque borrow that conflicts across the `.await`
yield point. With owned `self`, the future owns the state directly — no
reference means no Stacked Borrows conflict. The struct fields are all
heap-allocated or reference-counted, so the move is just pointer-sized
copies, not a deep copy.

## Are these changes tested?

Existing tests cover this code path. The fix is validated by miri
passing in CI (the same test that currently fails:
`test_nested_types_extract_missing_struct_names_missing_field`). We'll
run Comet CI against this branch first to confirm the miri violation is
resolved before merging.

## Are there any user-facing changes?

No.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

datasource Changes to the datasource crate

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Stacked Borrows violation in PushDecoderStreamState::transition

4 participants