tokio: enable the unsafe_op_in_unsafe_fn lint at the crate level (#7711)

Signed-off-by: ADD-SP <[email protected]>
This commit is contained in:
Qi
2025-11-09 12:35:08 +01:00
committed by GitHub
parent d4641ba9fc
commit 665f08b5ad
48 changed files with 384 additions and 149 deletions
+6 -2
View File
@@ -917,7 +917,9 @@ impl std::os::unix::io::AsFd for File {
#[cfg(unix)] #[cfg(unix)]
impl std::os::unix::io::FromRawFd for File { impl std::os::unix::io::FromRawFd for File {
unsafe fn from_raw_fd(fd: std::os::unix::io::RawFd) -> Self { unsafe fn from_raw_fd(fd: std::os::unix::io::RawFd) -> Self {
StdFile::from_raw_fd(fd).into() // Safety: exactly the same safety contract as
// `std::os::unix::io::FromRawFd::from_raw_fd`.
unsafe { StdFile::from_raw_fd(fd).into() }
} }
} }
@@ -942,7 +944,9 @@ cfg_windows! {
impl FromRawHandle for File { impl FromRawHandle for File {
unsafe fn from_raw_handle(handle: RawHandle) -> Self { unsafe fn from_raw_handle(handle: RawHandle) -> Self {
StdFile::from_raw_handle(handle).into() // Safety: exactly the same safety contract as
// `FromRawHandle::from_raw_handle`.
unsafe { StdFile::from_raw_handle(handle).into() }
} }
} }
} }
+2 -2
View File
@@ -171,7 +171,7 @@ feature! {
loop { loop {
let evt = ready!(self.registration.poll_read_ready(cx))?; let evt = ready!(self.registration.poll_read_ready(cx))?;
let b = &mut *(buf.unfilled_mut() as *mut [std::mem::MaybeUninit<u8>] as *mut [u8]); let b = unsafe { &mut *(buf.unfilled_mut() as *mut [std::mem::MaybeUninit<u8>] as *mut [u8]) };
// used only when the cfgs below apply // used only when the cfgs below apply
#[allow(unused_variables)] #[allow(unused_variables)]
@@ -213,7 +213,7 @@ feature! {
// Safety: We trust `TcpStream::read` to have filled up `n` bytes in the // Safety: We trust `TcpStream::read` to have filled up `n` bytes in the
// buffer. // buffer.
buf.assume_init(n); unsafe { buf.assume_init(n) };
buf.advance(n); buf.advance(n);
return Poll::Ready(Ok(())); return Poll::Ready(Ok(()));
}, },
+22 -4
View File
@@ -283,7 +283,9 @@ unsafe impl<'a> bytes::BufMut for ReadBuf<'a> {
// SAFETY: The caller guarantees that at least `cnt` unfilled bytes have been initialized. // SAFETY: The caller guarantees that at least `cnt` unfilled bytes have been initialized.
unsafe fn advance_mut(&mut self, cnt: usize) { unsafe fn advance_mut(&mut self, cnt: usize) {
self.assume_init(cnt); unsafe {
self.assume_init(cnt);
}
self.advance(cnt); self.advance(cnt);
} }
@@ -311,16 +313,32 @@ impl fmt::Debug for ReadBuf<'_> {
} }
} }
/// # Safety
///
/// The caller must ensure that `slice` is fully initialized
/// and never writes uninitialized bytes to the returned slice.
unsafe fn slice_to_uninit_mut(slice: &mut [u8]) -> &mut [MaybeUninit<u8>] { unsafe fn slice_to_uninit_mut(slice: &mut [u8]) -> &mut [MaybeUninit<u8>] {
&mut *(slice as *mut [u8] as *mut [MaybeUninit<u8>]) // SAFETY: `MaybeUninit<u8>` has the same memory layout as u8, and the caller
// promises to not write uninitialized bytes to the returned slice.
unsafe { &mut *(slice as *mut [u8] as *mut [MaybeUninit<u8>]) }
} }
/// # Safety
///
/// The caller must ensure that `slice` is fully initialized.
// TODO: This could use `MaybeUninit::slice_assume_init` when it is stable. // TODO: This could use `MaybeUninit::slice_assume_init` when it is stable.
unsafe fn slice_assume_init(slice: &[MaybeUninit<u8>]) -> &[u8] { unsafe fn slice_assume_init(slice: &[MaybeUninit<u8>]) -> &[u8] {
&*(slice as *const [MaybeUninit<u8>] as *const [u8]) // SAFETY: `MaybeUninit<u8>` has the same memory layout as u8, and the caller
// promises that `slice` is fully initialized.
unsafe { &*(slice as *const [MaybeUninit<u8>] as *const [u8]) }
} }
/// # Safety
///
/// The caller must ensure that `slice` is fully initialized.
// TODO: This could use `MaybeUninit::slice_assume_init_mut` when it is stable. // TODO: This could use `MaybeUninit::slice_assume_init_mut` when it is stable.
unsafe fn slice_assume_init_mut(slice: &mut [MaybeUninit<u8>]) -> &mut [u8] { unsafe fn slice_assume_init_mut(slice: &mut [MaybeUninit<u8>]) -> &mut [u8] {
&mut *(slice as *mut [MaybeUninit<u8>] as *mut [u8]) // SAFETY: `MaybeUninit<u8>` has the same memory layout as `u8`, and the caller
// promises that `slice` is fully initialized.
unsafe { &mut *(slice as *mut [MaybeUninit<u8>] as *mut [u8]) }
} }
+1 -1
View File
@@ -10,7 +10,7 @@
rust_2018_idioms, rust_2018_idioms,
unreachable_pub unreachable_pub
)] )]
#![deny(unused_must_use)] #![deny(unused_must_use, unsafe_op_in_unsafe_fn)]
#![doc(test( #![doc(test(
no_crate_inject, no_crate_inject,
attr(deny(warnings, rust_2018_idioms), allow(dead_code, unused_variables)) attr(deny(warnings, rust_2018_idioms), allow(dead_code, unused_variables))
+1 -1
View File
@@ -26,7 +26,7 @@ impl AtomicU16 {
/// All mutations must have happened before the unsynchronized load. /// All mutations must have happened before the unsynchronized load.
/// Additionally, there must be no concurrent mutations. /// Additionally, there must be no concurrent mutations.
pub(crate) unsafe fn unsync_load(&self) -> u16 { pub(crate) unsafe fn unsync_load(&self) -> u16 {
core::ptr::read(self.inner.get() as *const u16) unsafe { core::ptr::read(self.inner.get() as *const u16) }
} }
} }
+1 -1
View File
@@ -26,7 +26,7 @@ impl AtomicU32 {
/// All mutations must have happened before the unsynchronized load. /// All mutations must have happened before the unsynchronized load.
/// Additionally, there must be no concurrent mutations. /// Additionally, there must be no concurrent mutations.
pub(crate) unsafe fn unsync_load(&self) -> u32 { pub(crate) unsafe fn unsync_load(&self) -> u32 {
core::ptr::read(self.inner.get() as *const u32) unsafe { core::ptr::read(self.inner.get() as *const u32) }
} }
} }
+1 -1
View File
@@ -26,7 +26,7 @@ impl AtomicUsize {
/// All mutations must have happened before the unsynchronized load. /// All mutations must have happened before the unsynchronized load.
/// Additionally, there must be no concurrent mutations. /// Additionally, there must be no concurrent mutations.
pub(crate) unsafe fn unsync_load(&self) -> usize { pub(crate) unsafe fn unsync_load(&self) -> usize {
core::ptr::read(self.inner.get() as *const usize) unsafe { core::ptr::read(self.inner.get() as *const usize) }
} }
pub(crate) fn with_mut<R>(&mut self, f: impl FnOnce(&mut usize) -> R) -> R { pub(crate) fn with_mut<R>(&mut self, f: impl FnOnce(&mut usize) -> R) -> R {
+7 -2
View File
@@ -11,11 +11,16 @@ macro_rules! generate_addr_of_methods {
)*} )*}
) => { ) => {
impl<$($gen)*> $struct_name {$( impl<$($gen)*> $struct_name {$(
#[doc = "# Safety"]
#[doc = ""]
#[doc = "The `me` pointer must be valid."]
$(#[$attrs])* $(#[$attrs])*
$vis unsafe fn $fn_name(me: ::core::ptr::NonNull<Self>) -> ::core::ptr::NonNull<$field_type> { $vis unsafe fn $fn_name(me: ::core::ptr::NonNull<Self>) -> ::core::ptr::NonNull<$field_type> {
let me = me.as_ptr(); let me = me.as_ptr();
let field = ::std::ptr::addr_of_mut!((*me) $(.$field_name)+ ); // safety: the caller guarantees that `me` is valid
::core::ptr::NonNull::new_unchecked(field) let field = unsafe { ::std::ptr::addr_of_mut!((*me) $(.$field_name)+ ) };
// safety: the field pointer is never null
unsafe { ::core::ptr::NonNull::new_unchecked(field) }
} }
)*} )*}
}; };
+4 -2
View File
@@ -836,7 +836,9 @@ cfg_unix! {
/// The caller is responsible for ensuring that the socket is in /// The caller is responsible for ensuring that the socket is in
/// non-blocking mode. /// non-blocking mode.
unsafe fn from_raw_fd(fd: RawFd) -> TcpSocket { unsafe fn from_raw_fd(fd: RawFd) -> TcpSocket {
let inner = socket2::Socket::from_raw_fd(fd); // Safety: exactly the same safety requirements as the
// `FromRawFd::from_raw_fd` trait method.
let inner = unsafe { socket2::Socket::from_raw_fd(fd) };
TcpSocket { inner } TcpSocket { inner }
} }
} }
@@ -875,7 +877,7 @@ cfg_windows! {
/// The caller is responsible for ensuring that the socket is in /// The caller is responsible for ensuring that the socket is in
/// non-blocking mode. /// non-blocking mode.
unsafe fn from_raw_socket(socket: RawSocket) -> TcpSocket { unsafe fn from_raw_socket(socket: RawSocket) -> TcpSocket {
let inner = socket2::Socket::from_raw_socket(socket); let inner = unsafe { socket2::Socket::from_raw_socket(socket) };
TcpSocket { inner } TcpSocket { inner }
} }
} }
+3 -1
View File
@@ -259,7 +259,9 @@ impl AsFd for UnixSocket {
impl FromRawFd for UnixSocket { impl FromRawFd for UnixSocket {
unsafe fn from_raw_fd(fd: RawFd) -> UnixSocket { unsafe fn from_raw_fd(fd: RawFd) -> UnixSocket {
let inner = socket2::Socket::from_raw_fd(fd); // Safety: exactly the same safety requirements as the
// `FromRawFd::from_raw_fd` trait method.
let inner = unsafe { socket2::Socket::from_raw_fd(fd) };
UnixSocket { inner } UnixSocket { inner }
} }
} }
+39 -32
View File
@@ -126,7 +126,7 @@ impl NamedPipeServer {
/// [Tokio Runtime]: crate::runtime::Runtime /// [Tokio Runtime]: crate::runtime::Runtime
/// [enabled I/O]: crate::runtime::Builder::enable_io /// [enabled I/O]: crate::runtime::Builder::enable_io
pub unsafe fn from_raw_handle(handle: RawHandle) -> io::Result<Self> { pub unsafe fn from_raw_handle(handle: RawHandle) -> io::Result<Self> {
let named_pipe = mio_windows::NamedPipe::from_raw_handle(handle); let named_pipe = unsafe { mio_windows::NamedPipe::from_raw_handle(handle) };
Ok(Self { Ok(Self {
io: PollEvented::new(named_pipe)?, io: PollEvented::new(named_pipe)?,
@@ -999,7 +999,7 @@ impl NamedPipeClient {
/// [Tokio Runtime]: crate::runtime::Runtime /// [Tokio Runtime]: crate::runtime::Runtime
/// [enabled I/O]: crate::runtime::Builder::enable_io /// [enabled I/O]: crate::runtime::Builder::enable_io
pub unsafe fn from_raw_handle(handle: RawHandle) -> io::Result<Self> { pub unsafe fn from_raw_handle(handle: RawHandle) -> io::Result<Self> {
let named_pipe = mio_windows::NamedPipe::from_raw_handle(handle); let named_pipe = unsafe { mio_windows::NamedPipe::from_raw_handle(handle) };
Ok(Self { Ok(Self {
io: PollEvented::new(named_pipe)?, io: PollEvented::new(named_pipe)?,
@@ -2344,22 +2344,24 @@ impl ServerOptions {
mode mode
}; };
let h = windows_sys::CreateNamedPipeW( let h = unsafe {
addr.as_ptr(), windows_sys::CreateNamedPipeW(
open_mode, addr.as_ptr(),
pipe_mode, open_mode,
self.max_instances, pipe_mode,
self.out_buffer_size, self.max_instances,
self.in_buffer_size, self.out_buffer_size,
self.default_timeout, self.in_buffer_size,
attrs as *mut _, self.default_timeout,
); attrs as *mut _,
)
};
if h == windows_sys::INVALID_HANDLE_VALUE { if h == windows_sys::INVALID_HANDLE_VALUE {
return Err(io::Error::last_os_error()); return Err(io::Error::last_os_error());
} }
NamedPipeServer::from_raw_handle(h as _) unsafe { NamedPipeServer::from_raw_handle(h as _) }
} }
} }
@@ -2550,15 +2552,17 @@ impl ClientOptions {
// we have access to windows_sys it ultimately doesn't hurt to use // we have access to windows_sys it ultimately doesn't hurt to use
// `CreateFile` explicitly since it allows the use of our already // `CreateFile` explicitly since it allows the use of our already
// well-structured wide `addr` to pass into CreateFileW. // well-structured wide `addr` to pass into CreateFileW.
let h = windows_sys::CreateFileW( let h = unsafe {
addr.as_ptr(), windows_sys::CreateFileW(
desired_access, addr.as_ptr(),
0, desired_access,
attrs as *mut _, 0,
windows_sys::OPEN_EXISTING, attrs as *mut _,
self.get_flags(), windows_sys::OPEN_EXISTING,
null_mut(), self.get_flags(),
); null_mut(),
)
};
if h == windows_sys::INVALID_HANDLE_VALUE { if h == windows_sys::INVALID_HANDLE_VALUE {
return Err(io::Error::last_os_error()); return Err(io::Error::last_os_error());
@@ -2566,15 +2570,16 @@ impl ClientOptions {
if matches!(self.pipe_mode, PipeMode::Message) { if matches!(self.pipe_mode, PipeMode::Message) {
let mode = windows_sys::PIPE_READMODE_MESSAGE; let mode = windows_sys::PIPE_READMODE_MESSAGE;
let result = let result = unsafe {
windows_sys::SetNamedPipeHandleState(h, &mode, ptr::null_mut(), ptr::null_mut()); windows_sys::SetNamedPipeHandleState(h, &mode, ptr::null_mut(), ptr::null_mut())
};
if result == 0 { if result == 0 {
return Err(io::Error::last_os_error()); return Err(io::Error::last_os_error());
} }
} }
NamedPipeClient::from_raw_handle(h as _) unsafe { NamedPipeClient::from_raw_handle(h as _) }
} }
fn get_flags(&self) -> u32 { fn get_flags(&self) -> u32 {
@@ -2659,13 +2664,15 @@ unsafe fn named_pipe_info(handle: RawHandle) -> io::Result<PipeInfo> {
let mut in_buffer_size = 0; let mut in_buffer_size = 0;
let mut max_instances = 0; let mut max_instances = 0;
let result = windows_sys::GetNamedPipeInfo( let result = unsafe {
handle as _, windows_sys::GetNamedPipeInfo(
&mut flags, handle as _,
&mut out_buffer_size, &mut flags,
&mut in_buffer_size, &mut out_buffer_size,
&mut max_instances, &mut in_buffer_size,
); &mut max_instances,
)
};
if result == 0 { if result == 0 {
return Err(io::Error::last_os_error()); return Err(io::Error::last_os_error());
+1 -1
View File
@@ -750,7 +750,7 @@ impl Command {
where where
F: FnMut() -> io::Result<()> + Send + Sync + 'static, F: FnMut() -> io::Result<()> + Send + Sync + 'static,
{ {
self.std.pre_exec(f); unsafe { self.std.pre_exec(f) };
self self
} }
+1 -1
View File
@@ -162,7 +162,7 @@ impl Drop for Waiting {
} }
unsafe extern "system" fn callback(ptr: *mut std::ffi::c_void, _timer_fired: bool) { unsafe extern "system" fn callback(ptr: *mut std::ffi::c_void, _timer_fired: bool) {
let complete = &mut *(ptr as *mut Option<oneshot::Sender<()>>); let complete = unsafe { &mut *(ptr as *mut Option<oneshot::Sender<()>>) };
let _ = complete.take().unwrap().send(()); let _ = complete.take().unwrap().send(());
} }
+5 -1
View File
@@ -392,6 +392,10 @@ impl Handle {
#[track_caller] #[track_caller]
#[allow(dead_code)] #[allow(dead_code)]
/// # Safety
///
/// This must only be called in `LocalRuntime` if the runtime has been verified to be owned
/// by the current thread.
pub(crate) unsafe fn spawn_local_named<F>( pub(crate) unsafe fn spawn_local_named<F>(
&self, &self,
future: F, future: F,
@@ -412,7 +416,7 @@ impl Handle {
let future = super::task::trace::Trace::root(future); let future = super::task::trace::Trace::root(future);
#[cfg(all(tokio_unstable, feature = "tracing"))] #[cfg(all(tokio_unstable, feature = "tracing"))]
let future = crate::util::trace::task(future, "task", meta, id.as_u64()); let future = crate::util::trace::task(future, "task", meta, id.as_u64());
self.inner.spawn_local(future, id, meta.spawned_at) unsafe { self.inner.spawn_local(future, id, meta.spawned_at) }
} }
/// Returns the flavor of the current `Runtime`. /// Returns the flavor of the current `Runtime`.
+5 -2
View File
@@ -119,7 +119,8 @@ impl RegistrationSet {
let io = unsafe { NonNull::new_unchecked(Arc::as_ptr(io).cast_mut()) }; let io = unsafe { NonNull::new_unchecked(Arc::as_ptr(io).cast_mut()) };
super::EXPOSE_IO.unexpose_provenance(io.as_ptr()); super::EXPOSE_IO.unexpose_provenance(io.as_ptr());
let _ = synced.registrations.remove(io); // SAFETY: the caller guarantees that `io` is part of this list.
let _ = unsafe { synced.registrations.remove(io) };
} }
} }
@@ -141,6 +142,8 @@ unsafe impl linked_list::Link for Arc<ScheduledIo> {
unsafe fn pointers( unsafe fn pointers(
target: NonNull<Self::Target>, target: NonNull<Self::Target>,
) -> NonNull<linked_list::Pointers<ScheduledIo>> { ) -> NonNull<linked_list::Pointers<ScheduledIo>> {
NonNull::new_unchecked(target.as_ref().linked_list_pointers.get()) // safety: `target.as_ref().linked_list_pointers` is a `UnsafeCell` that
// always returns a non-null pointer.
unsafe { NonNull::new_unchecked(target.as_ref().linked_list_pointers.get()) }
} }
} }
+1 -1
View File
@@ -417,7 +417,7 @@ unsafe impl linked_list::Link for Waiter {
} }
unsafe fn pointers(target: NonNull<Waiter>) -> NonNull<linked_list::Pointers<Waiter>> { unsafe fn pointers(target: NonNull<Waiter>) -> NonNull<linked_list::Pointers<Waiter>> {
Waiter::addr_of_pointers(target) unsafe { Waiter::addr_of_pointers(target) }
} }
} }
+26 -6
View File
@@ -305,11 +305,15 @@ impl Inner {
Arc::into_raw(this) as *const () Arc::into_raw(this) as *const ()
} }
/// # Safety
///
/// The pointer must have been created by [`Self::into_raw`].
unsafe fn from_raw(ptr: *const ()) -> Arc<Inner> { unsafe fn from_raw(ptr: *const ()) -> Arc<Inner> {
Arc::from_raw(ptr as *const Inner) unsafe { Arc::from_raw(ptr as *const Inner) }
} }
} }
// TODO: Is this really a unsafe function?
unsafe fn unparker_to_raw_waker(unparker: Arc<Inner>) -> RawWaker { unsafe fn unparker_to_raw_waker(unparker: Arc<Inner>) -> RawWaker {
RawWaker::new( RawWaker::new(
Inner::into_raw(unparker), Inner::into_raw(unparker),
@@ -317,23 +321,39 @@ unsafe fn unparker_to_raw_waker(unparker: Arc<Inner>) -> RawWaker {
) )
} }
/// # Safety
///
/// The pointer must have been created by [`Inner::into_raw`].
unsafe fn clone(raw: *const ()) -> RawWaker { unsafe fn clone(raw: *const ()) -> RawWaker {
Arc::increment_strong_count(raw as *const Inner); unsafe {
unparker_to_raw_waker(Inner::from_raw(raw)) Arc::increment_strong_count(raw as *const Inner);
}
unsafe { unparker_to_raw_waker(Inner::from_raw(raw)) }
} }
/// # Safety
///
/// The pointer must have been created by [`Inner::into_raw`].
unsafe fn drop_waker(raw: *const ()) { unsafe fn drop_waker(raw: *const ()) {
drop(Inner::from_raw(raw)); drop(unsafe { Inner::from_raw(raw) });
} }
/// # Safety
///
/// The pointer must have been created by [`Inner::into_raw`].
unsafe fn wake(raw: *const ()) { unsafe fn wake(raw: *const ()) {
let unparker = Inner::from_raw(raw); let unparker = unsafe { Inner::from_raw(raw) };
unparker.unpark(); unparker.unpark();
} }
/// # Safety
///
/// The pointer must have been created by [`Inner::into_raw`].
unsafe fn wake_by_ref(raw: *const ()) { unsafe fn wake_by_ref(raw: *const ()) {
let raw = raw as *const Inner; let raw = raw as *const Inner;
(*raw).unpark(); unsafe {
(*raw).unpark();
}
} }
#[cfg(loom)] #[cfg(loom)]
@@ -474,6 +474,7 @@ impl Handle {
/// Spawn a task which isn't safe to send across thread boundaries onto the runtime. /// Spawn a task which isn't safe to send across thread boundaries onto the runtime.
/// ///
/// # Safety /// # Safety
///
/// This should only be used when this is a `LocalRuntime` or in another case where the runtime /// This should only be used when this is a `LocalRuntime` or in another case where the runtime
/// provably cannot be driven from or moved to different threads from the one on which the task /// provably cannot be driven from or moved to different threads from the one on which the task
/// is spawned. /// is spawned.
@@ -488,10 +489,12 @@ impl Handle {
F: crate::future::Future + 'static, F: crate::future::Future + 'static,
F::Output: 'static, F::Output: 'static,
{ {
let (handle, notified) = me // Safety: the caller guarantees that the this is only called on a `LocalRuntime`.
.shared let (handle, notified) = unsafe {
.owned me.shared
.bind_local(future, me.clone(), id, spawned_at); .owned
.bind_local(future, me.clone(), id, spawned_at)
};
me.task_hooks.spawn(&TaskMeta { me.task_hooks.spawn(&TaskMeta {
id, id,
@@ -55,13 +55,21 @@ impl<T: 'static> Shared<T> {
// Now that the tasks are linked together, insert them into the // Now that the tasks are linked together, insert them into the
// linked list. // linked list.
self.push_batch_inner(shared, first, prev, counter); //
// Safety: exactly the same safety requirements as `push_batch` method.
unsafe {
self.push_batch_inner(shared, first, prev, counter);
}
} }
/// Inserts several tasks that have been linked together into the queue. /// Inserts several tasks that have been linked together into the queue.
/// ///
/// The provided head and tail may be the same task. In this case, a /// The provided head and tail may be the same task. In this case, a
/// single task is inserted. /// single task is inserted.
///
/// # Safety
///
/// Must be called with the same `Synced` instance returned by `Inject::new`
#[inline] #[inline]
unsafe fn push_batch_inner<L>( unsafe fn push_batch_inner<L>(
&self, &self,
@@ -82,7 +90,8 @@ impl<T: 'static> Shared<T> {
let mut curr = Some(batch_head); let mut curr = Some(batch_head);
while let Some(task) = curr { while let Some(task) = curr {
curr = task.get_queue_next(); // Safety: exactly the same safety requirements as `push_batch_inner`.
curr = unsafe { task.get_queue_next() };
let _ = unsafe { task::Notified::<T>::from_raw(task) }; let _ = unsafe { task::Notified::<T>::from_raw(task) };
} }
@@ -106,7 +115,7 @@ impl<T: 'static> Shared<T> {
// //
// safety: All updates to the len atomic are guarded by the mutex. As // safety: All updates to the len atomic are guarded by the mutex. As
// such, a non-atomic load followed by a store is safe. // such, a non-atomic load followed by a store is safe.
let len = self.len.unsync_load(); let len = unsafe { self.len.unsync_load() };
self.len.store(len + num, Release); self.len.store(len + num, Release);
} }
+3 -3
View File
@@ -71,7 +71,7 @@ impl<T: 'static> Shared<T> {
} }
// safety: only mutated with the lock held // safety: only mutated with the lock held
let len = self.len.unsync_load(); let len = unsafe { self.len.unsync_load() };
let task = task.into_raw(); let task = task.into_raw();
// The next pointer should already be null // The next pointer should already be null
@@ -95,7 +95,7 @@ impl<T: 'static> Shared<T> {
/// ///
/// Must be called with the same `Synced` instance returned by `Inject::new` /// Must be called with the same `Synced` instance returned by `Inject::new`
pub(crate) unsafe fn pop(&self, synced: &mut Synced) -> Option<task::Notified<T>> { pub(crate) unsafe fn pop(&self, synced: &mut Synced) -> Option<task::Notified<T>> {
self.pop_n(synced, 1).next() unsafe { self.pop_n(synced, 1).next() }
} }
/// Pop `n` values from the queue /// Pop `n` values from the queue
@@ -110,7 +110,7 @@ impl<T: 'static> Shared<T> {
// safety: All updates to the len atomic are guarded by the mutex. As // safety: All updates to the len atomic are guarded by the mutex. As
// such, a non-atomic load followed by a store is safe. // such, a non-atomic load followed by a store is safe.
let len = self.len.unsync_load(); let len = unsafe { self.len.unsync_load() };
let n = cmp::min(n, len); let n = cmp::min(n, len);
// Decrement the count. // Decrement the count.
+3 -1
View File
@@ -133,6 +133,7 @@ cfg_rt! {
/// Spawn a local task /// Spawn a local task
/// ///
/// # Safety /// # Safety
///
/// This should only be called in `LocalRuntime` if the runtime has been verified to be owned /// This should only be called in `LocalRuntime` if the runtime has been verified to be owned
/// by the current thread. /// by the current thread.
#[allow(irrefutable_let_patterns)] #[allow(irrefutable_let_patterns)]
@@ -143,7 +144,8 @@ cfg_rt! {
F::Output: 'static, F::Output: 'static,
{ {
if let Handle::CurrentThread(h) = self { if let Handle::CurrentThread(h) = self {
current_thread::Handle::spawn_local(h, future, id, spawned_at) // Safety: caller guarantees that this is a `LocalRuntime`.
unsafe { current_thread::Handle::spawn_local(h, future, id, spawned_at) }
} else { } else {
panic!("Only current_thread and LocalSet have spawn_local internals implemented") panic!("Only current_thread and LocalSet have spawn_local internals implemented")
} }
+9
View File
@@ -9,6 +9,15 @@
//! Make sure to consult the relevant safety section of each function before //! Make sure to consult the relevant safety section of each function before
//! use. //! use.
// It doesn't make sense to enforce `unsafe_op_in_unsafe_fn` for this module because
//
// * This module is doing the low-level task management that requires tons of unsafe
// operations.
// * Excessive `unsafe {}` blocks hurt readability significantly.
// TODO: replace with `#[expect(unsafe_op_in_unsafe_fn)]` after bumpping
// the MSRV to 1.81.0.
#![allow(unsafe_op_in_unsafe_fn)]
use crate::future::Future; use crate::future::Future;
use crate::loom::cell::UnsafeCell; use crate::loom::cell::UnsafeCell;
use crate::runtime::context; use crate::runtime::context;
+1
View File
@@ -106,6 +106,7 @@ impl<S: 'static> OwnedTasks<S> {
/// Bind a task that isn't safe to transfer across thread boundaries. /// Bind a task that isn't safe to transfer across thread boundaries.
/// ///
/// # Safety /// # Safety
///
/// Only use this in `LocalRuntime` where the task cannot move /// Only use this in `LocalRuntime` where the task cannot move
pub(crate) unsafe fn bind_local<T>( pub(crate) unsafe fn bind_local<T>(
&self, &self,
+10 -4
View File
@@ -398,8 +398,11 @@ impl<S: 'static> Task<S> {
} }
} }
/// # Safety
///
/// `ptr` must be a valid pointer to a [`Header`].
unsafe fn from_raw(ptr: NonNull<Header>) -> Task<S> { unsafe fn from_raw(ptr: NonNull<Header>) -> Task<S> {
Task::new(RawTask::from_raw(ptr)) unsafe { Task::new(RawTask::from_raw(ptr)) }
} }
#[cfg(all( #[cfg(all(
@@ -479,8 +482,11 @@ impl<S: 'static> Notified<S> {
} }
impl<S: 'static> Notified<S> { impl<S: 'static> Notified<S> {
/// # Safety
///
/// [`RawTask::ptr`] must be a valid pointer to a [`Header`].
pub(crate) unsafe fn from_raw(ptr: RawTask) -> Notified<S> { pub(crate) unsafe fn from_raw(ptr: RawTask) -> Notified<S> {
Notified(Task::new(ptr)) Notified(unsafe { Task::new(ptr) })
} }
} }
@@ -597,11 +603,11 @@ unsafe impl<S> linked_list::Link for Task<S> {
} }
unsafe fn from_raw(ptr: NonNull<Header>) -> Task<S> { unsafe fn from_raw(ptr: NonNull<Header>) -> Task<S> {
Task::from_raw(ptr) unsafe { Task::from_raw(ptr) }
} }
unsafe fn pointers(target: NonNull<Header>) -> NonNull<linked_list::Pointers<Header>> { unsafe fn pointers(target: NonNull<Header>) -> NonNull<linked_list::Pointers<Header>> {
self::core::Trailer::addr_of_owned(Header::get_trailer(target)) unsafe { self::core::Trailer::addr_of_owned(Header::get_trailer(target)) }
} }
} }
+12
View File
@@ -1,3 +1,12 @@
// It doesn't make sense to enforce `unsafe_op_in_unsafe_fn` for this module because
//
// * This module is doing the low-level task management that requires tons of unsafe
// operations.
// * Excessive `unsafe {}` blocks hurt readability significantly.
// TODO: replace with `#[expect(unsafe_op_in_unsafe_fn)]` after bumpping
// the MSRV to 1.81.0.
#![allow(unsafe_op_in_unsafe_fn)]
use crate::future::Future; use crate::future::Future;
use crate::runtime::task::core::{Core, Trailer}; use crate::runtime::task::core::{Core, Trailer};
use crate::runtime::task::{Cell, Harness, Header, Id, Schedule, State}; use crate::runtime::task::{Cell, Harness, Header, Id, Schedule, State};
@@ -222,6 +231,9 @@ impl RawTask {
RawTask { ptr } RawTask { ptr }
} }
/// # Safety
///
/// `ptr` must be a valid pointer to a [`Header`].
pub(super) unsafe fn from_raw(ptr: NonNull<Header>) -> RawTask { pub(super) unsafe fn from_raw(ptr: NonNull<Header>) -> RawTask {
RawTask { ptr } RawTask { ptr }
} }
+8 -3
View File
@@ -82,14 +82,18 @@ impl Context {
where where
F: FnOnce(&Self) -> R, F: FnOnce(&Self) -> R,
{ {
crate::runtime::context::with_trace(f) unsafe { crate::runtime::context::with_trace(f) }
} }
/// SAFETY: Callers of this function must ensure that trace frames always
/// form a valid linked list.
unsafe fn with_current_frame<F, R>(f: F) -> R unsafe fn with_current_frame<F, R>(f: F) -> R
where where
F: FnOnce(&Cell<Option<NonNull<Frame>>>) -> R, F: FnOnce(&Cell<Option<NonNull<Frame>>>) -> R,
{ {
Self::try_with_current(|context| f(&context.active_frame)).expect(FAIL_NO_THREAD_LOCAL) unsafe {
Self::try_with_current(|context| f(&context.active_frame)).expect(FAIL_NO_THREAD_LOCAL)
}
} }
fn with_current_collector<F, R>(f: F) -> R fn with_current_collector<F, R>(f: F) -> R
@@ -313,7 +317,8 @@ cfg_rt_multi_thread! {
// clear the injection queue // clear the injection queue
let mut synced = synced.lock(); let mut synced = synced.lock();
while let Some(notified) = injection.pop(&mut synced.inject) { // Safety: exactly the same safety requirements as `trace_multi_thread` function.
while let Some(notified) = unsafe { injection.pop(&mut synced.inject) } {
dequeued.push(notified); dequeued.push(notified);
} }
+34 -12
View File
@@ -42,6 +42,9 @@ impl<S> ops::Deref for WakerRef<'_, S> {
} }
cfg_trace! { cfg_trace! {
/// # Safety
///
/// `$header` must be a valid pointer to a [`Header`].
macro_rules! trace { macro_rules! trace {
($header:expr, $op:expr) => { ($header:expr, $op:expr) => {
if let Some(id) = Header::get_tracing_id(&$header) { if let Some(id) = Header::get_tracing_id(&$header) {
@@ -65,31 +68,50 @@ cfg_not_trace! {
} }
unsafe fn clone_waker(ptr: *const ()) -> RawWaker { unsafe fn clone_waker(ptr: *const ()) -> RawWaker {
let header = NonNull::new_unchecked(ptr as *mut Header); // Safety: `ptr` was created from a `Header` pointer in function `waker_ref`.
trace!(header, "waker.clone"); let header = unsafe { NonNull::new_unchecked(ptr as *mut Header) };
header.as_ref().state.ref_inc(); #[cfg_attr(not(all(tokio_unstable, feature = "tracing")), allow(unused_unsafe))]
unsafe {
trace!(header, "waker.clone");
}
unsafe { header.as_ref() }.state.ref_inc();
raw_waker(header) raw_waker(header)
} }
unsafe fn drop_waker(ptr: *const ()) { unsafe fn drop_waker(ptr: *const ()) {
let ptr = NonNull::new_unchecked(ptr as *mut Header); // Safety: `ptr` was created from a `Header` pointer in function `waker_ref`.
trace!(ptr, "waker.drop"); let ptr = unsafe { NonNull::new_unchecked(ptr as *mut Header) };
let raw = RawTask::from_raw(ptr); // TODO; replace to #[expect(unused_unsafe)] after bumping MSRV to 1.81.0.
#[cfg_attr(not(all(tokio_unstable, feature = "tracing")), allow(unused_unsafe))]
unsafe {
trace!(ptr, "waker.drop");
}
let raw = unsafe { RawTask::from_raw(ptr) };
raw.drop_reference(); raw.drop_reference();
} }
unsafe fn wake_by_val(ptr: *const ()) { unsafe fn wake_by_val(ptr: *const ()) {
let ptr = NonNull::new_unchecked(ptr as *mut Header); // Safety: `ptr` was created from a `Header` pointer in function `waker_ref`.
trace!(ptr, "waker.wake"); let ptr = unsafe { NonNull::new_unchecked(ptr as *mut Header) };
let raw = RawTask::from_raw(ptr); // TODO; replace to #[expect(unused_unsafe)] after bumping MSRV to 1.81.0.
#[cfg_attr(not(all(tokio_unstable, feature = "tracing")), allow(unused_unsafe))]
unsafe {
trace!(ptr, "waker.wake");
}
let raw = unsafe { RawTask::from_raw(ptr) };
raw.wake_by_val(); raw.wake_by_val();
} }
// Wake without consuming the waker // Wake without consuming the waker
unsafe fn wake_by_ref(ptr: *const ()) { unsafe fn wake_by_ref(ptr: *const ()) {
let ptr = NonNull::new_unchecked(ptr as *mut Header); // Safety: `ptr` was created from a `Header` pointer in function `waker_ref`.
trace!(ptr, "waker.wake_by_ref"); let ptr = unsafe { NonNull::new_unchecked(ptr as *mut Header) };
let raw = RawTask::from_raw(ptr); // TODO; replace to #[expect(unused_unsafe)] after bumping MSRV to 1.81.0.
#[cfg_attr(not(all(tokio_unstable, feature = "tracing")), allow(unused_unsafe))]
unsafe {
trace!(ptr, "waker.wake_by_ref");
}
let raw = unsafe { RawTask::from_raw(ptr) };
raw.wake_by_ref(); raw.wake_by_ref();
} }
+12 -6
View File
@@ -471,7 +471,7 @@ unsafe impl linked_list::Link for TimerShared {
unsafe fn pointers( unsafe fn pointers(
target: NonNull<Self::Target>, target: NonNull<Self::Target>,
) -> NonNull<linked_list::Pointers<Self::Target>> { ) -> NonNull<linked_list::Pointers<Self::Target>> {
TimerShared::addr_of_pointers(target) unsafe { TimerShared::addr_of_pointers(target) }
} }
} }
@@ -644,7 +644,9 @@ impl TimerHandle {
/// SAFETY: The caller must ensure that the handle remains valid, the driver /// SAFETY: The caller must ensure that the handle remains valid, the driver
/// lock is held, and that the timer is not in any wheel linked lists. /// lock is held, and that the timer is not in any wheel linked lists.
pub(super) unsafe fn set_expiration(&self, tick: u64) { pub(super) unsafe fn set_expiration(&self, tick: u64) {
self.inner.as_ref().set_expiration(tick); unsafe {
self.inner.as_ref().set_expiration(tick);
}
} }
/// Attempts to mark this entry as pending. If the expiration time is after /// Attempts to mark this entry as pending. If the expiration time is after
@@ -657,14 +659,18 @@ impl TimerHandle {
/// lock is held, and that the timer is not in any wheel linked lists. /// lock is held, and that the timer is not in any wheel linked lists.
/// After returning Ok, the entry must be added to the pending list. /// After returning Ok, the entry must be added to the pending list.
pub(super) unsafe fn mark_pending(&self, not_after: u64) -> Result<(), u64> { pub(super) unsafe fn mark_pending(&self, not_after: u64) -> Result<(), u64> {
match self.inner.as_ref().state.mark_pending(not_after) { match unsafe { self.inner.as_ref().state.mark_pending(not_after) } {
Ok(()) => { Ok(()) => {
// mark this as being on the pending queue in registered_when // mark this as being on the pending queue in registered_when
self.inner.as_ref().set_registered_when(STATE_DEREGISTERED); unsafe {
self.inner.as_ref().set_registered_when(STATE_DEREGISTERED);
}
Ok(()) Ok(())
} }
Err(tick) => { Err(tick) => {
self.inner.as_ref().set_registered_when(tick); unsafe {
self.inner.as_ref().set_registered_when(tick);
}
Err(tick) Err(tick)
} }
} }
@@ -682,6 +688,6 @@ impl TimerHandle {
/// SAFETY: The driver lock must be held while invoking this function, and /// SAFETY: The driver lock must be held while invoking this function, and
/// the entry must not be in any wheel linked lists. /// the entry must not be in any wheel linked lists.
pub(super) unsafe fn fire(self, completed_state: TimerResult) -> Option<Waker> { pub(super) unsafe fn fire(self, completed_state: TimerResult) -> Option<Waker> {
self.inner.as_ref().state.fire(completed_state) unsafe { self.inner.as_ref().state.fire(completed_state) }
} }
} }
+1 -1
View File
@@ -120,7 +120,7 @@ impl Level {
} }
pub(crate) unsafe fn add_entry(&mut self, item: TimerHandle) { pub(crate) unsafe fn add_entry(&mut self, item: TimerHandle) {
let slot = slot_for(item.registered_when(), self.level); let slot = slot_for(unsafe { item.registered_when() }, self.level);
self.slot[slot].push_front(item); self.slot[slot].push_front(item);
+1 -1
View File
@@ -91,7 +91,7 @@ impl Wheel {
&mut self, &mut self,
item: TimerHandle, item: TimerHandle,
) -> Result<u64, (TimerHandle, InsertError)> { ) -> Result<u64, (TimerHandle, InsertError)> {
let when = item.sync_when(); let when = unsafe { item.sync_when() };
if when <= self.elapsed { if when <= self.elapsed {
return Err((item, InsertError::Elapsed)); return Err((item, InsertError::Elapsed));
+6 -3
View File
@@ -81,18 +81,21 @@ impl<T> ReusableBoxFuture<T> {
F: Future<Output = T> + Send + 'static, F: Future<Output = T> + Send + 'static,
{ {
// Drop the existing future, catching any panics. // Drop the existing future, catching any panics.
let result = panic::catch_unwind(AssertUnwindSafe(|| { let result = panic::catch_unwind(AssertUnwindSafe(|| unsafe {
ptr::drop_in_place(self.boxed.as_ptr()); ptr::drop_in_place(self.boxed.as_ptr());
})); }));
// Overwrite the future behind the pointer. This is safe because the // Overwrite the future behind the pointer. This is safe because the
// allocation was allocated with the same size and alignment as the type F. // allocation was allocated with the same size and alignment as the type F.
let self_ptr: *mut F = self.boxed.as_ptr() as *mut F; let self_ptr: *mut F = self.boxed.as_ptr() as *mut F;
ptr::write(self_ptr, future); // SAFETY: The pointer is valid and the layout is exactly same.
unsafe {
ptr::write(self_ptr, future);
}
// Update the vtable of self.boxed. The pointer is not null because we // Update the vtable of self.boxed. The pointer is not null because we
// just got it from self.boxed, which is not null. // just got it from self.boxed, which is not null.
self.boxed = NonNull::new_unchecked(self_ptr); self.boxed = unsafe { NonNull::new_unchecked(self_ptr) };
// If the old future's destructor panicked, resume unwinding. // If the old future's destructor panicked, resume unwinding.
match result { match result {
+2 -2
View File
@@ -157,9 +157,9 @@ mod tests {
if event_requires_infinite_sleep_in_handler(signum) { if event_requires_infinite_sleep_in_handler(signum) {
// Those events will enter an infinite loop in `handler`, so // Those events will enter an infinite loop in `handler`, so
// we need to run them on a separate thread // we need to run them on a separate thread
std::thread::spawn(move || super::handler(signum)); std::thread::spawn(move || unsafe { super::handler(signum) });
} else { } else {
super::handler(signum); unsafe { super::handler(signum) };
} }
} }
+1 -1
View File
@@ -775,6 +775,6 @@ unsafe impl linked_list::Link for Waiter {
} }
unsafe fn pointers(target: NonNull<Waiter>) -> NonNull<linked_list::Pointers<Waiter>> { unsafe fn pointers(target: NonNull<Waiter>) -> NonNull<linked_list::Pointers<Waiter>> {
Waiter::addr_of_pointers(target) unsafe { Waiter::addr_of_pointers(target) }
} }
} }
+1 -1
View File
@@ -1678,7 +1678,7 @@ unsafe impl linked_list::Link for Waiter {
} }
unsafe fn pointers(target: NonNull<Waiter>) -> NonNull<linked_list::Pointers<Waiter>> { unsafe fn pointers(target: NonNull<Waiter>) -> NonNull<linked_list::Pointers<Waiter>> {
Waiter::addr_of_pointers(target) unsafe { Waiter::addr_of_pointers(target) }
} }
} }
+23 -7
View File
@@ -163,9 +163,15 @@ impl<T> Block<T> {
} }
// Get the value // Get the value
let value = self.values[offset].with(|ptr| ptr::read(ptr)); //
// Safety:
//
// 1. The caller guarantees that there is no concurrent access to the slot.
// 2. The `UnsafeCell` always give us a valid pointer to the value.
let value = self.values[offset].with(|ptr| unsafe { ptr::read(ptr) });
Some(Read::Value(value.assume_init())) // Safety: the redy bit is set, so the value has been initialized.
Some(Read::Value(unsafe { value.assume_init() }))
} }
/// Returns true if *this* block has a value in the given slot. /// Returns true if *this* block has a value in the given slot.
@@ -197,7 +203,10 @@ impl<T> Block<T> {
let slot_offset = offset(slot_index); let slot_offset = offset(slot_index);
self.values[slot_offset].with_mut(|ptr| { self.values[slot_offset].with_mut(|ptr| {
ptr::write(ptr, MaybeUninit::new(value)); // Safety: the caller guarantees that there is no concurrent access to the slot
unsafe {
ptr::write(ptr, MaybeUninit::new(value));
}
}); });
// Release the value. After this point, the slot ref may no longer // Release the value. After this point, the slot ref may no longer
@@ -246,7 +255,11 @@ impl<T> Block<T> {
// tail_position is guaranteed to not access this block. // tail_position is guaranteed to not access this block.
self.header self.header
.observed_tail_position .observed_tail_position
.with_mut(|ptr| *ptr = tail_position); // Safety:
//
// 1. The caller guarantees unique access to the block.
// 2. The `UnsafeCell` always gives us a valid pointer.
.with_mut(|ptr| unsafe { *ptr = tail_position });
// Set the released bit, signalling to the receiver that it is safe to // Set the released bit, signalling to the receiver that it is safe to
// free the block's memory as soon as all slots **prior** to // free the block's memory as soon as all slots **prior** to
@@ -316,7 +329,9 @@ impl<T> Block<T> {
success: Ordering, success: Ordering,
failure: Ordering, failure: Ordering,
) -> Result<(), NonNull<Block<T>>> { ) -> Result<(), NonNull<Block<T>>> {
block.as_mut().header.start_index = self.header.start_index.wrapping_add(BLOCK_CAP); // Safety: caller guarantees that `block` is valid.
unsafe { block.as_mut() }.header.start_index =
self.header.start_index.wrapping_add(BLOCK_CAP);
let next_ptr = self let next_ptr = self
.header .header
@@ -428,8 +443,9 @@ impl<T> Values<T> {
if_loom! { if_loom! {
let p = _value.as_ptr() as *mut UnsafeCell<MaybeUninit<T>>; let p = _value.as_ptr() as *mut UnsafeCell<MaybeUninit<T>>;
for i in 0..BLOCK_CAP { for i in 0..BLOCK_CAP {
p.add(i) unsafe {
.write(UnsafeCell::new(MaybeUninit::uninit())); p.add(i).write(UnsafeCell::new(MaybeUninit::uninit()));
}
} }
} }
} }
+22 -7
View File
@@ -184,6 +184,13 @@ impl<T> Tx<T> {
} }
} }
/// # Safety
///
/// Behavior is undefined if any of the following conditions are violated:
///
/// - The `block` was created by [`Box::into_raw`].
/// - The `block` is not currently part of any linked list.
/// - The `block` is a valid pointer to a [`Block<T>`].
pub(crate) unsafe fn reclaim_block(&self, mut block: NonNull<Block<T>>) { pub(crate) unsafe fn reclaim_block(&self, mut block: NonNull<Block<T>>) {
// The block has been removed from the linked list and ownership // The block has been removed from the linked list and ownership
// is reclaimed. // is reclaimed.
@@ -192,24 +199,28 @@ impl<T> Tx<T> {
// inserting it back at the end of the linked list. // inserting it back at the end of the linked list.
// //
// First, reset the data // First, reset the data
block.as_mut().reclaim(); //
// Safety: caller guarantees the block is valid and not in any list.
unsafe {
block.as_mut().reclaim();
}
let mut reused = false; let mut reused = false;
// Attempt to insert the block at the end // Attempt to insert the block at the end
// //
// Walk at most three times // Walk at most three times
//
let curr_ptr = self.block_tail.load(Acquire); let curr_ptr = self.block_tail.load(Acquire);
// The pointer can never be null // The pointer can never be null
debug_assert!(!curr_ptr.is_null()); debug_assert!(!curr_ptr.is_null());
let mut curr = NonNull::new_unchecked(curr_ptr); // Safety: curr_ptr is never null.
let mut curr = unsafe { NonNull::new_unchecked(curr_ptr) };
// TODO: Unify this logic with Block::grow // TODO: Unify this logic with Block::grow
for _ in 0..3 { for _ in 0..3 {
match curr.as_ref().try_push(&mut block, AcqRel, Acquire) { match unsafe { curr.as_ref().try_push(&mut block, AcqRel, Acquire) } {
Ok(()) => { Ok(()) => {
reused = true; reused = true;
break; break;
@@ -221,7 +232,11 @@ impl<T> Tx<T> {
} }
if !reused { if !reused {
let _ = Box::from_raw(block.as_ptr()); // Safety:
//
// 1. Caller guarantees the block is valid and not in any list.
// 2. The block was created by `Box::into_raw`.
let _ = unsafe { Box::from_raw(block.as_ptr()) };
} }
} }
@@ -387,8 +402,8 @@ impl<T> Rx<T> {
} }
while let Some(block) = cur { while let Some(block) = cur {
cur = block.as_ref().load_next(Relaxed); cur = unsafe { block.as_ref() }.load_next(Relaxed);
drop(Box::from_raw(block.as_ptr())); drop(unsafe { Box::from_raw(block.as_ptr()) });
} }
} }
} }
+1 -1
View File
@@ -1385,7 +1385,7 @@ unsafe impl linked_list::Link for Waiter {
} }
unsafe fn pointers(target: NonNull<Waiter>) -> NonNull<linked_list::Pointers<Waiter>> { unsafe fn pointers(target: NonNull<Waiter>) -> NonNull<linked_list::Pointers<Waiter>> {
Waiter::addr_of_pointers(target) unsafe { Waiter::addr_of_pointers(target) }
} }
} }
+6 -2
View File
@@ -244,12 +244,16 @@ impl<T> OnceCell<T> {
// SAFETY: The OnceCell must not be empty. // SAFETY: The OnceCell must not be empty.
unsafe fn get_unchecked(&self) -> &T { unsafe fn get_unchecked(&self) -> &T {
&*self.value.with(|ptr| (*ptr).as_ptr()) unsafe { &*self.value.with(|ptr| (*ptr).as_ptr()) }
} }
// SAFETY: The OnceCell must not be empty. // SAFETY: The OnceCell must not be empty.
unsafe fn get_unchecked_mut(&mut self) -> &mut T { unsafe fn get_unchecked_mut(&mut self) -> &mut T {
&mut *self.value.with_mut(|ptr| (*ptr).as_mut_ptr()) // SAFETY:
//
// 1. The caller guarantees that the OnceCell is initialized.
// 2. The `&mut self` guarantees that there are no other references to the value.
unsafe { &mut *self.value.with_mut(|ptr| (*ptr).as_mut_ptr()) }
} }
fn set_value(&self, value: T, permit: SemaphorePermit<'_>) -> &T { fn set_value(&self, value: T, permit: SemaphorePermit<'_>) -> &T {
+29 -9
View File
@@ -404,31 +404,51 @@ struct Inner<T> {
struct Task(UnsafeCell<MaybeUninit<Waker>>); struct Task(UnsafeCell<MaybeUninit<Waker>>);
impl Task { impl Task {
/// # Safety
///
/// The caller must do the necessary synchronization to ensure that
/// the [`Self::0`] contains the valid [`Waker`] during the call.
unsafe fn will_wake(&self, cx: &mut Context<'_>) -> bool { unsafe fn will_wake(&self, cx: &mut Context<'_>) -> bool {
self.with_task(|w| w.will_wake(cx.waker())) unsafe { self.with_task(|w| w.will_wake(cx.waker())) }
} }
/// # Safety
///
/// The caller must do the necessary synchronization to ensure that
/// the [`Self::0`] contains the valid [`Waker`] during the call.
unsafe fn with_task<F, R>(&self, f: F) -> R unsafe fn with_task<F, R>(&self, f: F) -> R
where where
F: FnOnce(&Waker) -> R, F: FnOnce(&Waker) -> R,
{ {
self.0.with(|ptr| { self.0.with(|ptr| {
let waker: *const Waker = (*ptr).as_ptr(); let waker: *const Waker = unsafe { (*ptr).as_ptr() };
f(&*waker) f(unsafe { &*waker })
}) })
} }
/// # Safety
///
/// The caller must do the necessary synchronization to ensure that
/// the [`Self::0`] contains the valid [`Waker`] during the call.
unsafe fn drop_task(&self) { unsafe fn drop_task(&self) {
self.0.with_mut(|ptr| { self.0.with_mut(|ptr| {
let ptr: *mut Waker = (*ptr).as_mut_ptr(); let ptr: *mut Waker = unsafe { (*ptr).as_mut_ptr() };
ptr.drop_in_place(); unsafe {
ptr.drop_in_place();
}
}); });
} }
/// # Safety
///
/// The caller must do the necessary synchronization to ensure that
/// the [`Self::0`] contains the valid [`Waker`] during the call.
unsafe fn set_task(&self, cx: &mut Context<'_>) { unsafe fn set_task(&self, cx: &mut Context<'_>) {
self.0.with_mut(|ptr| { self.0.with_mut(|ptr| {
let ptr: *mut Waker = (*ptr).as_mut_ptr(); let ptr: *mut Waker = unsafe { (*ptr).as_mut_ptr() };
ptr.write(cx.waker().clone()); unsafe {
ptr.write(cx.waker().clone());
}
}); });
} }
} }
@@ -1377,7 +1397,7 @@ impl<T> Inner<T> {
/// If `VALUE_SENT` is not set, then only the sender may call this method; /// If `VALUE_SENT` is not set, then only the sender may call this method;
/// if it is set, then only the receiver may call this method. /// if it is set, then only the receiver may call this method.
unsafe fn consume_value(&self) -> Option<T> { unsafe fn consume_value(&self) -> Option<T> {
self.value.with_mut(|ptr| (*ptr).take()) self.value.with_mut(|ptr| unsafe { (*ptr).take() })
} }
/// Returns true if there is a value. This function does not check `state`. /// Returns true if there is a value. This function does not check `state`.
@@ -1390,7 +1410,7 @@ impl<T> Inner<T> {
/// If `VALUE_SENT` is not set, then only the sender may call this method; /// If `VALUE_SENT` is not set, then only the sender may call this method;
/// if it is set, then only the receiver may call this method. /// if it is set, then only the receiver may call this method.
unsafe fn has_value(&self) -> bool { unsafe fn has_value(&self) -> bool {
self.value.with(|ptr| (*ptr).is_some()) self.value.with(|ptr| unsafe { (*ptr).is_some() })
} }
} }
+1 -1
View File
@@ -255,7 +255,7 @@ impl<T> SetOnce<T> {
// SAFETY: The SetOnce must not be empty. // SAFETY: The SetOnce must not be empty.
unsafe fn get_unchecked(&self) -> &T { unsafe fn get_unchecked(&self) -> &T {
&*self.value.with(|ptr| (*ptr).as_ptr()) unsafe { &*self.value.with(|ptr| (*ptr).as_ptr()) }
} }
/// Returns a reference to the value currently stored in the `SetOnce`, or /// Returns a reference to the value currently stored in the `SetOnce`, or
+5 -3
View File
@@ -12,15 +12,17 @@ fn notify_clones_waker_before_lock() {
unsafe fn clone_w(data: *const ()) -> RawWaker { unsafe fn clone_w(data: *const ()) -> RawWaker {
let ptr = data as *const Notify; let ptr = data as *const Notify;
Arc::<Notify>::increment_strong_count(ptr); unsafe {
Arc::<Notify>::increment_strong_count(ptr);
}
// Or some other arbitrary code that shouldn't be executed while the // Or some other arbitrary code that shouldn't be executed while the
// Notify wait list is locked. // Notify wait list is locked.
(*ptr).notify_one(); unsafe { (*ptr).notify_one() };
RawWaker::new(data, VTABLE) RawWaker::new(data, VTABLE)
} }
unsafe fn drop_w(data: *const ()) { unsafe fn drop_w(data: *const ()) {
drop(Arc::<Notify>::from_raw(data as *const Notify)); drop(unsafe { Arc::<Notify>::from_raw(data as *const Notify) });
} }
unsafe fn wake(_data: *const ()) { unsafe fn wake(_data: *const ()) {
+18 -3
View File
@@ -1192,28 +1192,43 @@ impl task::Schedule for Arc<Shared> {
} }
impl LocalState { impl LocalState {
/// # Safety
///
/// This method must only be called from the thread who
/// has the same [`ThreadId`] as [`Self::owner`].
unsafe fn task_pop_front(&self) -> Option<task::Notified<Arc<Shared>>> { unsafe fn task_pop_front(&self) -> Option<task::Notified<Arc<Shared>>> {
// The caller ensures it is called from the same thread that owns // The caller ensures it is called from the same thread that owns
// the LocalSet. // the LocalSet.
self.assert_called_from_owner_thread(); self.assert_called_from_owner_thread();
self.local_queue.with_mut(|ptr| (*ptr).pop_front()) self.local_queue
.with_mut(|ptr| unsafe { (*ptr).pop_front() })
} }
/// # Safety
///
/// This method must only be called from the thread who
/// has the same [`ThreadId`] as [`Self::owner`].
unsafe fn task_push_back(&self, task: task::Notified<Arc<Shared>>) { unsafe fn task_push_back(&self, task: task::Notified<Arc<Shared>>) {
// The caller ensures it is called from the same thread that owns // The caller ensures it is called from the same thread that owns
// the LocalSet. // the LocalSet.
self.assert_called_from_owner_thread(); self.assert_called_from_owner_thread();
self.local_queue.with_mut(|ptr| (*ptr).push_back(task)); self.local_queue
.with_mut(|ptr| unsafe { (*ptr).push_back(task) });
} }
/// # Safety
///
/// This method must only be called from the thread who
/// has the same [`ThreadId`] as [`Self::owner`].
unsafe fn take_local_queue(&self) -> VecDeque<task::Notified<Arc<Shared>>> { unsafe fn take_local_queue(&self) -> VecDeque<task::Notified<Arc<Shared>>> {
// The caller ensures it is called from the same thread that owns // The caller ensures it is called from the same thread that owns
// the LocalSet. // the LocalSet.
self.assert_called_from_owner_thread(); self.assert_called_from_owner_thread();
self.local_queue.with_mut(|ptr| std::mem::take(&mut (*ptr))) self.local_queue
.with_mut(|ptr| std::mem::take(unsafe { &mut (*ptr) }))
} }
unsafe fn task_remove(&self, task: &Task<Arc<Shared>>) -> Option<Task<Arc<Shared>>> { unsafe fn task_remove(&self, task: &Task<Arc<Shared>>) -> Option<Task<Arc<Shared>>> {
+6 -3
View File
@@ -349,7 +349,10 @@ impl<T> IdleNotifiedSet<T> {
unsafe fn move_to_new_list<T>(from: &mut LinkedList<T>, to: &mut LinkedList<T>) { unsafe fn move_to_new_list<T>(from: &mut LinkedList<T>, to: &mut LinkedList<T>) {
while let Some(entry) = from.pop_back() { while let Some(entry) = from.pop_back() {
entry.my_list.with_mut(|ptr| { entry.my_list.with_mut(|ptr| {
*ptr = List::Neither; // Safety: pointer is accessed while holding the mutex.
unsafe {
*ptr = List::Neither;
}
}); });
to.push_front(entry); to.push_front(entry);
} }
@@ -479,13 +482,13 @@ unsafe impl<T> linked_list::Link for ListEntry<T> {
} }
unsafe fn from_raw(ptr: NonNull<ListEntry<T>>) -> Arc<ListEntry<T>> { unsafe fn from_raw(ptr: NonNull<ListEntry<T>>) -> Arc<ListEntry<T>> {
Arc::from_raw(ptr.as_ptr()) unsafe { Arc::from_raw(ptr.as_ptr()) }
} }
unsafe fn pointers( unsafe fn pointers(
target: NonNull<ListEntry<T>>, target: NonNull<ListEntry<T>>,
) -> NonNull<linked_list::Pointers<ListEntry<T>>> { ) -> NonNull<linked_list::Pointers<ListEntry<T>>> {
ListEntry::addr_of_pointers(target) unsafe { ListEntry::addr_of_pointers(target) }
} }
} }
+9
View File
@@ -1,4 +1,11 @@
#![cfg_attr(not(feature = "full"), allow(dead_code))] #![cfg_attr(not(feature = "full"), allow(dead_code))]
// It doesn't make sense to enforce `unsafe_op_in_unsafe_fn` for this module because
//
// * The intrusive linked list naturally relies on unsafe operations.
// * Excessive `unsafe {}` blocks hurt readability significantly.
// TODO: replace with `#[expect(unsafe_op_in_unsafe_fn)]` after bumpping
// the MSRV to 1.81.0.
#![allow(unsafe_op_in_unsafe_fn)]
//! An intrusive double linked list of data. //! An intrusive double linked list of data.
//! //!
@@ -64,6 +71,8 @@ pub(crate) unsafe trait Link {
/// stack as the argument. In particular, the method may not create an /// stack as the argument. In particular, the method may not create an
/// intermediate reference in the process of creating the resulting raw /// intermediate reference in the process of creating the resulting raw
/// pointer. /// pointer.
///
/// The `target` pointer must be valid.
unsafe fn pointers(target: NonNull<Self::Target>) -> NonNull<Pointers<Self::Target>>; unsafe fn pointers(target: NonNull<Self::Target>) -> NonNull<Pointers<Self::Target>>;
} }
+1 -1
View File
@@ -35,7 +35,7 @@ impl<T> RcCell<T> {
// not called recursively. Finally, this is the only place that can // not called recursively. Finally, this is the only place that can
// create mutable references to the inner Rc. This ensures that any // create mutable references to the inner Rc. This ensures that any
// mutable references created here are exclusive. // mutable references created here are exclusive.
self.inner.with_mut(|ptr| f(&mut *ptr)) self.inner.with_mut(|ptr| f(unsafe { &mut *ptr }))
} }
pub(crate) fn get(&self) -> Option<Rc<T>> { pub(crate) fn get(&self) -> Option<Rc<T>> {
+2 -1
View File
@@ -27,6 +27,7 @@ pub(crate) struct ShardedList<L, T> {
/// call to call. /// call to call.
pub(crate) unsafe trait ShardedListItem: Link { pub(crate) unsafe trait ShardedListItem: Link {
/// # Safety /// # Safety
///
/// The provided pointer must point at a valid list item. /// The provided pointer must point at a valid list item.
unsafe fn get_shard_id(target: NonNull<Self::Target>) -> usize; unsafe fn get_shard_id(target: NonNull<Self::Target>) -> usize;
} }
@@ -79,7 +80,7 @@ impl<L: ShardedListItem> ShardedList<L, L::Target> {
/// - `node` is not contained by any list, /// - `node` is not contained by any list,
/// - `node` is currently contained by some other `GuardedLinkedList`. /// - `node` is currently contained by some other `GuardedLinkedList`.
pub(crate) unsafe fn remove(&self, node: NonNull<L::Target>) -> Option<L::Handle> { pub(crate) unsafe fn remove(&self, node: NonNull<L::Target>) -> Option<L::Handle> {
let id = L::get_shard_id(node); let id = unsafe { L::get_shard_id(node) };
let mut lock = self.shard_inner(id); let mut lock = self.shard_inner(id);
// SAFETY: Since the shard id cannot change, it's not possible for this node // SAFETY: Since the shard id cannot change, it's not possible for this node
// to be in any other list of the same sharded list. // to be in any other list of the same sharded list.
+2 -1
View File
@@ -9,7 +9,8 @@ use std::{
pub(super) unsafe fn try_transmute<Src, Target: 'static>(x: Src) -> Result<Target, Src> { pub(super) unsafe fn try_transmute<Src, Target: 'static>(x: Src) -> Result<Target, Src> {
if nonstatic_typeid::<Src>() == TypeId::of::<Target>() { if nonstatic_typeid::<Src>() == TypeId::of::<Target>() {
let x = ManuallyDrop::new(x); let x = ManuallyDrop::new(x);
Ok(mem::transmute_copy::<Src, Target>(&x)) // SAFETY: we have checked that the types are the same.
Ok(unsafe { mem::transmute_copy::<Src, Target>(&x) })
} else { } else {
Err(x) Err(x)
} }
+10 -4
View File
@@ -51,22 +51,28 @@ fn waker_vtable<W: Wake>() -> &'static RawWakerVTable {
} }
unsafe fn clone_arc_raw<T: Wake>(data: *const ()) -> RawWaker { unsafe fn clone_arc_raw<T: Wake>(data: *const ()) -> RawWaker {
Arc::<T>::increment_strong_count(data as *const T); // Safety: `data` was created from an `Arc::as_ptr` in function `waker_ref`.
unsafe {
Arc::<T>::increment_strong_count(data as *const T);
}
RawWaker::new(data, waker_vtable::<T>()) RawWaker::new(data, waker_vtable::<T>())
} }
unsafe fn wake_arc_raw<T: Wake>(data: *const ()) { unsafe fn wake_arc_raw<T: Wake>(data: *const ()) {
let arc: Arc<T> = Arc::from_raw(data as *const T); // Safety: `data` was created from an `Arc::as_ptr` in function `waker_ref`.
let arc: Arc<T> = unsafe { Arc::from_raw(data as *const T) };
Wake::wake(arc); Wake::wake(arc);
} }
// used by `waker_ref` // used by `waker_ref`
unsafe fn wake_by_ref_arc_raw<T: Wake>(data: *const ()) { unsafe fn wake_by_ref_arc_raw<T: Wake>(data: *const ()) {
// Retain Arc, but don't touch refcount by wrapping in ManuallyDrop // Retain Arc, but don't touch refcount by wrapping in ManuallyDrop
let arc = ManuallyDrop::new(Arc::<T>::from_raw(data.cast())); // Safety: `data` was created from an `Arc::as_ptr` in function `waker_ref`.
let arc = ManuallyDrop::new(unsafe { Arc::<T>::from_raw(data.cast()) });
Wake::wake_by_ref(&arc); Wake::wake_by_ref(&arc);
} }
unsafe fn drop_arc_raw<T: Wake>(data: *const ()) { unsafe fn drop_arc_raw<T: Wake>(data: *const ()) {
drop(Arc::<T>::from_raw(data.cast())); // Safety: `data` was created from an `Arc::as_ptr` in function `waker_ref`.
drop(unsafe { Arc::<T>::from_raw(data.cast()) });
} }