Skip to content

Commit 6f048ca

Browse files
authored
tokio: avoid temporary references in linked_list::Link impls (#4841)
1 parent 922fc91 commit 6f048ca

File tree

11 files changed

+129
-31
lines changed

11 files changed

+129
-31
lines changed

tokio/build.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,10 @@ fn main() {
77
if ac.probe_rustc_version(1, 59) {
88
autocfg::emit("tokio_const_thread_local")
99
}
10+
11+
if !ac.probe_rustc_version(1, 51) {
12+
autocfg::emit("tokio_no_addr_of")
13+
}
1014
}
1115

1216
Err(e) => {

tokio/src/io/driver/scheduled_io.rs

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,14 @@ cfg_io_readiness! {
6666
_p: PhantomPinned,
6767
}
6868

69+
generate_addr_of_methods! {
70+
impl<> Waiter {
71+
unsafe fn addr_of_pointers(self: NonNull<Self>) -> NonNull<linked_list::Pointers<Waiter>> {
72+
&self.pointers
73+
}
74+
}
75+
}
76+
6977
/// Future returned by `readiness()`.
7078
struct Readiness<'a> {
7179
scheduled_io: &'a ScheduledIo,
@@ -399,8 +407,8 @@ cfg_io_readiness! {
399407
ptr
400408
}
401409

402-
unsafe fn pointers(mut target: NonNull<Waiter>) -> NonNull<linked_list::Pointers<Waiter>> {
403-
NonNull::from(&mut target.as_mut().pointers)
410+
unsafe fn pointers(target: NonNull<Waiter>) -> NonNull<linked_list::Pointers<Waiter>> {
411+
Waiter::addr_of_pointers(target)
404412
}
405413
}
406414

tokio/src/macros/addr_of.rs

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
//! This module defines a macro that lets you go from a raw pointer to a struct
2+
//! to a raw pointer to a field of the struct.
3+
4+
#[cfg(not(tokio_no_addr_of))]
5+
macro_rules! generate_addr_of_methods {
6+
(
7+
impl<$($gen:ident)*> $struct_name:ty {$(
8+
$(#[$attrs:meta])*
9+
$vis:vis unsafe fn $fn_name:ident(self: NonNull<Self>) -> NonNull<$field_type:ty> {
10+
&self$(.$field_name:tt)+
11+
}
12+
)*}
13+
) => {
14+
impl<$($gen)*> $struct_name {$(
15+
$(#[$attrs])*
16+
$vis unsafe fn $fn_name(me: ::core::ptr::NonNull<Self>) -> ::core::ptr::NonNull<$field_type> {
17+
let me = me.as_ptr();
18+
let field = ::std::ptr::addr_of_mut!((*me) $(.$field_name)+ );
19+
::core::ptr::NonNull::new_unchecked(field)
20+
}
21+
)*}
22+
};
23+
}
24+
25+
// The `addr_of_mut!` macro is only available for MSRV at least 1.51.0. This
26+
// version of the macro uses a workaround for older versions of rustc.
27+
#[cfg(tokio_no_addr_of)]
28+
macro_rules! generate_addr_of_methods {
29+
(
30+
impl<$($gen:ident)*> $struct_name:ty {$(
31+
$(#[$attrs:meta])*
32+
$vis:vis unsafe fn $fn_name:ident(self: NonNull<Self>) -> NonNull<$field_type:ty> {
33+
&self$(.$field_name:tt)+
34+
}
35+
)*}
36+
) => {
37+
impl<$($gen)*> $struct_name {$(
38+
$(#[$attrs])*
39+
$vis unsafe fn $fn_name(me: ::core::ptr::NonNull<Self>) -> ::core::ptr::NonNull<$field_type> {
40+
let me = me.as_ptr();
41+
let me_u8 = me as *mut u8;
42+
43+
let field_offset = {
44+
let me_ref = &*me;
45+
let field_ref_u8 = (&me_ref $(.$field_name)+ ) as *const $field_type as *const u8;
46+
field_ref_u8.offset_from(me_u8)
47+
};
48+
49+
::core::ptr::NonNull::new_unchecked(me_u8.offset(field_offset).cast())
50+
}
51+
)*}
52+
};
53+
}

tokio/src/macros/mod.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,9 @@ mod ready;
1515
#[macro_use]
1616
mod thread_local;
1717

18+
#[macro_use]
19+
mod addr_of;
20+
1821
cfg_trace! {
1922
#[macro_use]
2023
mod trace;

tokio/src/runtime/task/core.rs

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ pub(crate) struct Header {
6060
/// Task state.
6161
pub(super) state: State,
6262

63-
pub(super) owned: UnsafeCell<linked_list::Pointers<Header>>,
63+
pub(super) owned: linked_list::Pointers<Header>,
6464

6565
/// Pointer to next task, used with the injection queue.
6666
pub(super) queue_next: UnsafeCell<Option<NonNull<Header>>>,
@@ -86,6 +86,14 @@ pub(crate) struct Header {
8686
pub(super) id: Option<tracing::Id>,
8787
}
8888

89+
generate_addr_of_methods! {
90+
impl<> Header {
91+
pub(super) unsafe fn addr_of_owned(self: NonNull<Self>) -> NonNull<linked_list::Pointers<Header>> {
92+
&self.owned
93+
}
94+
}
95+
}
96+
8997
unsafe impl Send for Header {}
9098
unsafe impl Sync for Header {}
9199

@@ -111,7 +119,7 @@ impl<T: Future, S: Schedule> Cell<T, S> {
111119
Box::new(Cell {
112120
header: Header {
113121
state,
114-
owned: UnsafeCell::new(linked_list::Pointers::new()),
122+
owned: linked_list::Pointers::new(),
115123
queue_next: UnsafeCell::new(None),
116124
vtable: raw::vtable::<T, S>(),
117125
owner_id: UnsafeCell::new(0),

tokio/src/runtime/task/mod.rs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -473,8 +473,7 @@ unsafe impl<S> linked_list::Link for Task<S> {
473473
}
474474

475475
unsafe fn pointers(target: NonNull<Header>) -> NonNull<linked_list::Pointers<Header>> {
476-
// Not super great as it avoids some of looms checking...
477-
NonNull::from(target.as_ref().owned.with_mut(|ptr| &mut *ptr))
476+
Header::addr_of_owned(target)
478477
}
479478
}
480479

tokio/src/sync/batch_semaphore.rs

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,14 @@ struct Waiter {
112112
_p: PhantomPinned,
113113
}
114114

115+
generate_addr_of_methods! {
116+
impl<> Waiter {
117+
unsafe fn addr_of_pointers(self: NonNull<Self>) -> NonNull<linked_list::Pointers<Waiter>> {
118+
&self.pointers
119+
}
120+
}
121+
}
122+
115123
impl Semaphore {
116124
/// The maximum number of permits which a semaphore can hold.
117125
///
@@ -704,12 +712,6 @@ impl std::error::Error for TryAcquireError {}
704712
///
705713
/// `Waiter` is forced to be !Unpin.
706714
unsafe impl linked_list::Link for Waiter {
707-
// XXX: ideally, we would be able to use `Pin` here, to enforce the
708-
// invariant that list entries may not move while in the list. However, we
709-
// can't do this currently, as using `Pin<&'a mut Waiter>` as the `Handle`
710-
// type would require `Semaphore` to be generic over a lifetime. We can't
711-
// use `Pin<*mut Waiter>`, as raw pointers are `Unpin` regardless of whether
712-
// or not they dereference to an `!Unpin` target.
713715
type Handle = NonNull<Waiter>;
714716
type Target = Waiter;
715717

@@ -721,7 +723,7 @@ unsafe impl linked_list::Link for Waiter {
721723
ptr
722724
}
723725

724-
unsafe fn pointers(mut target: NonNull<Waiter>) -> NonNull<linked_list::Pointers<Waiter>> {
725-
NonNull::from(&mut target.as_mut().pointers)
726+
unsafe fn pointers(target: NonNull<Waiter>) -> NonNull<linked_list::Pointers<Waiter>> {
727+
Waiter::addr_of_pointers(target)
726728
}
727729
}

tokio/src/sync/broadcast.rs

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -361,6 +361,14 @@ struct Waiter {
361361
_p: PhantomPinned,
362362
}
363363

364+
generate_addr_of_methods! {
365+
impl<> Waiter {
366+
unsafe fn addr_of_pointers(self: NonNull<Self>) -> NonNull<linked_list::Pointers<Waiter>> {
367+
&self.pointers
368+
}
369+
}
370+
}
371+
364372
struct RecvGuard<'a, T> {
365373
slot: RwLockReadGuard<'a, Slot<T>>,
366374
}
@@ -1140,8 +1148,8 @@ unsafe impl linked_list::Link for Waiter {
11401148
ptr
11411149
}
11421150

1143-
unsafe fn pointers(mut target: NonNull<Waiter>) -> NonNull<linked_list::Pointers<Waiter>> {
1144-
NonNull::from(&mut target.as_mut().pointers)
1151+
unsafe fn pointers(target: NonNull<Waiter>) -> NonNull<linked_list::Pointers<Waiter>> {
1152+
Waiter::addr_of_pointers(target)
11451153
}
11461154
}
11471155

tokio/src/sync/notify.rs

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -214,7 +214,6 @@ enum NotificationType {
214214
}
215215

216216
#[derive(Debug)]
217-
#[repr(C)] // required by `linked_list::Link` impl
218217
struct Waiter {
219218
/// Intrusive linked-list pointers.
220219
pointers: linked_list::Pointers<Waiter>,
@@ -229,6 +228,14 @@ struct Waiter {
229228
_p: PhantomPinned,
230229
}
231230

231+
generate_addr_of_methods! {
232+
impl<> Waiter {
233+
unsafe fn addr_of_pointers(self: NonNull<Self>) -> NonNull<linked_list::Pointers<Waiter>> {
234+
&self.pointers
235+
}
236+
}
237+
}
238+
232239
/// Future returned from [`Notify::notified()`].
233240
///
234241
/// This future is fused, so once it has completed, any future calls to poll
@@ -950,7 +957,7 @@ unsafe impl linked_list::Link for Waiter {
950957
}
951958

952959
unsafe fn pointers(target: NonNull<Waiter>) -> NonNull<linked_list::Pointers<Waiter>> {
953-
target.cast()
960+
Waiter::addr_of_pointers(target)
954961
}
955962
}
956963

tokio/src/time/driver/entry.rs

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -326,7 +326,7 @@ pub(super) type EntryList = crate::util::linked_list::LinkedList<TimerShared, Ti
326326
///
327327
/// Note that this structure is located inside the `TimerEntry` structure.
328328
#[derive(Debug)]
329-
#[repr(C)] // required by `link_list::Link` impl
329+
#[repr(C)]
330330
pub(crate) struct TimerShared {
331331
/// Data manipulated by the driver thread itself, only.
332332
driver_state: CachePadded<TimerSharedPadded>,
@@ -339,6 +339,14 @@ pub(crate) struct TimerShared {
339339
_p: PhantomPinned,
340340
}
341341

342+
generate_addr_of_methods! {
343+
impl<> TimerShared {
344+
unsafe fn addr_of_pointers(self: NonNull<Self>) -> NonNull<linked_list::Pointers<TimerShared>> {
345+
&self.driver_state.0.pointers
346+
}
347+
}
348+
}
349+
342350
impl TimerShared {
343351
pub(super) fn new() -> Self {
344352
Self {
@@ -421,7 +429,6 @@ impl TimerShared {
421429
/// padded. This contains the information that the driver thread accesses most
422430
/// frequently to minimize contention. In particular, we move it away from the
423431
/// waker, as the waker is updated on every poll.
424-
#[repr(C)] // required by `link_list::Link` impl
425432
struct TimerSharedPadded {
426433
/// A link within the doubly-linked list of timers on a particular level and
427434
/// slot. Valid only if state is equal to Registered.
@@ -476,7 +483,7 @@ unsafe impl linked_list::Link for TimerShared {
476483
unsafe fn pointers(
477484
target: NonNull<Self::Target>,
478485
) -> NonNull<linked_list::Pointers<Self::Target>> {
479-
target.cast()
486+
TimerShared::addr_of_pointers(target)
480487
}
481488
}
482489

tokio/src/util/idle_notified_set.rs

Lines changed: 9 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -88,10 +88,6 @@ enum List {
8888
/// move it out from this entry to prevent it from getting leaked. (Since the
8989
/// two linked lists are emptied in the destructor of `IdleNotifiedSet`, the
9090
/// value should not be leaked.)
91-
///
92-
/// This type is `#[repr(C)]` because its `linked_list::Link` implementation
93-
/// requires that `pointers` is the first field.
94-
#[repr(C)]
9591
struct ListEntry<T> {
9692
/// The linked list pointers of the list this entry is in.
9793
pointers: linked_list::Pointers<ListEntry<T>>,
@@ -105,6 +101,14 @@ struct ListEntry<T> {
105101
_pin: PhantomPinned,
106102
}
107103

104+
generate_addr_of_methods! {
105+
impl<T> ListEntry<T> {
106+
unsafe fn addr_of_pointers(self: NonNull<Self>) -> NonNull<linked_list::Pointers<ListEntry<T>>> {
107+
&self.pointers
108+
}
109+
}
110+
}
111+
108112
// With mutable access to the `IdleNotifiedSet`, you can get mutable access to
109113
// the values.
110114
unsafe impl<T: Send> Send for IdleNotifiedSet<T> {}
@@ -453,11 +457,6 @@ unsafe impl<T> linked_list::Link for ListEntry<T> {
453457
unsafe fn pointers(
454458
target: NonNull<ListEntry<T>>,
455459
) -> NonNull<linked_list::Pointers<ListEntry<T>>> {
456-
// Safety: The pointers struct is the first field and ListEntry is
457-
// `#[repr(C)]` so this cast is safe.
458-
//
459-
// We do this rather than doing a field access since `std::ptr::addr_of`
460-
// is too new for our MSRV.
461-
target.cast()
460+
ListEntry::addr_of_pointers(target)
462461
}
463462
}

0 commit comments

Comments
 (0)