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 it usable with 1.82.0+ #2

Closed
wants to merge 7 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
39 changes: 20 additions & 19 deletions .github/workflows/check.yml
Original file line number Diff line number Diff line change
Expand Up @@ -84,22 +84,23 @@ jobs:
run: cargo install cargo-rdme
- name: cargo rdme --check
run: cargo rdme --check
# disable msrv, since we are nightly only
# msrv:
# runs-on: ubuntu-latest
# # we use a matrix here just because env can't be used in job names
# # https://docs.github.com/en/actions/learn-github-actions/contexts#context-availability
# strategy:
# matrix:
# msrv: [1.61.0] # `impl Trait` requires this
# name: ubuntu / ${{ matrix.msrv }}
# steps:
# - uses: actions/checkout@v4
# with:
# submodules: true
# - name: Install ${{ matrix.msrv }}
# uses: dtolnay/rust-toolchain@master
# with:
# toolchain: ${{ matrix.msrv }}
# - name: cargo +${{ matrix.msrv }} check
# run: cargo check
msrv:
runs-on: ubuntu-latest
# we use a matrix here just because env can't be used in job names
# https://docs.github.com/en/actions/learn-github-actions/contexts#context-availability
strategy:
matrix:
msrv: [1.82.0] # new_uninit requires this
name: ubuntu / ${{ matrix.msrv }}
steps:
- uses: actions/checkout@v4
with:
submodules: true
- name: Install ${{ matrix.msrv }}
uses: dtolnay/rust-toolchain@master
with:
toolchain: ${{ matrix.msrv }}
- name: cargo install cargo-hack
uses: taiki-e/install-action@cargo-hack
- name: cargo hack
run: cargo +${{ matrix.msrv }} hack --feature-powerset --exclude-features alloc,default check
4 changes: 2 additions & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,8 @@ paste = "1.0"
pinned-init-macro = { path = "./pinned-init-macro", version = "=0.0.5" }

[features]
default = ["std"]
std = ["alloc"]
default = ["std", "alloc"]
std = []
alloc = []

[dev-dependencies]
Expand Down
16 changes: 9 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,15 +26,17 @@ it into it's final memory location.

This library allows you to do in-place initialization safely.

### Nightly Needed for `alloc` and `std` features
### Nightly Needed for `alloc` feature

This library requires unstable features when the `alloc` or `std` features are enabled and thus
can only be used with a nightly compiler. The internally used features are:
- `allocator_api`
- `get_mut_unchecked`
This library requires the `allocator_api` unstable feature when the `alloc` feature
is enabled and thus this feature can only be used with a nightly compiler.
When enabling the `alloc` feature, the user will be required to activate
`allocator_api` as well.

When enabling the `alloc` or `std` feature, the user will be required to activate these features:
- `allocator_api`
The feature is enabled by default, thus by default `pinned-init` will require a
nightly compiler. However, using the crate on stable compilers is possible by
disabling `alloc`. In practice this will require the `std` feature, because
stable compilers have neither `Box` nor `Arc` in no-std mode.

## Overview

Expand Down
3 changes: 2 additions & 1 deletion examples/linked_list.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
#![feature(allocator_api)]
#![cfg_attr(feature = "alloc", feature(allocator_api))]

use core::{
cell::Cell,
convert::Infallible,
Expand Down
7 changes: 6 additions & 1 deletion examples/mutex.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
#![feature(allocator_api)]
#![cfg_attr(feature = "alloc", feature(allocator_api))]

use core::{
cell::{Cell, UnsafeCell},
marker::PhantomPinned,
Expand Down Expand Up @@ -161,8 +162,12 @@ impl WaitEntry {
}
}

#[cfg(not(any(feature = "std", feature = "alloc")))]
fn main() {}

#[allow(dead_code)]
#[cfg_attr(test, test)]
#[cfg(any(feature = "std", feature = "alloc"))]
fn main() {
let mtx: Pin<Arc<CMutex<usize>>> = Arc::pin_init(CMutex::new(0)).unwrap();
let mut handles = vec![];
Expand Down
9 changes: 6 additions & 3 deletions examples/pthread_mutex.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
// inspired by https://github.com/nbdd0121/pin-init/blob/trunk/examples/pthread_mutex.rs
#![feature(allocator_api)]
#![cfg_attr(feature = "alloc", feature(allocator_api))]
#[cfg(not(windows))]
mod pthread_mtx {
#[cfg(feature = "alloc")]
use core::alloc::AllocError;
use core::{
alloc::AllocError,
cell::UnsafeCell,
marker::PhantomPinned,
mem::MaybeUninit,
Expand Down Expand Up @@ -45,6 +46,8 @@ mod pthread_mtx {
match e {}
}
}

#[cfg(feature = "alloc")]
impl From<AllocError> for Error {
fn from(_: AllocError) -> Self {
Self::Alloc
Expand Down Expand Up @@ -129,7 +132,7 @@ mod pthread_mtx {

#[cfg_attr(test, test)]
fn main() {
#[cfg(not(windows))]
#[cfg(all(any(feature = "std", feature = "alloc"), not(windows)))]
{
use core::pin::Pin;
use pinned_init::*;
Expand Down
6 changes: 5 additions & 1 deletion examples/static_init.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
#![feature(allocator_api)]
#![cfg_attr(feature = "alloc", feature(allocator_api))]

use core::{
cell::{Cell, UnsafeCell},
Expand Down Expand Up @@ -77,6 +77,10 @@ unsafe impl PinInit<CMutex<usize>> for CountInit {

pub static COUNT: StaticInit<CMutex<usize>, CountInit> = StaticInit::new(CountInit);

#[cfg(not(any(feature = "std", feature = "alloc")))]
fn main() {}

#[cfg(any(feature = "std", feature = "alloc"))]
fn main() {
let mtx: Pin<Arc<CMutex<usize>>> = Arc::pin_init(CMutex::new(0)).unwrap();
let mut handles = vec![];
Expand Down
101 changes: 57 additions & 44 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,15 +18,17 @@
//!
//! This library allows you to do in-place initialization safely.
//!
//! ## Nightly Needed for `alloc` and `std` features
//! ## Nightly Needed for `alloc` feature
//!
//! This library requires unstable features when the `alloc` or `std` features are enabled and thus
//! can only be used with a nightly compiler. The internally used features are:
//! - `allocator_api`
//! - `get_mut_unchecked`
//! This library requires the `allocator_api` unstable feature when the `alloc` feature
//! is enabled and thus this feature can only be used with a nightly compiler.
//! When enabling the `alloc` feature, the user will be required to activate
//! `allocator_api` as well.
//!
//! When enabling the `alloc` or `std` feature, the user will be required to activate these features:
//! - `allocator_api`
//! The feature is enabled by default, thus by default `pinned-init` will require a
//! nightly compiler. However, using the crate on stable compilers is possible by
//! disabling `alloc`. In practice this will require the `std` feature, because
//! stable compilers have neither `Box` nor `Arc` in no-std mode.
//!
//! # Overview
//!
Expand Down Expand Up @@ -236,15 +238,14 @@
#![forbid(missing_docs, unsafe_op_in_unsafe_fn)]
#![cfg_attr(not(feature = "std"), no_std)]
#![cfg_attr(feature = "alloc", feature(allocator_api))]
#![cfg_attr(feature = "alloc", feature(get_mut_unchecked))]

#[cfg(feature = "alloc")]
extern crate alloc;

#[cfg(all(feature = "alloc", not(feature = "std")))]
use alloc::boxed::Box;
#[cfg(feature = "alloc")]
use alloc::sync::Arc;
use alloc::{boxed::Box, sync::Arc};
#[cfg(feature = "std")]
use std::sync::Arc;

use core::{
cell::UnsafeCell,
Expand All @@ -259,6 +260,11 @@ use core::{
#[cfg(feature = "alloc")]
use core::alloc::AllocError;

// Allocations are infallible without the allocator API. In that case, just
// require From<Infallible> for the trait that is passed to the try_* macros,
#[cfg(not(feature = "alloc"))]
type AllocError = Infallible;

#[doc(hidden)]
pub mod __internal;
#[doc(hidden)]
Expand Down Expand Up @@ -1156,7 +1162,6 @@ unsafe impl<T, E> PinInit<T, E> for T {
}

/// Smart pointer that can initialize memory in-place.
#[cfg(feature = "alloc")]
pub trait InPlaceInit<T>: Sized {
/// Use the given pin-initializer to pin-initialize a `T` inside of a new smart pointer of this
/// type.
Expand Down Expand Up @@ -1200,40 +1205,73 @@ pub trait InPlaceInit<T>: Sized {
}

#[cfg(feature = "alloc")]
macro_rules! try_new_uninit {
($type:ident) => {
$type::try_new_uninit()?
};
}
#[cfg(all(feature = "std", not(feature = "alloc")))]
macro_rules! try_new_uninit {
($type:ident) => {
$type::new_uninit()
};
}

#[cfg(any(feature = "std", feature = "alloc"))]
impl<T> InPlaceInit<T> for Box<T> {
#[inline]
fn try_pin_init<E>(init: impl PinInit<T, E>) -> Result<Pin<Self>, E>
where
E: From<AllocError>,
{
Box::try_new_uninit()?.write_pin_init(init)
try_new_uninit!(Box).write_pin_init(init)
}

#[inline]
fn try_init<E>(init: impl Init<T, E>) -> Result<Self, E>
where
E: From<AllocError>,
{
Box::try_new_uninit()?.write_init(init)
try_new_uninit!(Box).write_init(init)
}
}

#[cfg(feature = "alloc")]
#[cfg(any(feature = "std", feature = "alloc"))]
impl<T> InPlaceInit<T> for Arc<T> {
#[inline]
fn try_pin_init<E>(init: impl PinInit<T, E>) -> Result<Pin<Self>, E>
where
E: From<AllocError>,
{
Arc::try_new_uninit()?.write_pin_init(init)
let mut this = try_new_uninit!(Arc);
let Some(slot) = Arc::get_mut(&mut this) else {
// SAFETY: the Arc has just been created and has no external referecnes
unsafe { core::hint::unreachable_unchecked() }
};
let slot = slot.as_mut_ptr();
// SAFETY: When init errors/panics, slot will get deallocated but not dropped,
// slot is valid and will not be moved, because we pin it later.
unsafe { init.__pinned_init(slot)? };
// SAFETY: All fields have been initialized and this is the only `Arc` to that data.
Ok(unsafe { Pin::new_unchecked(this.assume_init()) })
}

#[inline]
fn try_init<E>(init: impl Init<T, E>) -> Result<Self, E>
where
E: From<AllocError>,
{
Arc::try_new_uninit()?.write_init(init)
let mut this = try_new_uninit!(Arc);
let Some(slot) = Arc::get_mut(&mut this) else {
// SAFETY: the Arc has just been created and has no external referecnes
unsafe { core::hint::unreachable_unchecked() }
};
let slot = slot.as_mut_ptr();
// SAFETY: When init errors/panics, slot will get deallocated but not dropped,
// slot is valid.
unsafe { init.__init(slot)? };
// SAFETY: All fields have been initialized.
Ok(unsafe { this.assume_init() })
}
}

Expand All @@ -1253,7 +1291,7 @@ pub trait InPlaceWrite<T> {
fn write_pin_init<E>(self, init: impl PinInit<T, E>) -> Result<Pin<Self::Initialized>, E>;
}

#[cfg(feature = "alloc")]
#[cfg(any(feature = "std", feature = "alloc"))]
impl<T> InPlaceWrite<T> for Box<MaybeUninit<T>> {
type Initialized = Box<T>;

Expand All @@ -1276,31 +1314,6 @@ impl<T> InPlaceWrite<T> for Box<MaybeUninit<T>> {
}
}

#[cfg(feature = "alloc")]
impl<T> InPlaceWrite<T> for Arc<MaybeUninit<T>> {
type Initialized = Arc<T>;

fn write_init<E>(mut self, init: impl Init<T, E>) -> Result<Self::Initialized, E> {
let slot = unsafe { Arc::get_mut_unchecked(&mut self) };
let slot = slot.as_mut_ptr();
// SAFETY: When init errors/panics, slot will get deallocated but not dropped,
// slot is valid.
unsafe { init.__init(slot)? };
// SAFETY: All fields have been initialized.
Ok(unsafe { self.assume_init() })
}

fn write_pin_init<E>(mut self, init: impl PinInit<T, E>) -> Result<Pin<Self::Initialized>, E> {
let slot = unsafe { Arc::get_mut_unchecked(&mut self) };
let slot = slot.as_mut_ptr();
// SAFETY: When init errors/panics, slot will get deallocated but not dropped,
// slot is valid and will not be moved, because we pin it later.
unsafe { init.__pinned_init(slot)? };
// SAFETY: All fields have been initialized and this is the only `Arc` to that data.
Ok(unsafe { Pin::new_unchecked(self.assume_init()) })
}
}

/// Trait facilitating pinned destruction.
///
/// Use [`pinned_drop`] to implement this trait safely:
Expand Down Expand Up @@ -1409,7 +1422,7 @@ impl_zeroable! {
//
// In this case we are allowed to use `T: ?Sized`, since all zeros is the `None` variant.
{<T: ?Sized>} Option<NonNull<T>>,
#[cfg(feature = "alloc")]
#[cfg(any(feature = "std", feature = "alloc"))]
{<T: ?Sized>} Option<Box<T>>,

// SAFETY: `null` pointer is valid.
Expand Down
21 changes: 17 additions & 4 deletions tests/alloc_fail.rs
Original file line number Diff line number Diff line change
@@ -1,14 +1,22 @@
#![feature(allocator_api)]
#![cfg_attr(feature = "alloc", feature(allocator_api))]

use core::{alloc::AllocError, convert::Infallible};
#[cfg(feature = "alloc")]
use core::alloc::AllocError;

use core::convert::Infallible;
use pinned_init::*;
use std::sync::Arc;

#[path = "./ring_buf.rs"]
mod ring_buf;
use ring_buf::*;

#[cfg(all(not(miri), not(NO_ALLOC_FAIL_TESTS), not(target_os = "macos")))]
#[cfg(all(
feature = "alloc",
not(miri),
not(NO_ALLOC_FAIL_TESTS),
not(target_os = "macos")
))]
#[test]
fn too_big_pinned() {
// should be too big with current hardware.
Expand All @@ -23,7 +31,12 @@ fn too_big_pinned() {
));
}

#[cfg(all(not(miri), not(NO_ALLOC_FAIL_TESTS), not(target_os = "macos")))]
#[cfg(all(
feature = "alloc",
not(miri),
not(NO_ALLOC_FAIL_TESTS),
not(target_os = "macos")
))]
#[test]
fn too_big_in_place() {
// should be too big with current hardware.
Expand Down
Loading