Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Make pinned-init usable with 1.82.0+ #24

Merged
merged 7 commits into from
Nov 29, 2024
Merged

Conversation

bonzini
Copy link
Contributor

@bonzini bonzini commented Nov 19, 2024

This pull request, on top of #23, makes it possible to use pinned_init with stable Rust. In particular:

  • new_uninit has been stabilized (and anyway in the end it was just a glorified Box::<MaybeUninit>::new())
  • allocator_api is needed to have fallible allocation, but not if you just replace core::alloc::AllocError with core::convert::Infallible
  • get_mut_unchecked is not needed, because after the introduction of InPlaceWrite you actually need to check that you're not writing to a shared Arc—thus Arc::get_mut is the right associated function to use.

The main work is to isolate a little bit more the pieces of code that refer to feature(allocator_api).

Successful CI run at bonzini#2.

Diff on top of #23 here.

@bonzini bonzini changed the title Stable Make pinned-init usable with 1.82.0+ Nov 19, 2024
@y86-dev
Copy link
Member

y86-dev commented Nov 22, 2024

do you mind rebasing this one on top of main? thanks!

Copy link
Member

@y86-dev y86-dev left a comment

Choose a reason for hiding this comment

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

If I only enable the arc feature (with default-features = false), then it doesn't compile, this should fix it.

.github/workflows/check.yml Outdated Show resolved Hide resolved
src/lib.rs Outdated Show resolved Hide resolved
src/lib.rs Outdated Show resolved Hide resolved
@bonzini bonzini force-pushed the stable branch 8 times, most recently from 60eb861 to 0b1558a Compare November 22, 2024 16:05
@bonzini bonzini requested a review from y86-dev November 22, 2024 16:31
@bonzini bonzini force-pushed the stable branch 4 times, most recently from bfe482d to bc78bd2 Compare November 24, 2024 11:56
@bonzini
Copy link
Contributor Author

bonzini commented Nov 24, 2024

If I only enable the arc feature (with default-features = false), then it doesn't compile, this should fix it.

Thinking more about it, after the introduction of InPlaceWrite you actually need to check that you're not writing to a shared Arc. Thus Arc::get_mut is the right associated function to use; get_mut_unchecked goes away and the need for an arc feature with it.

@y86-dev
Copy link
Member

y86-dev commented Nov 25, 2024

You're correct. But having the function panic is not a good idea, since panics in the kernel are not an option.

In that case, it probably makes more sense to just not implement InPlaceWrite for Arc or come up with a better error type. We'd still need to use get_mut_unchecked in InPlaceInit though.

@bonzini
Copy link
Contributor Author

bonzini commented Nov 25, 2024

it probably makes more sense to just not implement InPlaceWrite for Arc

As you prefer! The choice is:

  • keep the code more similar to the kernel, at the cost of a possible panic due to lack of UniqueArc (userspace panics are not as bad, and InPlaceInit will never panic)
  • avoid panics, and remove the impl<T> InPlaceWrite for Arc<T>, though this makes future backports a bit harder

We'd still need to use get_mut_unchecked in InPlaceInit though.

It's not strictly necessary. There are cases in which get_mut_unchecked is useful even with refcount > 1, but pinned_init is only concerned with the case of refcount == 1 because it provides a safe API. So using get_mut_unchecked is only a small optimization. Again there are multiple choices:

  • use Arc::get_mut(...).unwrap(), the performance cost is probably minimal and we know it can't fail
  • use Arc::get_mut_unchecked(...) and disable Arc on stable Rust, preserving the optimization.
  • check for it in build.rs, but it seems a bit overengineered

In both cases, just tell me which of the possibilities you prefer and I'll implement it. :)

@y86-dev
Copy link
Member

y86-dev commented Nov 25, 2024

it probably makes more sense to just not implement InPlaceWrite for Arc

As you prefer! The choice is:

  • keep the code more similar to the kernel, at the cost of a possible panic due to lack of UniqueArc (userspace panics are not as bad, and InPlaceInit will never panic)

  • avoid panics, and remove the impl<T> InPlaceWrite for Arc<T>, though this makes future backports a bit harder

I think we should remove the InPlaceWrite impl for Arc. What do you mean by "this makes future backports a bit harder"?

We'd still need to use get_mut_unchecked in InPlaceInit though.

It's not strictly necessary. There are cases in which get_mut_unchecked is useful even with refcount > 1, but pinned_init is only concerned with the case of refcount == 1 because it provides a safe API. So using get_mut_unchecked is only a small optimization. Again there are multiple choices:

  • use Arc::get_mut(...).unwrap(), the performance cost is probably minimal and we know it can't fail

  • use Arc::get_mut_unchecked(...) and disable Arc on stable Rust, preserving the optimization.

  • check for it in build.rs, but it seems a bit overengineered

Oh I have an idea, we can just use

match Arc::get_mut(...) {
    Ok(...) => ...
    Err(...) => unsafe { hint::unreachable_unchecked() },
}

That way we have the performance and don't rely on an unstable feature.

@bonzini
Copy link
Contributor Author

bonzini commented Nov 25, 2024

What do you mean by "this makes future backports a bit harder"?

I mean that future backports may have to account for the Arc implementation being in <UniqueArc<T> as InPlaceWrite<T, E>> for the kernel and <Arc<T> as InPlaceInit<T, E>> for the userspace crate. No objections though, I will do as you requested over the next few days.

Copy link
Member

@y86-dev y86-dev left a comment

Choose a reason for hiding this comment

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

some minor things, but otherwise looks pretty good.

README.md Outdated Show resolved Hide resolved
build.rs Outdated Show resolved Hide resolved
src/lib.rs Outdated Show resolved Hide resolved
src/lib.rs Outdated Show resolved Hide resolved
@bonzini bonzini force-pushed the stable branch 2 times, most recently from 87f96df to 0251607 Compare November 26, 2024 11:48
Copy link
Member

@y86-dev y86-dev left a comment

Choose a reason for hiding this comment

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

I sadly can't comment on commit messages, in the first commit 876f228 ("lib: revert InPlaceWrite implementation for Arc<MaybeUninit<T>>") I would prefer if you put Suggested-by: Benno Lossin <[email protected]>, for kernel tags, I use that. Thanks!

Same of course for the next one.


1919cf5 ("lib: make "std" independent of allocator_api") also needs its commit message updated, it still references the get_mut_unchecked feature.

README.md Outdated Show resolved Hide resolved
README.md Outdated Show resolved Hide resolved
The kernel version of pinned_init implemented InPlaceWrite on UniqueArc,
not Arc.  This ensures that InPlaceWrite is not writing to a shared Arc.
Userspace does not have this facility and therefore cannot lift the
kernel implementation of InPlaceWrite directly into Arc<>.

One possibility would be to use Arc::get_mut(), though this would introduce
a panic in the case where the Arc is shared.  So just revert part of
commit 6841b61 ("rust: init: add `write_[pin_]init` functions", 2024-11-22).

Suggested-by: Benno Lossin <[email protected]>
Signed-off-by: Paolo Bonzini <[email protected]>
Instead of using an unstable feature use unreachable_unchecked() to
enable optimization.

Suggested-by: Benno Lossin <[email protected]>
Signed-off-by: Paolo Bonzini <[email protected]>
The only dependency of the InPlaceInit trait on the allocator API is the
AllocError type.  Replace it with Infallible instead, i.e. allow any
error as long as it has an "impl From<Infallible> for MyError" - which
can have a trivial implementation as seen in examples/rror.rs.

While admittedly of limited usefulness due to orphan rules, this is
a first step towards allowing usage of pinned_init entirely without
the allocator API, and therefore on stable Rust.

Signed-off-by: Paolo Bonzini <[email protected]>
Memory allocation (i.e. the Box and Arc) can be present even without the
allocator_api.  Allow feature = "std" when the code is simply checking for
the existence of Box and Arc, and the allocator API is not used.

Signed-off-by: Paolo Bonzini <[email protected]>
When compiling without allocator_api, assume that allocations cannot fail.
This way, nightly Rust features are not absolutely needed for pinned_init,
and it can be used with stable Rust; right now the minimum supported Rust
version is 1.82.0, where the new_uninit version was stabilized.

Signed-off-by: Paolo Bonzini <[email protected]>
Signed-off-by: Paolo Bonzini <[email protected]>
@y86-dev y86-dev merged commit 812af8a into Rust-for-Linux:main Nov 29, 2024
15 checks passed
@y86-dev
Copy link
Member

y86-dev commented Nov 29, 2024

thanks again for all the help!

@bonzini bonzini deleted the stable branch December 2, 2024 15:31
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Development

Successfully merging this pull request may close these issues.

2 participants