qubit-clock 0.13.0

Injectable wall and monotonic clocks with deterministic timers
Documentation
// =============================================================================
//    Copyright (c) 2025 - 2026 Haixing Hu.
//
//    SPDX-License-Identifier: Apache-2.0
//
//    Licensed under the Apache License, Version 2.0.
// =============================================================================
//! Defines the inline Tokio timer future representation.

// qubit-style: allow coverage-cfg

use std::future::Future;
use std::panic::AssertUnwindSafe;
use std::panic::catch_unwind;
use std::panic::resume_unwind;
use std::pin::Pin;
use std::sync::Arc;
use std::task::Context;
use std::task::Poll;

use pin_project_lite::pin_project;
use tokio::sync::futures::OwnedNotified;
use tokio::time::Sleep;

use crate::TimeError;
use crate::TimerUnavailableError;
use crate::timer::internal::tokio_runtime_liveness::TokioRuntimeLiveness;
#[cfg(coverage)]
use crate::timer::tokio_timer::take_tokio_timer_sleep_poll_panic;

pin_project! {
    /// Tokio sleep paired with shared retained-runtime liveness.
    #[derive(Debug)]
    pub(crate) struct TokioTimerFuture {
        // Native Tokio deadline future.
        #[pin]
        sleep: Sleep,
        // Shutdown notification owned by this deadline.
        #[pin]
        shutdown: OwnedNotified,
        // Shared state used for race-free shutdown checks.
        liveness: Arc<TokioRuntimeLiveness>,
    }
}

impl TokioTimerFuture {
    /// Creates a future for one native sleep.
    ///
    /// # Parameters
    ///
    /// * `sleep` - Native Tokio deadline future.
    /// * `liveness` - Shared state for the sleep's retained runtime.
    ///
    /// # Returns
    ///
    /// A single allocation containing both wait conditions.
    #[must_use]
    pub(crate) fn new(sleep: Sleep, liveness: Arc<TokioRuntimeLiveness>) -> Self {
        let shutdown = liveness.shutdown_notification();
        Self {
            sleep,
            shutdown,
            liveness,
        }
    }
}

impl Future for TokioTimerFuture {
    type Output = Result<(), TimeError>;

    /// Polls shutdown before the native sleep and preserves unexpected panics.
    fn poll(self: Pin<&mut Self>, context: &mut Context<'_>) -> Poll<Self::Output> {
        let mut this = self.project();
        if this.liveness.is_shutdown() || this.shutdown.as_mut().poll(context).is_ready() {
            return runtime_shutdown();
        }
        match catch_unwind(AssertUnwindSafe(|| {
            #[cfg(coverage)]
            if take_tokio_timer_sleep_poll_panic() {
                panic!("injected Tokio sleep poll panic");
            }
            this.sleep.as_mut().poll(context)
        })) {
            Ok(Poll::Pending) => Poll::Pending,
            Ok(Poll::Ready(())) => Poll::Ready(Ok(())),
            Err(payload) => {
                if this.liveness.is_shutdown() {
                    runtime_shutdown()
                } else {
                    resume_unwind(payload)
                }
            }
        }
    }
}

/// Creates the structured runtime-shutdown result.
///
/// # Returns
///
/// A ready timer-unavailable result reporting runtime shutdown.
fn runtime_shutdown() -> Poll<Result<(), TimeError>> {
    Poll::Ready(Err(TimeError::TimerUnavailable {
        source: TimerUnavailableError::RuntimeShuttingDown,
    }))
}