Skip to content

Fix pinned drop violations; make types Unpin where practical#222

Merged
matheus-consoli merged 4 commits into
yoshuawuyts:mainfrom
A248:soundly-pinned
Jan 22, 2026
Merged

Fix pinned drop violations; make types Unpin where practical#222
matheus-consoli merged 4 commits into
yoshuawuyts:mainfrom
A248:soundly-pinned

Conversation

@A248

@A248 A248 commented Jan 13, 2026

Copy link
Copy Markdown
Contributor

I was writing a library of my own and decided to test this one. Although I had seen that #188 described pinning problems, I only discovered the extent of this myself, when this library began failing my code's anti-move checks.

This PR shores up the pin-on-drop problem. Pinned types have to be dropped in the same place, and ChunkedVec wasn't doing this.

Changes summary

  • FutureGroup and StreamGroup are now Unpin types. At heart, they kinda always were (see below)
  • ChunkedVec is now built around pinning, fulfilling its destiny as a data structure made specifically for pinnable data
  • Brief changes to tests, to catch the bug that spawned this PR to begin with

Unpinned collections

Properly speaking, neither FutureGroup nor StreamGroup need to be pinned. The futures/streams are placed in allocations managed by ChunkedVec, which is responsible for keeping the futures/streams pinned. This has been true. In fact, this means the entire pin_project/#[pin] management done by these types was superfluous. FutureGroup and StreamGroup don't store any pinnable data inline, not even when the implementation used Slab.

Going forward, these types can be safely exposed as Unpin. Even if their implementation changes again, FutureGroup and StreamGroup are never going to store their streams/futures on stack. Only inline storage makes pinning self-referential data meaningful; heap storage will always be stable. Thus, making them Unpin is totally safe from the perspective of future compatibility.

Growing streams and polling them too

As a side effect of making FutureGroup and StreamGroup Unpin types, they can now have items added to them even after they've been pinned. This was already the case for Unpin items, but now it will be true for !Unpin types as well.

This was alluded to in #188: by requiring the group to be pinned in order to be polled, the size becomes frozen and new insertions are impossible. I didn't investigate the previous version of futures-concurrency very much, but "freezing" the collection might be the only reason it used pinning.

That said, abandoning that design is surely for the better, and I don't think future implementations of this crate would ever need to bring it back. It was more of a side effect and a quirk of how the crate was implemened.

A248 added 2 commits January 14, 2026 02:06
* FutureGroup and StreamGroup are now `Unpin` types. At heart,
  they kinda always were
* ChunkedVec is now built around pinning, fulfilling its destiny as
  a data structure made specifically for pinnable data
* Brief changes to tests, to catch the bug that spawned this commit

Properly speaking, neither FutureGroup nor StreamGroup need to be
pinned. The futures/streams are placed in allocations managed by
ChunkedVec, which is responsible for keeping the futures/streams
pinned. This has been true. In fact, this means the entire
pin_project/`#[pin]` management done by these types was superfluous.
FutureGroup and StreamGroup don't store any pinnable data inline.

Going forward, these types can be safely exposed as Unpin. Even if
their implementation changes again, FutureGroup and StreamGroup are
never going to store their streams/futures on stack. Only inline
storage makes pinning self-referential data meaningful; heap
storage will always be stable. Thus, making them Unpin is totally
safe from the perspective of future compatibility.

The same goes for derived types. As long as the futures/streams
are polled on the heap, there's no way they will need to be pinned
in the old-fashioned (inline) way.
@A248

A248 commented Jan 13, 2026

Copy link
Copy Markdown
Contributor Author

I don't know why, however, MIRI is very slow to run the pin_safety tests. If this is not related to this PR, feel free to disregard.

Running tests/pin_safety.rs (target/miri/x86_64-unknown-linux-gnu/debug/deps/pin_safety-385fd5db276d1151)
running 4 tests
test future_group_no_move_on_repeated_growth ... ok
test stream_group_no_move_on_growth ... ok
test stream_group_no_move_on_repeated_growth ... ok
test result: ok. 4 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 67.99s

A248 added 2 commits January 17, 2026 17:57
If dropping one of the items in ChunkedVec fails from a panic,
  the other items won't get dropped. So, the memory has to be
  leaked in order to uphold the Drop guarantee for pinned data.
@yoshuawuyts

Copy link
Copy Markdown
Owner

Thanks for submitting this patch! I thing @matheus-consoli is probably the right person to review this, which is why I've assigned them to it. However if they don't get around to it by the end of next week, please feel free to ping me and I'll do my best to review this.

@matheus-consoli matheus-consoli left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Hey, thank you so much for fixing this! Everything looks great

@matheus-consoli matheus-consoli merged commit e66b2ad into yoshuawuyts:main Jan 22, 2026
7 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants