futuresdr 0.0.41

An Experimental Async SDR Runtime for Heterogeneous Architectures.
Documentation
use std::future::Future;
use std::pin::Pin;

use crate::runtime::Result;
use crate::runtime::dev::BlockMeta;
use crate::runtime::dev::MessageOutputs;
use crate::runtime::dev::WorkIo;

/// Send-capable marker for normal runtime blocks.
///
/// This keeps verbose return-type-notation bounds in one place: normal blocks
/// must have a `Send` value, `Send` block-on future, and `Send` futures returned
/// from the kernel lifecycle methods.
#[doc(hidden)]
pub trait SendKernel: Kernel<BlockOn: Send> + Send
where
    Self: Kernel<work(..): Send, init(..): Send, deinit(..): Send>,
{
}

impl<T> SendKernel for T where
    T: Kernel<BlockOn: Send, work(..): Send, init(..): Send, deinit(..): Send> + Send
{
}

/// Processing logic for a block.
///
/// `Kernel` is the central trait custom block authors implement. The
/// `#[derive(Block)]` macro declares stream and message ports from annotated
/// fields and methods; the `Kernel` implementation supplies initialization,
/// work, and shutdown behavior.
///
/// The runtime calls [`Kernel::init`] once, then repeatedly calls
/// [`Kernel::work`] until the block marks itself finished or the flowgraph is
/// stopped, and finally calls [`Kernel::deinit`]. A `work()` implementation
/// should consume and produce exactly the number of stream items it handled and
/// use [`WorkIo`] to request another immediate call, wait on a future, or finish.
///
/// Normal runtime entry points accept only kernels whose value, block-on future,
/// and returned futures are `Send`. Kernels that do not satisfy these bounds can
/// still run in a local domain.
///
/// ```
/// use futuresdr::runtime::dev::prelude::*;
///
/// #[derive(Block)]
/// struct Scale {
///     #[input]
///     input: DefaultCpuReader<f32>,
///     #[output]
///     output: DefaultCpuWriter<f32>,
///     gain: f32,
/// }
///
/// impl Kernel for Scale {
///     async fn work(
///         &mut self,
///         io: &mut WorkIo,
///         _mo: &mut MessageOutputs,
///         _meta: &mut BlockMeta,
///     ) -> Result<()> {
///         let input = self.input.slice();
///         let output = self.output.slice();
///         let n = input.len().min(output.len());
///
///         for i in 0..n {
///             output[i] = input[i] * self.gain;
///         }
///
///         self.input.consume(n);
///         self.output.produce(n);
///
///         if self.input.finished() {
///             io.finished = true;
///         }
///
///         Ok(())
///     }
/// }
/// ```
pub trait Kernel {
    /// Typed future that may be used to wake the block again.
    type BlockOn: Future<Output = ()> + 'static = std::future::Pending<()>;

    /// Return the typed future that should wake this block again.
    ///
    /// This is queried after [`WorkIo::block_on`] has been set. Returning
    /// `None` falls back to waiting only for inbox/stream notifications.
    fn block_on(&mut self) -> Option<Pin<&mut Self::BlockOn>> {
        None
    }

    /// Process stream data and emit messages.
    ///
    /// Implementations inspect their input buffers, write output buffers, update
    /// consume/produce counts, optionally post PMTs through [`MessageOutputs`],
    /// and update [`WorkIo`] flags before returning.
    fn work(
        &mut self,
        _io: &mut WorkIo,
        _mo: &mut MessageOutputs,
        _b: &mut BlockMeta,
    ) -> impl Future<Output = Result<()>> {
        async { Ok(()) }
    }

    /// Initialize the kernel before normal work starts.
    ///
    /// This is the place to allocate runtime resources, send initial messages,
    /// or update [`BlockMeta`]. Stream ports have already been initialized and
    /// validated when this method is called.
    fn init(
        &mut self,
        _mo: &mut MessageOutputs,
        _b: &mut BlockMeta,
    ) -> impl Future<Output = Result<()>> {
        async { Ok(()) }
    }

    /// De-initialize the kernel after work has stopped.
    ///
    /// This is called during block shutdown even when the block stopped because
    /// the flowgraph was terminated. It should release resources owned by the
    /// block and may post final messages.
    fn deinit(
        &mut self,
        _mo: &mut MessageOutputs,
        _b: &mut BlockMeta,
    ) -> impl Future<Output = Result<()>> {
        async { Ok(()) }
    }
}