Skip to content

Commit 020de25

Browse files
authored
Unrolled build for rust-lang#121622
Rollup merge of rust-lang#121622 - dtolnay:wake, r=cuviper Preserve same vtable pointer when cloning raw waker, to fix Waker::will_wake Fixes rust-lang#121600. As `@jkarneges` identified in rust-lang#121600 (comment), the issue is two different const promotions produce two statics at different addresses, which may or may not later be deduplicated by the linker (in this case not). Prior to rust-lang#119863, the content of the statics was compared, and they were equal. After, the address of the statics are compared and they are not equal. It is documented that `will_wake` _"works on a best-effort basis, and may return false even when the Wakers would awaken the same task"_ so this PR fixes a quality-of-implementation issue, not a correctness issue.
2 parents 5119208 + db535ba commit 020de25

File tree

3 files changed

+49
-0
lines changed

3 files changed

+49
-0
lines changed

library/alloc/src/task.rs

+13
Original file line numberDiff line numberDiff line change
@@ -136,6 +136,15 @@ impl<W: Wake + Send + Sync + 'static> From<Arc<W>> for RawWaker {
136136
#[inline(always)]
137137
fn raw_waker<W: Wake + Send + Sync + 'static>(waker: Arc<W>) -> RawWaker {
138138
// Increment the reference count of the arc to clone it.
139+
//
140+
// The #[inline(always)] is to ensure that raw_waker and clone_waker are
141+
// always generated in the same code generation unit as one another, and
142+
// therefore that the structurally identical const-promoted RawWakerVTable
143+
// within both functions is deduplicated at LLVM IR code generation time.
144+
// This allows optimizing Waker::will_wake to a single pointer comparison of
145+
// the vtable pointers, rather than comparing all four function pointers
146+
// within the vtables.
147+
#[inline(always)]
139148
unsafe fn clone_waker<W: Wake + Send + Sync + 'static>(waker: *const ()) -> RawWaker {
140149
unsafe { Arc::increment_strong_count(waker as *const W) };
141150
RawWaker::new(
@@ -304,6 +313,10 @@ impl<W: LocalWake + 'static> From<Rc<W>> for RawWaker {
304313
#[inline(always)]
305314
fn local_raw_waker<W: LocalWake + 'static>(waker: Rc<W>) -> RawWaker {
306315
// Increment the reference count of the Rc to clone it.
316+
//
317+
// Refer to the comment on raw_waker's clone_waker regarding why this is
318+
// always inline.
319+
#[inline(always)]
307320
unsafe fn clone_waker<W: LocalWake + 'static>(waker: *const ()) -> RawWaker {
308321
unsafe { Rc::increment_strong_count(waker as *const W) };
309322
RawWaker::new(

library/alloc/tests/lib.rs

+2
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@
4141
#![feature(thin_box)]
4242
#![feature(strict_provenance)]
4343
#![feature(drain_keep_rest)]
44+
#![feature(local_waker)]
4445
#![allow(internal_features)]
4546
#![deny(fuzzy_provenance_casts)]
4647
#![deny(unsafe_op_in_unsafe_fn)]
@@ -62,6 +63,7 @@ mod rc;
6263
mod slice;
6364
mod str;
6465
mod string;
66+
mod task;
6567
mod thin_box;
6668
mod vec;
6769
mod vec_deque;

library/alloc/tests/task.rs

+34
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
use alloc::rc::Rc;
2+
use alloc::sync::Arc;
3+
use alloc::task::{LocalWake, Wake};
4+
use core::task::{LocalWaker, Waker};
5+
6+
#[test]
7+
fn test_waker_will_wake_clone() {
8+
struct NoopWaker;
9+
10+
impl Wake for NoopWaker {
11+
fn wake(self: Arc<Self>) {}
12+
}
13+
14+
let waker = Waker::from(Arc::new(NoopWaker));
15+
let clone = waker.clone();
16+
17+
assert!(waker.will_wake(&clone));
18+
assert!(clone.will_wake(&waker));
19+
}
20+
21+
#[test]
22+
fn test_local_waker_will_wake_clone() {
23+
struct NoopWaker;
24+
25+
impl LocalWake for NoopWaker {
26+
fn wake(self: Rc<Self>) {}
27+
}
28+
29+
let waker = LocalWaker::from(Rc::new(NoopWaker));
30+
let clone = waker.clone();
31+
32+
assert!(waker.will_wake(&clone));
33+
assert!(clone.will_wake(&waker));
34+
}

0 commit comments

Comments
 (0)