mirror of
https://github.com/tokio-rs/tokio.git
synced 2026-09-06 00:00:10 +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)]
|
#[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() }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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(()));
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -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
@@ -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))
|
||||||
|
|||||||
@@ -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) }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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) }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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 {
|
||||||
|
|||||||
@@ -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) }
|
||||||
}
|
}
|
||||||
)*}
|
)*}
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -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 }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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 }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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());
|
||||||
|
|||||||
@@ -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
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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(());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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`.
|
||||||
|
|||||||
@@ -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()) }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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) }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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.
|
||||||
|
|||||||
@@ -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,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;
|
||||||
|
|||||||
@@ -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,
|
||||||
|
|||||||
@@ -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)) }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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 }
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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) }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|
||||||
|
|||||||
@@ -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));
|
||||||
|
|||||||
@@ -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 {
|
||||||
|
|||||||
@@ -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) };
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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) }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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) }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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()));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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()) });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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) }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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 {
|
||||||
|
|||||||
@@ -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() })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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
@@ -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>>> {
|
||||||
|
|||||||
@@ -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) }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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>>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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>> {
|
||||||
|
|||||||
@@ -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.
|
||||||
|
|||||||
@@ -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
@@ -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()) });
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user