PinCell use new_unchecked

pub struct PinCell<'s, T: ?Sized> {
    _marker: PhantomData<*mut &'s ()>,
   //  _marker: PhantomData<&'s ()>, is Ok?
    pub value: T,
}

impl<'s, T> PinCell<'s, T> {
    pub const fn new(value: T) -> Self {
        Self { _marker: PhantomData, value }
    }
}

impl<'s, T: ?Sized> PinCell<'s, T> {
    pub const fn pin(&'s mut self) -> Pin<&'s mut T> {
        unsafe { Pin::new_unchecked(&mut self.value) }
    }
}

use:

fn pin_in_stack<T>(v: T) {
    let mut p = PinCell::new(v);
    let s = p.pin();
}

move and return p will compile fail

I assume you’re asking whether this API would be sound. Note that even if it was sound, it would not really be necessary since we can build an even easier to use API in the form of the pin! macro. Yes, it’s a macro, but macros aren’t all that bad, and also, there is no known way to avoid the need for a macro, as far as I’m aware, so a macro is the only option anyway. Which makes post like yours somewhat interesting since the premise is likely thar you're trying to change that and demonstrate a way to avoid macros after all.

Note that you do need to study the safety requirements for Pin carefully though. And indeed this API is unsound under those requirements, since it allows a user to circumvent the drop guarantee of pinning by wrapping the PinCell in a ManuallyDrop. Rust Playground


For further discussion/information/context on the drop guarantee and why it might be useful, feel free to take a look at this semi-recent thread where a different user presented their own idea of a presumably-sound macro-free stack-pinning API that failed to be sound due to the drop guarantee.

4 Likes

So, is it feasible to replace scope with a macro similar to pin! ?

pub type NScope<'scope, 'env> = &'scope Scope<'scope, 'env>;

fn demo() {
    let p = &Scope{...} as NScope;
    //      --------------------- as scope!()

}

And impl Drop for Scope

For questions about API design unrelated to the standard library, deep free to move over to users.rust-lang.org, which would be the better place to ask/discuss those. :slight_smile:

Also try to make your questions a bit longer / explain more clearly what you are asking, including necessary context; e. g. in case of API design, what the API is there for, etc. That makes it easier to answer.

1 Like

I mean std::thread::scoped::scope

impl<'scope, 'env> Drop for Scope<'scope, 'env> {
    fn drop(&mut self) {
        while self.data.num_running_threads.load(Ordering::Acquire) != 0 {
            park();
        }
    }
}

pub struct WScope<'scope, 'env>(Scope<'scope, 'env>);

pub type NScope<'scope, 'env> = &'scope WScope<'scope, 'env>;

macro_rules! scope {
    () => {
        &WScope(Scope {
            data: Arc::new(ScopeData {
                num_running_threads: AtomicUsize::new(0),
                main_thread: current(),
                a_thread_panicked: AtomicBool::new(false),
            }),
            env: PhantomData,
            scope: PhantomData,
        }) as NScope
    }
}

fn demo() {
    // Now use std::thread::scoped::scope
    scope(|s| {
        // use s
    });


    let s = scope!();
    // use s
}

My English is not good, so I can only express my meaning in code.

The pinning guarantee is not sufficient for thread::scope. The reason is that while a pinned value is guaranteed to be dropped before it is invalidated (the memory is freed/reused), there's no guarantee that it actually gets dropped.

This is why there's no async equivalent to thread::scope. If you yield/await inbetween creating the scope! and dropping it, and that yield never resumes, then the threads are never joined even though the borrow lifetime they have access to is not prevented from expiring.

A closureless scope! macro is probably sound if only used in synchronous functions, but there's no way to enforce that.

I did this precisely because I wanted to use yield/wait in the scope。。。 Did I ignore anything?

Will Drop for Scope be triggered when the Generator is destroyed?

Yes, but there's no guarantee that it is.

async fn ouch(r: &mut [u8]) {
    let s = scope!();
    s.spawn(|| loop {
        *r.choose_mut(&mut thread_rng()).unwrap() = random();
    });
    loop {
        // yield from Future::poll
        future::pending::<()>().await;
    }
    unreachable!();
}

fn bang() {
    let mut data = Box::new([0; 1024]);
    let mut fut = Box::pin(ouch(&mut *data));
    fut.as_mut().now_or_never(); // poll once
    std::mem::forget(fut); // leak future
    drop(data); // drop data it borrowed
}

This compiles (playground). The spawned thread continues to access the data given to the async fn after its lifetime has expired, because the thread was never joined, because the future holding the thread handle was just leaked, not dropped.

If you only borrow data owned by the future, no issues result. But async fn can borrow data that they don't own; allowing such is one of the main points of async Rust.

Leaking things is sound and allowed in safe code. If something is pinned to a synchronous function's stack, you're guaranteed that it will be dropped before any lifetimes is captures expire. If something is pinned anywhere else, you're guaranteed that it will be dropped before it's deallocated, but if it's leaked and never deallocated, it can outlive the lifetimes that it captures, because it's unusable after that lifetime. (Unless you bypass that with unsafe, such as done with scope! allowing spawning threads with access to non-'static borrows terminated not by the lifetime but by the (safely but now unsoundly skippable) drop.)

No, forbidding leaking is not a viable strategy.

Thank you for your guidance, to this conclusion, I feel pity.

In my opinion, this macro allows flexible scope thread a lot.

Is it feasible to add unsafe function calls to the scope! macro so that users can ensure safe by themselves?

Is the Rust available unsafe macro :rofl:

Nope, rustc doesn't check macros for unsafety properly. For example, this compiles:

macro_rules! test{
    () => {{ let () = *(0 as *const _);}}
}

fn main() {
    test!()
}

Playground

But it can compile fail:

unsafe fn a() {}
macro_rules! test{
    () => {{a()}}
}

fn ss() {
    test!()
}

error[E0133]: call to unsafe function is unsafe and requires unsafe function or block
  --> src\main.rs:56:13
   |
56 |     () => {{a()}}
   |             ^^^ call to unsafe function
...
60 |     test!()
   |     ------- in this macro invocation
   |
   = note: consult the function's documentation for information on how to avoid undefined behavior
   = note: this error originates in the macro `test` (in Nightly builds, run with -Z macro-backtrace for more info)

What are the processing rules of rustc and why are the two paragraphs inconsistent?

I think it's a bug, I'm gonna go make an issue real quick.

fn aa() {
    let () = *(0 as *const ());
}

My IDE told me there was a problem, but my compiler passed...

The issue for reference.