rust-lang / futures-rs

Zero-cost asynchronous programming in Rust
https://rust-lang.github.io/futures-rs/
Apache License 2.0
5.29k stars 607 forks source link

Different `waker_vtable` addresses for same type #2864

Open timotheyca opened 3 weeks ago

timotheyca commented 3 weeks ago
use futures::task::ArcWake;

struct Temp;

impl ArcWake for Temp {
    fn wake_by_ref(arc_self: &std::sync::Arc<Self>) {
        let _ = arc_self;
    }
}

fn main() {
    let temp = std::sync::Arc::new(Temp);
    let waker = futures::task::waker_ref(&temp);
    println!("{waker:?} {:?}", waker.clone());
    futures::executor::block_on(async {});
}

Sometimes (more often than not, in my experience), when running in --release mode, vtable addresses don't match:

WakerRef { waker: ManuallyDrop { value: Waker { data: 0x5654d35e5b90, vtable: 0x5654d2ad62b8 } }, _marker: PhantomData<&()> } Waker { data: 0x5654d35e5b90, vtable: 0x5654d2ad6308 }

Why this matters: this leads to AtomicWaker and alike to clone (or, worse, wake) on each poll because of how Waker::will_wake compares Wakers. Example: https://github.com/sdroege/async-tungstenite/issues/133

Is this a compiler bug? If yes, can we do something to mitigate it?

taiki-e commented 3 weeks ago

Same issue as https://github.com/rust-lang/futures-rs/pull/2829#issuecomment-1962863389 / https://github.com/rust-lang/rust/pull/121622#issuecomment-1975888092?

timotheyca commented 3 weeks ago

yes, seems to be the same issue

timotheyca commented 3 weeks ago

#[inline(always)] on clone_arc_raw has some positive effect on the situation sometimes (ref_wake_same passes?)

sdroege commented 3 weeks ago

Quoting myself from

[...] this sounds like a bug in the code doing the comparison. If multiple codegen units are involved, it's not guaranteed that the vtable is always the same for the same object.

That's why e.g. https://doc.rust-lang.org/stable/std/sync/struct.Arc.html#method.ptr_eq exists.