nolife
nolife copied to clipboard
Add thread-safety where possible
@steffahn wrote:
With the new implementation, thread-safety shouldn't really be any problem - essentially adding the right manual
SendandSyncimplementations should be all that's necessary.1However, the API currently contains API for the erased
dyn Future<Output = Never>type, and with thread-safety implemented,dyn Future<Output = Never> + Sendwould become a viable alternative, too. (With relaxations forSync, a+ Syncwould never be useful, I believe.)This is both in the form of the current default
F = dyn Future<Output = Never>, and with thenew_erasedmethod that producesdyn Future, notdyn Future + Send.Solutions to this include
one could change the default to
F = dyn Future<…> + Sendor keep it as-is, or remove it and offer two type synonyms, sayDynScope<T> = BoxScope<T, dyn Future<Output = Never>>andDynScopeSend<T> = BoxScope<T, dyn Future<Output = Never> + Send>. (I don't like these names, they're just place-holders for now.)one could add an additional method so we have
new_erasedandnew_erased_sendproducing the respective erased types
- alternatively, there could be a single method to produce these different types, using a trait and type inference at the use-site
- alternatively and/or additionally, it's also possible to just create
Fromimplementations. These could offer all kinds of conversions, most notably fromF: TopScope<Output = Never>orF: TopScope<Output = Never, Future: Send>to the respective erasedBoxScopetypes. I have not yet checked how Rust’s coherence checks would like additional conversions, e.g. also fromBoxScope<T, F>to the erasedBoxScope<T, dyn Future<…>+…>types, and fromBoxScope<T, dyn Futuer<…> + Send>toBoxScope<T, dyn Future<…>>.Footnotes
- For
BoxScope<T, F>, theSendandSyncbound would in principle need to consider the two constituent typesFand (for<'a>)<T as Family<'a>>::Family. However,Syncmight be further relaxed (à laSyncWrapper), as the future (and with current API also the&mut <T as Family<'_>>::Familycannot be accessed unless through&mut selfAPI. ↩
This issue has the purpose of adding some kind of thread-safety to nolife, continuing the discussion initiated in #15
My take on thread-safety from an API perspective
- I don't think relaxing
Syncis very interesting, and it might force otherwise avoidable breakage in the future if we ever add an operation that gets the value from a shared reference. Interested parties can make use ofSyncWrapper(thank you for the link btw, I didn't know there was a crate for that) - I think that the general case is probably to have a scope that is
Send, and so the default dyn version should add+ Send + Syncto the bound. We could then add an additionalnew_local_dynconstructor (blanket name) to relax the additional bounds.
alternatively, there could be a single method to produce these different types, using a trait and type inference at the use-site
Something like:
pub fn new_dyn<B: ErasedBoxScope, S: TopScope<Family = T>>(scope: S) -> B
where
S::Future: 'static,
{
let this = mem::ManuallyDrop::new(BoxScope::new(scope));
ErasedBoxScope::new(this.0)
}
I feel like the additional trait might confuse users, same about the From implementations.
Current proposal I feel most comfortable with:
- Add the send and sync impl to
BoxScope - Change the default parameter for
BoxScopeto add theSend + Syncbounds on the erased future type - Add a
new_local_dynmethod that returns an erased BoxScope without the bounds on the erased future type
Implemented as a draft PR (#19), but I get a weird issue when trying to get the type from a function returning an impl Scope: the Send bound on Scope::Future is lost.
I'll try to look into it today or tomorrow ^^
Thank you, take your time[^1], I find the soundness proofs to be actually complicated
[^1]: (I myself have been somewhat slowed down by the early access release of a certain game :grinning: )