-
Notifications
You must be signed in to change notification settings - Fork 20
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
Unnecessary Box::pin() #201
Comments
TL;DR I have to admit I'm not too fond of the At first, this reminded me of an issue I had once (How to construct a struct with internal lifetime dependency?). Basically, values on stack move more than I thought they do. In case of While I was playing with the code, I unrolled I agree this is not high priority, in benchmark I quickly made |
In the worst case scenario, getting rid of |
From my understanding Unpin is not derived for generators because they have self references, therefore they can not be moved safely.
I actually want to keep the async blocks. I think that they are a major readability and ergonomics improvement (In the old futures version we didn't have those, and it was super difficult to write readable futures code). What I was wondering about is what is the way normal people are expected to use async blocks with things like the My current (poor) understanding of the Pinning thing is that I can work with a structure, but I have to pin it first somewhere. Currently we pin it to the heap using I'm almost sure there is another way. We just don't know about it yet. Maybe, for example, I can pin the structure to the stack and then work with it? I was wondering if you could check around to find if there is another way (Besides changing the async blocks). I agree with you that this is not an urgent issue, nor will it give us serious speed-ups. I'm just really curious about what is the right thing to do here. We were promised zero cost abstraction futures, but this is a strange case where I don't know how to do it. One place where I thought we might find an answer would be the sources of futures? |
Well, futures are now split between the compiler, std and futures-rs, so investigating it may take some effort. Before doing that, I'll try to search some more and try asking some people. |
Okay, here's another theory of mine. In this presentation (from ~14:20 to ~16:00), WithoutBoats stated the goal is to have "one heap allocation per future". After seeing this commit, I wouldn't be surprised if it turned out every But then, why is it possible to avoid Then again, I'm not really convinced. |
@pzmarzly: Thanks for the work you did to figure this issue out.
I could be wrong, but let me try to write what I understand about those topics. This means that you only allocate the "top level" future. If you have inside your
I think that One strong evidence that I have for this opinion this issue that we opened for A hint from futures-rsThere is some other interesting thing I noticed lately that might help with solving this issue. // ...
use std::pin::Pin;
use pin_utils::{unsafe_pinned, unsafe_unpinned};
/// Combinator that guarantees one [`Poll::Pending`] before polling its inner
/// future.
///
/// This is created by the
/// [`FutureTestExt::pending_once`](super::FutureTestExt::pending_once)
/// method.
#[derive(Debug, Clone)]
#[must_use = "futures do nothing unless you `.await` or poll them"]
pub struct PendingOnce<Fut: Future> {
future: Fut,
polled_before: bool,
}
impl<Fut: Future> PendingOnce<Fut> {
unsafe_pinned!(future: Fut);
unsafe_unpinned!(polled_before: bool);
pub(super) fn new(future: Fut) -> Self {
Self {
future,
polled_before: false,
}
}
} Note the let foo = Foo { /* ... */ };
pin_mut!(foo);
let _: Pin<&mut Foo> = foo; Maybe we can do something like this to pin things to the stack? Other directionsSome things that I think we can try:
|
I tried it on |
Cool!
I have to agree, it feels too good to be true. What I fear is that we are doing something unsound here, but we can not see it or experience it in our tests.
If you are working from master, running Note: In the new "atomic payments" design (PR #199) the integration tests are not yet working, so it's not very useful to test it there. One thing I think we can do from here is write a simplified self contained example of the problem code with |
There's a similar (but slightly different-looking) version of this in the
RFC itself; maybe it's better to use that to be safe:
https://github.com/rust-lang/rfcs/blob/master/text/2349-pin.md#stack-pinning-api-potential-future-extension
…On Sat, Jun 1, 2019, 12:26 real ***@***.***> wrote:
I tried it on pzmarzly/unsafe-pin, it compiles
Cool!
pin_mut code looks so simple that I'd be surprised if that's the answer.
I have to agree, it feels too good to be true. What I fear is that we are
doing something unsound here, but we can not see it or experience it in our
tests.
I tried it on pzmarzly/unsafe-pin, it compiles, but I haven't tested
whether it panics when connecting to relay/index.
If you are working from master, running cargo test should run all the
integration tests, which also checks connection to relay/index. If the
tests pass, it means that it probably doesn't crash. (Unfortunately, we
can't be sure that it is actually safe).
Note: In the new "atomic payments" design (PR #199
<#199>) the integration tests
are not yet working, so it's not very useful to test it there.
One thing I think we can do from here is write a simplified self contained
example of the problem code with Box::pin(), then write a simplified self
contained code with the pin_mut solution. Finally we can open an issue at
the futures github repository with the two self contained examples and ask
if we are doing the right thing.
—
You are receiving this because you are subscribed to this thread.
Reply to this email directly, view it on GitHub
<#201>,
or mute the thread
<https://github.com/notifications/unsubscribe-auth/AAX7HLNRHCNJ3ASTI7Q6PVTPYI6GBANCNFSM4HNJ5ANA>
.
|
If we go this way, I'd prefer to use let f = async { ... };
pin_mut!(f);
select! { f => ..., ... }; vs let f = async { ... };
let mut f = pinned(f);
let f = f.into_pin();
select! { f => ..., ... }; We could just copy that macro, though MIT license would require us to attribute its author. BTW in |
It seems there might be some issue with the drop guarantees:
https://doc.rust-lang.org/std/pin/index.html#drop-guarantee
which make the RFC version unsound. The problem is, that if you just pin
something and then "go on", nobody guarantees it'll be dropped (and not
forgotten), and once you return from the function the memory (on stack) is
reused. I'm not sure whether the macro version is sound.
A different, less ergonomic stack-pinning API is suggested here:
rust-lang/rfcs#2349 (comment)
A longer, somewhat relevant post is here (only started reading):
https://www.ralfj.de/blog/2018/04/10/safe-intrusive-collections-with-pinning.html
…On Sat, Jun 1, 2019, 18:17 Paweł Zmarzły ***@***.***> wrote:
If we go this way, I'd prefer to use pin_mut macro over RFC example
implementation. I find it more elegant and error-prone:
let f = async { ... };pin_mut!(f);select! { f => ..., ... };
vs
let f = async { ... };let mut f = pinned(f);let f = f.into_pin();select! { f => ..., ... };
We could just copy that macro, though MIT license would require us to
attribute its author.
BTW in pzmarzly/unsafe-pin I was modifying master, so this solution seems
to be okay.
—
You are receiving this because you commented.
Reply to this email directly, view it on GitHub
<#201>,
or mute the thread
<https://github.com/notifications/unsubscribe-auth/AAX7HLOTYIFXLVMLVM2MRODPYKHHNANCNFSM4HNJ5ANA>
.
|
The Async Book has chapter on pinning, which gives |
For some reason I didn't notice that conversation in this topic, I have no idea why. Sorry about that! I am also aware of some unsoundness issues with the stack pinning. I'm not yet sure what is the right direction here. I think we should leave this open and keep using |
Some places in the code base use
Box::pin(...)
only to create a future that satisfies Pin requirements. One example fromcomponents/channeler/src/connect_pool.rs
:Box::pin(...)
is a runtime tradeoff we make only to satisfy the compiler. Is there a way to write this code without usingBox::pin(...)
, and also without using unsafe tricks? It seems to me that we should be able to pin the future to the stack somehow, but I'm not sure if we are supposed to do this.This is not high priority, but I was curious about this problem for a while. There are other places in the code that have the same problem, you can probably find them by searching for "Box::pin". (Note that there are some legitimate uses of Box::pin around the code too).
@pzmarzly : Do you think you can check this out?
The text was updated successfully, but these errors were encountered: