mirror of
https://github.com/tokio-rs/tokio.git
synced 2026-08-18 00:00:09 +02:00
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:
@@ -917,7 +917,9 @@ impl std::os::unix::io::AsFd for File {
|
||||
#[cfg(unix)]
|
||||
impl std::os::unix::io::FromRawFd for File {
|
||||
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 {
|
||||
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() }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -171,7 +171,7 @@ feature! {
|
||||
loop {
|
||||
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
|
||||
#[allow(unused_variables)]
|
||||
@@ -213,7 +213,7 @@ feature! {
|
||||
|
||||
// Safety: We trust `TcpStream::read` to have filled up `n` bytes in the
|
||||
// buffer.
|
||||
buf.assume_init(n);
|
||||
unsafe { buf.assume_init(n) };
|
||||
buf.advance(n);
|
||||
return Poll::Ready(Ok(()));
|
||||
},
|
||||
|
||||
@@ -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.
|
||||
unsafe fn advance_mut(&mut self, cnt: usize) {
|
||||
self.assume_init(cnt);
|
||||
unsafe {
|
||||
self.assume_init(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>] {
|
||||
&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.
|
||||
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.
|
||||
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
@@ -10,7 +10,7 @@
|
||||
rust_2018_idioms,
|
||||
unreachable_pub
|
||||
)]
|
||||
#![deny(unused_must_use)]
|
||||
#![deny(unused_must_use, unsafe_op_in_unsafe_fn)]
|
||||
#![doc(test(
|
||||
no_crate_inject,
|
||||
attr(deny(warnings, rust_2018_idioms), allow(dead_code, unused_variables))
|
||||
|
||||
@@ -26,7 +26,7 @@ impl AtomicU16 {
|
||||
/// All mutations must have happened before the unsynchronized load.
|
||||
/// Additionally, there must be no concurrent mutations.
|
||||
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) }
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -26,7 +26,7 @@ impl AtomicU32 {
|
||||
/// All mutations must have happened before the unsynchronized load.
|
||||
/// Additionally, there must be no concurrent mutations.
|
||||
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) }
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -26,7 +26,7 @@ impl AtomicUsize {
|
||||
/// All mutations must have happened before the unsynchronized load.
|
||||
/// Additionally, there must be no concurrent mutations.
|
||||
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 {
|
||||
|
||||
@@ -11,11 +11,16 @@ macro_rules! generate_addr_of_methods {
|
||||
)*}
|
||||
) => {
|
||||
impl<$($gen)*> $struct_name {$(
|
||||
#[doc = "# Safety"]
|
||||
#[doc = ""]
|
||||
#[doc = "The `me` pointer must be valid."]
|
||||
$(#[$attrs])*
|
||||
$vis unsafe fn $fn_name(me: ::core::ptr::NonNull<Self>) -> ::core::ptr::NonNull<$field_type> {
|
||||
let me = me.as_ptr();
|
||||
let field = ::std::ptr::addr_of_mut!((*me) $(.$field_name)+ );
|
||||
::core::ptr::NonNull::new_unchecked(field)
|
||||
// safety: the caller guarantees that `me` is valid
|
||||
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) }
|
||||
}
|
||||
)*}
|
||||
};
|
||||
|
||||
@@ -836,7 +836,9 @@ cfg_unix! {
|
||||
/// The caller is responsible for ensuring that the socket is in
|
||||
/// non-blocking mode.
|
||||
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 }
|
||||
}
|
||||
}
|
||||
@@ -875,7 +877,7 @@ cfg_windows! {
|
||||
/// The caller is responsible for ensuring that the socket is in
|
||||
/// non-blocking mode.
|
||||
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 }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -259,7 +259,9 @@ impl AsFd for UnixSocket {
|
||||
|
||||
impl FromRawFd for 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 }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -126,7 +126,7 @@ impl NamedPipeServer {
|
||||
/// [Tokio Runtime]: crate::runtime::Runtime
|
||||
/// [enabled I/O]: crate::runtime::Builder::enable_io
|
||||
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 {
|
||||
io: PollEvented::new(named_pipe)?,
|
||||
@@ -999,7 +999,7 @@ impl NamedPipeClient {
|
||||
/// [Tokio Runtime]: crate::runtime::Runtime
|
||||
/// [enabled I/O]: crate::runtime::Builder::enable_io
|
||||
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 {
|
||||
io: PollEvented::new(named_pipe)?,
|
||||
@@ -2344,22 +2344,24 @@ impl ServerOptions {
|
||||
mode
|
||||
};
|
||||
|
||||
let h = windows_sys::CreateNamedPipeW(
|
||||
addr.as_ptr(),
|
||||
open_mode,
|
||||
pipe_mode,
|
||||
self.max_instances,
|
||||
self.out_buffer_size,
|
||||
self.in_buffer_size,
|
||||
self.default_timeout,
|
||||
attrs as *mut _,
|
||||
);
|
||||
let h = unsafe {
|
||||
windows_sys::CreateNamedPipeW(
|
||||
addr.as_ptr(),
|
||||
open_mode,
|
||||
pipe_mode,
|
||||
self.max_instances,
|
||||
self.out_buffer_size,
|
||||
self.in_buffer_size,
|
||||
self.default_timeout,
|
||||
attrs as *mut _,
|
||||
)
|
||||
};
|
||||
|
||||
if h == windows_sys::INVALID_HANDLE_VALUE {
|
||||
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
|
||||
// `CreateFile` explicitly since it allows the use of our already
|
||||
// well-structured wide `addr` to pass into CreateFileW.
|
||||
let h = windows_sys::CreateFileW(
|
||||
addr.as_ptr(),
|
||||
desired_access,
|
||||
0,
|
||||
attrs as *mut _,
|
||||
windows_sys::OPEN_EXISTING,
|
||||
self.get_flags(),
|
||||
null_mut(),
|
||||
);
|
||||
let h = unsafe {
|
||||
windows_sys::CreateFileW(
|
||||
addr.as_ptr(),
|
||||
desired_access,
|
||||
0,
|
||||
attrs as *mut _,
|
||||
windows_sys::OPEN_EXISTING,
|
||||
self.get_flags(),
|
||||
null_mut(),
|
||||
)
|
||||
};
|
||||
|
||||
if h == windows_sys::INVALID_HANDLE_VALUE {
|
||||
return Err(io::Error::last_os_error());
|
||||
@@ -2566,15 +2570,16 @@ impl ClientOptions {
|
||||
|
||||
if matches!(self.pipe_mode, PipeMode::Message) {
|
||||
let mode = windows_sys::PIPE_READMODE_MESSAGE;
|
||||
let result =
|
||||
windows_sys::SetNamedPipeHandleState(h, &mode, ptr::null_mut(), ptr::null_mut());
|
||||
let result = unsafe {
|
||||
windows_sys::SetNamedPipeHandleState(h, &mode, ptr::null_mut(), ptr::null_mut())
|
||||
};
|
||||
|
||||
if result == 0 {
|
||||
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 {
|
||||
@@ -2659,13 +2664,15 @@ unsafe fn named_pipe_info(handle: RawHandle) -> io::Result<PipeInfo> {
|
||||
let mut in_buffer_size = 0;
|
||||
let mut max_instances = 0;
|
||||
|
||||
let result = windows_sys::GetNamedPipeInfo(
|
||||
handle as _,
|
||||
&mut flags,
|
||||
&mut out_buffer_size,
|
||||
&mut in_buffer_size,
|
||||
&mut max_instances,
|
||||
);
|
||||
let result = unsafe {
|
||||
windows_sys::GetNamedPipeInfo(
|
||||
handle as _,
|
||||
&mut flags,
|
||||
&mut out_buffer_size,
|
||||
&mut in_buffer_size,
|
||||
&mut max_instances,
|
||||
)
|
||||
};
|
||||
|
||||
if result == 0 {
|
||||
return Err(io::Error::last_os_error());
|
||||
|
||||
@@ -750,7 +750,7 @@ impl Command {
|
||||
where
|
||||
F: FnMut() -> io::Result<()> + Send + Sync + 'static,
|
||||
{
|
||||
self.std.pre_exec(f);
|
||||
unsafe { self.std.pre_exec(f) };
|
||||
self
|
||||
}
|
||||
|
||||
|
||||
@@ -162,7 +162,7 @@ impl Drop for Waiting {
|
||||
}
|
||||
|
||||
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(());
|
||||
}
|
||||
|
||||
|
||||
@@ -392,6 +392,10 @@ impl Handle {
|
||||
|
||||
#[track_caller]
|
||||
#[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>(
|
||||
&self,
|
||||
future: F,
|
||||
@@ -412,7 +416,7 @@ impl Handle {
|
||||
let future = super::task::trace::Trace::root(future);
|
||||
#[cfg(all(tokio_unstable, feature = "tracing"))]
|
||||
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`.
|
||||
|
||||
@@ -119,7 +119,8 @@ impl RegistrationSet {
|
||||
let io = unsafe { NonNull::new_unchecked(Arc::as_ptr(io).cast_mut()) };
|
||||
|
||||
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(
|
||||
target: NonNull<Self::Target>,
|
||||
) -> 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()) }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -417,7 +417,7 @@ unsafe impl linked_list::Link for Waiter {
|
||||
}
|
||||
|
||||
unsafe fn pointers(target: NonNull<Waiter>) -> NonNull<linked_list::Pointers<Waiter>> {
|
||||
Waiter::addr_of_pointers(target)
|
||||
unsafe { Waiter::addr_of_pointers(target) }
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -305,11 +305,15 @@ impl Inner {
|
||||
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> {
|
||||
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 {
|
||||
RawWaker::new(
|
||||
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 {
|
||||
Arc::increment_strong_count(raw as *const Inner);
|
||||
unparker_to_raw_waker(Inner::from_raw(raw))
|
||||
unsafe {
|
||||
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 ()) {
|
||||
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 ()) {
|
||||
let unparker = Inner::from_raw(raw);
|
||||
let unparker = unsafe { Inner::from_raw(raw) };
|
||||
unparker.unpark();
|
||||
}
|
||||
|
||||
/// # Safety
|
||||
///
|
||||
/// The pointer must have been created by [`Inner::into_raw`].
|
||||
unsafe fn wake_by_ref(raw: *const ()) {
|
||||
let raw = raw as *const Inner;
|
||||
(*raw).unpark();
|
||||
unsafe {
|
||||
(*raw).unpark();
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(loom)]
|
||||
|
||||
@@ -474,6 +474,7 @@ impl Handle {
|
||||
/// Spawn a task which isn't safe to send across thread boundaries onto the runtime.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// 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
|
||||
/// is spawned.
|
||||
@@ -488,10 +489,12 @@ impl Handle {
|
||||
F: crate::future::Future + 'static,
|
||||
F::Output: 'static,
|
||||
{
|
||||
let (handle, notified) = me
|
||||
.shared
|
||||
.owned
|
||||
.bind_local(future, me.clone(), id, spawned_at);
|
||||
// Safety: the caller guarantees that the this is only called on a `LocalRuntime`.
|
||||
let (handle, notified) = unsafe {
|
||||
me.shared
|
||||
.owned
|
||||
.bind_local(future, me.clone(), id, spawned_at)
|
||||
};
|
||||
|
||||
me.task_hooks.spawn(&TaskMeta {
|
||||
id,
|
||||
|
||||
@@ -55,13 +55,21 @@ impl<T: 'static> Shared<T> {
|
||||
|
||||
// Now that the tasks are linked together, insert them into the
|
||||
// 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.
|
||||
///
|
||||
/// The provided head and tail may be the same task. In this case, a
|
||||
/// single task is inserted.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// Must be called with the same `Synced` instance returned by `Inject::new`
|
||||
#[inline]
|
||||
unsafe fn push_batch_inner<L>(
|
||||
&self,
|
||||
@@ -82,7 +90,8 @@ impl<T: 'static> Shared<T> {
|
||||
let mut curr = Some(batch_head);
|
||||
|
||||
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) };
|
||||
}
|
||||
@@ -106,7 +115,7 @@ impl<T: 'static> Shared<T> {
|
||||
//
|
||||
// safety: All updates to the len atomic are guarded by the mutex. As
|
||||
// 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);
|
||||
}
|
||||
|
||||
@@ -71,7 +71,7 @@ impl<T: 'static> Shared<T> {
|
||||
}
|
||||
|
||||
// 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();
|
||||
|
||||
// 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`
|
||||
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
|
||||
@@ -110,7 +110,7 @@ impl<T: 'static> Shared<T> {
|
||||
|
||||
// safety: All updates to the len atomic are guarded by the mutex. As
|
||||
// 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);
|
||||
|
||||
// Decrement the count.
|
||||
|
||||
@@ -133,6 +133,7 @@ cfg_rt! {
|
||||
/// Spawn a local task
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// This should only be called in `LocalRuntime` if the runtime has been verified to be owned
|
||||
/// by the current thread.
|
||||
#[allow(irrefutable_let_patterns)]
|
||||
@@ -143,7 +144,8 @@ cfg_rt! {
|
||||
F::Output: 'static,
|
||||
{
|
||||
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 {
|
||||
panic!("Only current_thread and LocalSet have spawn_local internals implemented")
|
||||
}
|
||||
|
||||
@@ -9,6 +9,15 @@
|
||||
//! Make sure to consult the relevant safety section of each function before
|
||||
//! 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::loom::cell::UnsafeCell;
|
||||
use crate::runtime::context;
|
||||
|
||||
@@ -106,6 +106,7 @@ impl<S: 'static> OwnedTasks<S> {
|
||||
/// Bind a task that isn't safe to transfer across thread boundaries.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// Only use this in `LocalRuntime` where the task cannot move
|
||||
pub(crate) unsafe fn bind_local<T>(
|
||||
&self,
|
||||
|
||||
@@ -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> {
|
||||
Task::new(RawTask::from_raw(ptr))
|
||||
unsafe { Task::new(RawTask::from_raw(ptr)) }
|
||||
}
|
||||
|
||||
#[cfg(all(
|
||||
@@ -479,8 +482,11 @@ 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> {
|
||||
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> {
|
||||
Task::from_raw(ptr)
|
||||
unsafe { Task::from_raw(ptr) }
|
||||
}
|
||||
|
||||
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)) }
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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::runtime::task::core::{Core, Trailer};
|
||||
use crate::runtime::task::{Cell, Harness, Header, Id, Schedule, State};
|
||||
@@ -222,6 +231,9 @@ impl RawTask {
|
||||
RawTask { ptr }
|
||||
}
|
||||
|
||||
/// # Safety
|
||||
///
|
||||
/// `ptr` must be a valid pointer to a [`Header`].
|
||||
pub(super) unsafe fn from_raw(ptr: NonNull<Header>) -> RawTask {
|
||||
RawTask { ptr }
|
||||
}
|
||||
|
||||
@@ -82,14 +82,18 @@ impl Context {
|
||||
where
|
||||
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
|
||||
where
|
||||
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
|
||||
@@ -313,7 +317,8 @@ cfg_rt_multi_thread! {
|
||||
|
||||
// clear the injection queue
|
||||
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);
|
||||
}
|
||||
|
||||
|
||||
@@ -42,6 +42,9 @@ impl<S> ops::Deref for WakerRef<'_, S> {
|
||||
}
|
||||
|
||||
cfg_trace! {
|
||||
/// # Safety
|
||||
///
|
||||
/// `$header` must be a valid pointer to a [`Header`].
|
||||
macro_rules! trace {
|
||||
($header:expr, $op:expr) => {
|
||||
if let Some(id) = Header::get_tracing_id(&$header) {
|
||||
@@ -65,31 +68,50 @@ cfg_not_trace! {
|
||||
}
|
||||
|
||||
unsafe fn clone_waker(ptr: *const ()) -> RawWaker {
|
||||
let header = NonNull::new_unchecked(ptr as *mut Header);
|
||||
trace!(header, "waker.clone");
|
||||
header.as_ref().state.ref_inc();
|
||||
// Safety: `ptr` was created from a `Header` pointer in function `waker_ref`.
|
||||
let header = unsafe { NonNull::new_unchecked(ptr as *mut Header) };
|
||||
#[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)
|
||||
}
|
||||
|
||||
unsafe fn drop_waker(ptr: *const ()) {
|
||||
let ptr = NonNull::new_unchecked(ptr as *mut Header);
|
||||
trace!(ptr, "waker.drop");
|
||||
let raw = RawTask::from_raw(ptr);
|
||||
// Safety: `ptr` was created from a `Header` pointer in function `waker_ref`.
|
||||
let ptr = unsafe { NonNull::new_unchecked(ptr as *mut Header) };
|
||||
// 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();
|
||||
}
|
||||
|
||||
unsafe fn wake_by_val(ptr: *const ()) {
|
||||
let ptr = NonNull::new_unchecked(ptr as *mut Header);
|
||||
trace!(ptr, "waker.wake");
|
||||
let raw = RawTask::from_raw(ptr);
|
||||
// Safety: `ptr` was created from a `Header` pointer in function `waker_ref`.
|
||||
let ptr = unsafe { NonNull::new_unchecked(ptr as *mut Header) };
|
||||
// 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();
|
||||
}
|
||||
|
||||
// Wake without consuming the waker
|
||||
unsafe fn wake_by_ref(ptr: *const ()) {
|
||||
let ptr = NonNull::new_unchecked(ptr as *mut Header);
|
||||
trace!(ptr, "waker.wake_by_ref");
|
||||
let raw = RawTask::from_raw(ptr);
|
||||
// Safety: `ptr` was created from a `Header` pointer in function `waker_ref`.
|
||||
let ptr = unsafe { NonNull::new_unchecked(ptr as *mut Header) };
|
||||
// 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();
|
||||
}
|
||||
|
||||
|
||||
@@ -471,7 +471,7 @@ unsafe impl linked_list::Link for TimerShared {
|
||||
unsafe fn pointers(
|
||||
target: NonNull<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
|
||||
/// lock is held, and that the timer is not in any wheel linked lists.
|
||||
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
|
||||
@@ -657,14 +659,18 @@ impl TimerHandle {
|
||||
/// 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.
|
||||
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(()) => {
|
||||
// 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(())
|
||||
}
|
||||
Err(tick) => {
|
||||
self.inner.as_ref().set_registered_when(tick);
|
||||
unsafe {
|
||||
self.inner.as_ref().set_registered_when(tick);
|
||||
}
|
||||
Err(tick)
|
||||
}
|
||||
}
|
||||
@@ -682,6 +688,6 @@ impl TimerHandle {
|
||||
/// SAFETY: The driver lock must be held while invoking this function, and
|
||||
/// the entry must not be in any wheel linked lists.
|
||||
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) }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -120,7 +120,7 @@ impl Level {
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
|
||||
@@ -91,7 +91,7 @@ impl Wheel {
|
||||
&mut self,
|
||||
item: TimerHandle,
|
||||
) -> Result<u64, (TimerHandle, InsertError)> {
|
||||
let when = item.sync_when();
|
||||
let when = unsafe { item.sync_when() };
|
||||
|
||||
if when <= self.elapsed {
|
||||
return Err((item, InsertError::Elapsed));
|
||||
|
||||
@@ -81,18 +81,21 @@ impl<T> ReusableBoxFuture<T> {
|
||||
F: Future<Output = T> + Send + 'static,
|
||||
{
|
||||
// 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());
|
||||
}));
|
||||
|
||||
// Overwrite the future behind the pointer. This is safe because the
|
||||
// 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;
|
||||
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
|
||||
// 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.
|
||||
match result {
|
||||
|
||||
@@ -157,9 +157,9 @@ mod tests {
|
||||
if event_requires_infinite_sleep_in_handler(signum) {
|
||||
// Those events will enter an infinite loop in `handler`, so
|
||||
// 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 {
|
||||
super::handler(signum);
|
||||
unsafe { super::handler(signum) };
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -775,6 +775,6 @@ unsafe impl linked_list::Link for Waiter {
|
||||
}
|
||||
|
||||
unsafe fn pointers(target: NonNull<Waiter>) -> NonNull<linked_list::Pointers<Waiter>> {
|
||||
Waiter::addr_of_pointers(target)
|
||||
unsafe { Waiter::addr_of_pointers(target) }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1678,7 +1678,7 @@ unsafe impl linked_list::Link for Waiter {
|
||||
}
|
||||
|
||||
unsafe fn pointers(target: NonNull<Waiter>) -> NonNull<linked_list::Pointers<Waiter>> {
|
||||
Waiter::addr_of_pointers(target)
|
||||
unsafe { Waiter::addr_of_pointers(target) }
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -163,9 +163,15 @@ impl<T> Block<T> {
|
||||
}
|
||||
|
||||
// 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.
|
||||
@@ -197,7 +203,10 @@ impl<T> Block<T> {
|
||||
let slot_offset = offset(slot_index);
|
||||
|
||||
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
|
||||
@@ -246,7 +255,11 @@ impl<T> Block<T> {
|
||||
// tail_position is guaranteed to not access this block.
|
||||
self.header
|
||||
.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
|
||||
// free the block's memory as soon as all slots **prior** to
|
||||
@@ -316,7 +329,9 @@ impl<T> Block<T> {
|
||||
success: Ordering,
|
||||
failure: Ordering,
|
||||
) -> 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
|
||||
.header
|
||||
@@ -428,8 +443,9 @@ impl<T> Values<T> {
|
||||
if_loom! {
|
||||
let p = _value.as_ptr() as *mut UnsafeCell<MaybeUninit<T>>;
|
||||
for i in 0..BLOCK_CAP {
|
||||
p.add(i)
|
||||
.write(UnsafeCell::new(MaybeUninit::uninit()));
|
||||
unsafe {
|
||||
p.add(i).write(UnsafeCell::new(MaybeUninit::uninit()));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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>>) {
|
||||
// The block has been removed from the linked list and ownership
|
||||
// is reclaimed.
|
||||
@@ -192,24 +199,28 @@ impl<T> Tx<T> {
|
||||
// inserting it back at the end of the linked list.
|
||||
//
|
||||
// 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;
|
||||
|
||||
// Attempt to insert the block at the end
|
||||
//
|
||||
// Walk at most three times
|
||||
//
|
||||
let curr_ptr = self.block_tail.load(Acquire);
|
||||
|
||||
// The pointer can never be 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
|
||||
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(()) => {
|
||||
reused = true;
|
||||
break;
|
||||
@@ -221,7 +232,11 @@ impl<T> Tx<T> {
|
||||
}
|
||||
|
||||
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 {
|
||||
cur = block.as_ref().load_next(Relaxed);
|
||||
drop(Box::from_raw(block.as_ptr()));
|
||||
cur = unsafe { block.as_ref() }.load_next(Relaxed);
|
||||
drop(unsafe { Box::from_raw(block.as_ptr()) });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1385,7 +1385,7 @@ unsafe impl linked_list::Link for Waiter {
|
||||
}
|
||||
|
||||
unsafe fn pointers(target: NonNull<Waiter>) -> NonNull<linked_list::Pointers<Waiter>> {
|
||||
Waiter::addr_of_pointers(target)
|
||||
unsafe { Waiter::addr_of_pointers(target) }
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -244,12 +244,16 @@ impl<T> OnceCell<T> {
|
||||
|
||||
// SAFETY: The OnceCell must not be empty.
|
||||
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.
|
||||
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 {
|
||||
|
||||
@@ -404,31 +404,51 @@ struct Inner<T> {
|
||||
struct Task(UnsafeCell<MaybeUninit<Waker>>);
|
||||
|
||||
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 {
|
||||
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
|
||||
where
|
||||
F: FnOnce(&Waker) -> R,
|
||||
{
|
||||
self.0.with(|ptr| {
|
||||
let waker: *const Waker = (*ptr).as_ptr();
|
||||
f(&*waker)
|
||||
let waker: *const Waker = unsafe { (*ptr).as_ptr() };
|
||||
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) {
|
||||
self.0.with_mut(|ptr| {
|
||||
let ptr: *mut Waker = (*ptr).as_mut_ptr();
|
||||
ptr.drop_in_place();
|
||||
let ptr: *mut Waker = unsafe { (*ptr).as_mut_ptr() };
|
||||
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<'_>) {
|
||||
self.0.with_mut(|ptr| {
|
||||
let ptr: *mut Waker = (*ptr).as_mut_ptr();
|
||||
ptr.write(cx.waker().clone());
|
||||
let ptr: *mut Waker = unsafe { (*ptr).as_mut_ptr() };
|
||||
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 it is set, then only the receiver may call this method.
|
||||
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`.
|
||||
@@ -1390,7 +1410,7 @@ impl<T> Inner<T> {
|
||||
/// 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.
|
||||
unsafe fn has_value(&self) -> bool {
|
||||
self.value.with(|ptr| (*ptr).is_some())
|
||||
self.value.with(|ptr| unsafe { (*ptr).is_some() })
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -255,7 +255,7 @@ impl<T> SetOnce<T> {
|
||||
|
||||
// SAFETY: The SetOnce must not be empty.
|
||||
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
|
||||
|
||||
@@ -12,15 +12,17 @@ fn notify_clones_waker_before_lock() {
|
||||
|
||||
unsafe fn clone_w(data: *const ()) -> RawWaker {
|
||||
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
|
||||
// Notify wait list is locked.
|
||||
(*ptr).notify_one();
|
||||
unsafe { (*ptr).notify_one() };
|
||||
RawWaker::new(data, VTABLE)
|
||||
}
|
||||
|
||||
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 ()) {
|
||||
|
||||
+18
-3
@@ -1192,28 +1192,43 @@ impl task::Schedule for Arc<Shared> {
|
||||
}
|
||||
|
||||
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>>> {
|
||||
// The caller ensures it is called from the same thread that owns
|
||||
// the LocalSet.
|
||||
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>>) {
|
||||
// The caller ensures it is called from the same thread that owns
|
||||
// the LocalSet.
|
||||
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>>> {
|
||||
// The caller ensures it is called from the same thread that owns
|
||||
// the LocalSet.
|
||||
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>>> {
|
||||
|
||||
@@ -349,7 +349,10 @@ impl<T> IdleNotifiedSet<T> {
|
||||
unsafe fn move_to_new_list<T>(from: &mut LinkedList<T>, to: &mut LinkedList<T>) {
|
||||
while let Some(entry) = from.pop_back() {
|
||||
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);
|
||||
}
|
||||
@@ -479,13 +482,13 @@ unsafe impl<T> linked_list::Link for 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(
|
||||
target: NonNull<ListEntry<T>>,
|
||||
) -> NonNull<linked_list::Pointers<ListEntry<T>>> {
|
||||
ListEntry::addr_of_pointers(target)
|
||||
unsafe { ListEntry::addr_of_pointers(target) }
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,11 @@
|
||||
#![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.
|
||||
//!
|
||||
@@ -64,6 +71,8 @@ pub(crate) unsafe trait Link {
|
||||
/// stack as the argument. In particular, the method may not create an
|
||||
/// intermediate reference in the process of creating the resulting raw
|
||||
/// pointer.
|
||||
///
|
||||
/// The `target` pointer must be valid.
|
||||
unsafe fn pointers(target: NonNull<Self::Target>) -> NonNull<Pointers<Self::Target>>;
|
||||
}
|
||||
|
||||
|
||||
@@ -35,7 +35,7 @@ impl<T> RcCell<T> {
|
||||
// not called recursively. Finally, this is the only place that can
|
||||
// create mutable references to the inner Rc. This ensures that any
|
||||
// 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>> {
|
||||
|
||||
@@ -27,6 +27,7 @@ pub(crate) struct ShardedList<L, T> {
|
||||
/// call to call.
|
||||
pub(crate) unsafe trait ShardedListItem: Link {
|
||||
/// # Safety
|
||||
///
|
||||
/// The provided pointer must point at a valid list item.
|
||||
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 currently contained by some other `GuardedLinkedList`.
|
||||
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);
|
||||
// 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.
|
||||
|
||||
@@ -9,7 +9,8 @@ use std::{
|
||||
pub(super) unsafe fn try_transmute<Src, Target: 'static>(x: Src) -> Result<Target, Src> {
|
||||
if nonstatic_typeid::<Src>() == TypeId::of::<Target>() {
|
||||
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 {
|
||||
Err(x)
|
||||
}
|
||||
|
||||
+10
-4
@@ -51,22 +51,28 @@ fn waker_vtable<W: Wake>() -> &'static RawWakerVTable {
|
||||
}
|
||||
|
||||
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>())
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
// used by `waker_ref`
|
||||
unsafe fn wake_by_ref_arc_raw<T: Wake>(data: *const ()) {
|
||||
// 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);
|
||||
}
|
||||
|
||||
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()) });
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user