Pin ergonomics

Search for the phrase here.

1 Like

(NOT A CONTRIBUTION)

This depends on your use case; it makes sense that for OS primitives that need to be at a stable address, this is an extra initialization step that wouldn't necessarily be necessary if they could be constructed pinned (via something like emplacement). But for both self-referential objects and intrusive data structures, its preferable to separate being constructed from being pinned, because they have a pre-pinned null state that they're expected to go through before they get pinned.

You could do the same thing with OS semaphores by using Option instead of MaybeUninit to track whether it is init, and automatically init it when used. This would add a null check to every method, of course; I expect the cost of a null check to be dwarfed by the synchronization cost of using an OS provided semaphore, but your mileage may vary.

Semaphore is Sync and has &self methods, if 2 threads try to concurrently initialize a semaphore, bad things might happen. Logic to prevent this kind of race would be expensive and error prone even if possible, as you can't really use any synchronization primitives (except atomics), because you are building them.

It appears to me that @withoutboats' proposal permits pinned fields of a struct, which, together with an NRVO guarantee, would seem to directly address your example, e.g.

struct Semaphore {
  raw: pinned UnsafeCell<MaybeUninit<RawSemaphore>>,
}

impl Semaphore {
   const fn new() -> Semaphore {
     let mut pinned this = Semaphore { ... }
     unsafe { create_semaphore(this.raw.as_mut_ptr()) }
     this
   }
}
1 Like

My note about futures in Pin problem: they just have 1 method, that accepts Pin<&mut self>, it is a really simplified case for Pin. Also, while implementing futures you generally have state to indicate if future was or wasn't polled at least once, and if not do some initialization. It really works only because of Pin<&mut Self>

I can't really comment on pin + intrusive data structures, because my use cases allow to not use pin: I'm just mutably borrowing a node for 'a, inserting it, then removing at the end of 'a

I'm not sure how would you guarantee that Semaphore is not moved after creation in that case?

That's what I meant about "an NRVO guarantee". NRVO in this context means "named return value optimization", a term-of-art stolen from C++. Basically, the language would need to guarantee that whenever a pinned local is returned by move, it is actually originally constructed into the place its caller provided for it to be moved into, so no move occurs. This is difficult to guarantee for the general case, but a guarantee for pinned places should be easier, and erroring out if it's not possible should be acceptable (unlike in the general case).

I'm also imagining that a struct with a pinned field has to be in a pinned place, so whether or not this happens is visible from the annotations on the let (note the edit to my earlier post).

1 Like

That guarantee would sell this for me. Creating value immediately pinned with guaranteed address would be more then sufficient for my use cases instead of !Move types

This topic was automatically closed 90 days after the last reply. New replies are no longer allowed.