use std::{
future::Future,
marker::PhantomData,
ops::{Add, Sub},
pin::Pin,
sync::{
Arc,
atomic::{AtomicBool, Ordering},
},
task::{Context, Poll},
};
use pin_project_lite::pin_project;
pub use super::participant::{Participating, participate};
use super::{
ctx::{self, ModeSnapshot, flash_ambient, flash_enabled},
ids::WaiterId,
system::{self, FLASH},
};
pub use crate::common::time::Duration;
use crate::flash::time::{FlashTimeout, TimeoutError};
#[must_use]
pub struct RealIoScope {
_priv: (),
}
pub fn real_io() -> RealIoScope {
system::real_io_enter();
RealIoScope { _priv: () }
}
impl Drop for RealIoScope {
fn drop(&mut self) {
system::real_io_exit();
}
}
pub(crate) struct FlashSleep {
handle: Option<system::AsyncHandle>,
delta_nanos: u64,
}
impl FlashSleep {
pub(crate) fn new(duration: Duration) -> Self {
Self {
delta_nanos: duration_to_nanos(duration),
handle: None,
}
}
}
impl Future for FlashSleep {
type Output = ();
fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<()> {
if let Some(handle) = self.handle.as_ref() {
if handle.granted() {
self.handle = None;
return Poll::Ready(());
}
return Poll::Pending;
}
let (handle, adv) = system::register_sleep_async(self.delta_nanos, cx.waker().clone());
self.handle = Some(handle);
adv.fire();
Poll::Pending
}
}
impl Drop for FlashSleep {
fn drop(&mut self) {
if let Some(handle) = self.handle.take() {
system::cancel_async_wait(&handle);
}
}
}
pub struct FlashYield {
handle: Option<(WaiterId, Arc<AtomicBool>)>,
done: bool,
}
impl Future for FlashYield {
type Output = ();
fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<()> {
if self.done {
return Poll::Ready(());
}
if let Some((_, granted)) = self.handle.as_ref() {
if granted.load(Ordering::Acquire) {
self.done = true;
self.handle = None;
return Poll::Ready(());
}
return Poll::Pending;
}
let (id, granted, adv) = system::register_yield_async(cx.waker().clone());
self.handle = Some((id, granted));
adv.fire();
Poll::Pending
}
}
impl Drop for FlashYield {
fn drop(&mut self) {
if let Some((id, _)) = self.handle.take() {
system::cancel_yield(id);
}
}
}
pub fn yield_now() -> Yield {
if flash_ambient() {
Yield::Flash(FlashYield {
handle: None,
done: false,
})
} else {
Yield::Real { yielded: false }
}
}
#[must_use = "a Yield future does nothing unless `.await`ed"]
pub enum Yield {
Flash(FlashYield),
Real { yielded: bool },
}
impl Future for Yield {
type Output = ();
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<()> {
match self.get_mut() {
Self::Flash(f) => Pin::new(f).poll(cx),
Self::Real { yielded } => {
if *yielded {
Poll::Ready(())
} else {
*yielded = true;
cx.waker().wake_by_ref();
Poll::Pending
}
}
}
}
}
#[must_use]
pub fn hang_dump(context: &str) -> String {
format!(
"[flash hang dump] {context}\n{thread}\n--- quiescence engine ---\n{engine}\
--- sync primitives ---\n{registry}",
thread = super::diag::current_thread_context(),
engine = system::dump(),
registry = super::diag::snapshot(),
)
}
pub fn log_hang_dump(context: &str) {
tracing::error!(target: "flash::hang", "{}", hang_dump(context));
}
pub fn virtual_sleep(duration: Duration) -> impl Future<Output = ()> {
FlashSleep::new(duration)
}
pub async fn virtual_timeout<F>(duration: Duration, future: F) -> Result<F::Output, TimeoutError>
where
F: Future,
{
FlashTimeout {
future,
sleep: FlashSleep::new(duration),
}
.await
}
#[must_use]
pub fn virtual_now() -> Instant {
Instant::now_virtual()
}
pub fn virtual_park_timeout(duration: Duration) {
crate::thread::park_timeout_virtual(duration);
}
pub(super) fn duration_to_nanos(d: Duration) -> u64 {
const NANOS_PER_SEC: u64 = 1_000_000_000;
d.as_secs()
.saturating_mul(NANOS_PER_SEC)
.saturating_add(u64::from(d.subsec_nanos()))
}
#[cfg(test)]
#[inline]
pub(crate) fn advance(delta: Duration) {
FLASH.clock.advance(duration_to_nanos(delta));
}
#[inline]
pub fn reset() {
FLASH.reset();
}
#[must_use]
pub struct FlashScope(ModeSnapshot, PhantomData<*mut ()>);
impl Drop for FlashScope {
fn drop(&mut self) {
ctx::restore_mode(self.0);
}
}
#[doc(hidden)]
pub fn enter_dynamic(on: bool) -> FlashScope {
FlashScope(ctx::push_active(on), PhantomData)
}
pub fn flash_real() -> FlashScope {
enter_dynamic(false)
}
#[must_use]
pub struct AmbientScope(ModeSnapshot, PhantomData<*mut ()>);
impl Drop for AmbientScope {
fn drop(&mut self) {
ctx::restore_mode(self.0);
}
}
pub fn ambient_scope(on: bool) -> AmbientScope {
AmbientScope(ctx::push_ambient(on), PhantomData)
}
#[inline]
#[must_use]
pub fn ambient_snapshot() -> bool {
flash_ambient()
}
pub fn set_ambient_for_spawn(on: bool) -> AmbientScope {
ambient_scope(on)
}
pin_project! {
pub struct WithAmbient<F> {
on: bool,
#[pin]
fut: F,
}
}
impl<F: Future> Future for WithAmbient<F> {
type Output = F::Output;
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<F::Output> {
let this = self.project();
let _a = set_ambient_for_spawn(*this.on);
this.fut.poll(cx)
}
}
pub fn with_ambient<F: Future>(on: bool, fut: F) -> WithAmbient<F> {
WithAmbient { on, fut }
}
pin_project! {
pub struct FlashDynamic<F> {
on: bool,
#[pin]
fut: F,
}
}
impl<F: Future> Future for FlashDynamic<F> {
type Output = F::Output;
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<F::Output> {
let this = self.project();
let _g = enter_dynamic(*this.on);
this.fut.poll(cx)
}
}
#[doc(hidden)]
pub fn dynamic<F: Future>(on: bool, fut: F) -> FlashDynamic<F> {
FlashDynamic { on, fut }
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)]
pub struct Instant(u64);
impl Instant {
pub(in crate::flash) const BASE_NANOS: u64 = 86_400_000_000_000;
#[inline]
pub(crate) fn as_virtual_nanos(self) -> u64 {
self.0
}
#[inline]
#[must_use]
pub fn duration_since(&self, earlier: Self) -> Duration {
self.saturating_duration_since(earlier)
}
#[inline]
#[must_use]
pub fn elapsed(&self) -> Duration {
Self::now().saturating_duration_since(*self)
}
#[inline]
#[must_use]
pub fn now() -> Self {
if flash_enabled() {
Self::now_virtual()
} else {
Self(FLASH.clock.real_now_nanos())
}
}
#[inline]
#[must_use]
pub fn now_virtual() -> Self {
Self(FLASH.clock.now_nanos())
}
#[inline]
#[must_use]
pub fn saturating_duration_since(&self, earlier: Self) -> Duration {
Duration::from_nanos(self.0.saturating_sub(earlier.0))
}
}
impl Add<Duration> for Instant {
type Output = Self;
#[inline]
fn add(self, rhs: Duration) -> Self {
Self(self.0.saturating_add(duration_to_nanos(rhs)))
}
}
impl Sub<Duration> for Instant {
type Output = Self;
#[inline]
fn sub(self, rhs: Duration) -> Self {
Self(self.0.saturating_sub(duration_to_nanos(rhs)))
}
}
impl Sub<Self> for Instant {
type Output = Duration;
#[inline]
fn sub(self, rhs: Self) -> Duration {
self.saturating_duration_since(rhs)
}
}