Skip to content

Commit f62d9fb

Browse files
committed
Prevent unnecessary allocation in compat03as01 Waker
1 parent 0e058f7 commit f62d9fb

File tree

2 files changed

+42
-11
lines changed

2 files changed

+42
-11
lines changed

futures-util/src/compat/compat03as01.rs

+40-9
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,21 @@ use futures_01::{
44
StartSend as StartSend01, Stream as Stream01,
55
};
66
use futures_core::{
7-
task as task03, TryFuture as TryFuture03, TryStream as TryStream03,
7+
task::{
8+
self as task03,
9+
RawWaker,
10+
RawWakerVTable,
11+
},
12+
TryFuture as TryFuture03,
13+
TryStream as TryStream03,
814
};
915
use futures_sink::Sink as Sink03;
10-
use crate::task::ArcWake as ArcWake03;
11-
use std::{pin::Pin, sync::Arc};
16+
use crate::task::{ArcWake as ArcWake03, WakerRef};
17+
use std::{
18+
mem,
19+
pin::Pin,
20+
sync::Arc,
21+
};
1222

1323
/// Converts a futures 0.3 [`TryFuture`](futures_core::future::TryFuture),
1424
/// [`TryStream`](futures_core::stream::TryStream) or
@@ -108,19 +118,40 @@ where
108118
}
109119
}
110120

121+
#[derive(Clone)]
111122
struct Current(task01::Task);
112123

113124
impl Current {
114125
fn new() -> Current {
115126
Current(task01::current())
116127
}
117128

118-
fn as_waker(&self) -> task03::Waker {
119-
// For simplicity reasons wrap the Waker into an Arc.
120-
// We can optimize this again later on and reintroduce WakerLt<'a> which
121-
// derefs to Waker, and where cloning it through RawWakerVTable returns
122-
// an Arc version
123-
ArcWake03::into_waker(Arc::new(Current(self.0.clone())))
129+
fn as_waker(&self) -> WakerRef<'_> {
130+
unsafe fn ptr_to_current<'a>(ptr: *const ()) -> &'a Current {
131+
&*(ptr as *const Current)
132+
}
133+
fn current_to_ptr(current: &Current) -> *const () {
134+
current as *const Current as *const ()
135+
}
136+
137+
unsafe fn clone(ptr: *const ()) -> RawWaker {
138+
// Lazily create the `Arc` only when the waker is actually cloned.
139+
// FIXME: remove `transmute` when a `Waker` -> `RawWaker` conversion
140+
// function is landed in `core`.
141+
mem::transmute::<task03::Waker, RawWaker>(
142+
Arc::new(ptr_to_current(ptr).clone()).into_waker()
143+
)
144+
}
145+
unsafe fn drop(_: *const ()) {}
146+
unsafe fn wake(ptr: *const ()) {
147+
ptr_to_current(ptr).0.notify()
148+
}
149+
150+
let ptr = current_to_ptr(self);
151+
let vtable = &RawWakerVTable { clone, drop, wake };
152+
unsafe {
153+
WakerRef::new(task03::Waker::new_unchecked(RawWaker::new(ptr, vtable)))
154+
}
124155
}
125156
}
126157

futures-util/src/task/arc_wake.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -23,9 +23,9 @@ pub trait ArcWake {
2323
///
2424
/// If `wake()` is called on the returned `Waker`,
2525
/// the `wake()` function that is defined inside this trait will get called.
26-
fn into_waker(wake: Arc<Self>) -> Waker where Self: Sized
26+
fn into_waker(self: Arc<Self>) -> Waker where Self: Sized
2727
{
28-
let ptr = Arc::into_raw(wake) as *const();
28+
let ptr = Arc::into_raw(self) as *const();
2929

3030
unsafe {
3131
Waker::new_unchecked(RawWaker::new(ptr, waker_vtable!(Self)))

0 commit comments

Comments
 (0)