some_executor 0.7.2

A trait for libraries that abstract over any executor
Documentation
// SPDX-License-Identifier: MIT OR Apache-2.0

//! Storing a typed completion notifier in a type-erased task.
//!
//! [`ObserverNotified`] is generic over the value it receives, so a notifier
//! written against a concrete output type cannot be stored in a task whose output
//! has been erased to `Box<dyn Any>`. The wrappers here bridge that: they accept
//! `&dyn Any`, downcast it to the type the inner notifier expects, and forward.
//!
//! Two variants, differing only in the bounds the erased task requires:
//! [`ObserverNotifiedErased`] for the `Send` path taken by
//! [`Task::into_objsafe`](crate::task::Task::into_objsafe), and
//! [`ObserverNotifiedErasedLocal`] for the `'static`-but-not-`Send` path taken by
//! `into_objsafe_local` and `into_objsafe_static`.
//!
//! The downcast panics on mismatch, for the same reason as in
//! [`crate::dyn_observer`]: the value was boxed by this crate, so a failure means
//! a layer above unwrapped one box too few or too many rather than that a caller
//! supplied the wrong type.

use crate::observer::ObserverNotified;
use std::any::Any;
use std::marker::PhantomData;
use std::ops::{Deref, DerefMut};

/**
Wraps an observer of known value type into an observer of 'any' value type.

The resulting observer implements ObserverNotified<dyn Any + Send + 'static> and can be used in place of the original observer.
*/
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct ObserverNotifiedErased<O, V>(O, PhantomData<V>)
where
    O: ObserverNotified<V>;

impl<Observer, Value> ObserverNotified<dyn Any + Send + 'static>
    for ObserverNotifiedErased<Observer, Value>
where
    Observer: ObserverNotified<Value>,
    Value: Unpin + 'static,
{
    fn notify(&mut self, value: &(dyn Any + Send + 'static)) {
        let downcast = value.downcast_ref::<Value>().expect("Downcast failed");
        self.0.notify(downcast);
    }
}

impl<O, V> ObserverNotifiedErased<O, V>
where
    O: ObserverNotified<V>,
{
    pub fn new(observer: O) -> Self {
        Self(observer, PhantomData)
    }
}

//boilerplate

impl<O, V> From<O> for ObserverNotifiedErased<O, V>
where
    O: ObserverNotified<V>,
{
    fn from(observer: O) -> Self {
        Self::new(observer)
    }
}

impl<O, V> ObserverNotifiedErased<O, V>
where
    O: ObserverNotified<V>,
{
    // pub fn into_inner(self) -> O {
    //     self.0
    // }
}

impl<O, V> AsRef<O> for ObserverNotifiedErased<O, V>
where
    O: ObserverNotified<V>,
{
    fn as_ref(&self) -> &O {
        &self.0
    }
}

impl<O, V> AsMut<O> for ObserverNotifiedErased<O, V>
where
    O: ObserverNotified<V>,
{
    fn as_mut(&mut self) -> &mut O {
        &mut self.0
    }
}

impl<O, V> Deref for ObserverNotifiedErased<O, V>
where
    O: ObserverNotified<V>,
{
    type Target = O;

    fn deref(&self) -> &Self::Target {
        &self.0
    }
}

impl<O, V> DerefMut for ObserverNotifiedErased<O, V>
where
    O: ObserverNotified<V>,
{
    fn deref_mut(&mut self) -> &mut Self::Target {
        &mut self.0
    }
}

/**
Wraps an observer of known value type into an observer of 'any' value type for local (non-Send) contexts.

The resulting observer implements ObserverNotified<dyn Any + 'static> and can be used in place of the original observer.
*/
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct ObserverNotifiedErasedLocal<O, V>(O, PhantomData<V>)
where
    O: ObserverNotified<V>;

impl<Observer, Value> ObserverNotified<dyn Any + 'static>
    for ObserverNotifiedErasedLocal<Observer, Value>
where
    Observer: ObserverNotified<Value>,
    Value: Unpin + 'static,
{
    fn notify(&mut self, value: &(dyn Any + 'static)) {
        let downcast = value.downcast_ref::<Value>().expect("Downcast failed");
        self.0.notify(downcast);
    }
}

impl<O, V> ObserverNotifiedErasedLocal<O, V>
where
    O: ObserverNotified<V>,
{
    pub fn new(observer: O) -> Self {
        Self(observer, PhantomData)
    }
}

//boilerplate

impl<O, V> From<O> for ObserverNotifiedErasedLocal<O, V>
where
    O: ObserverNotified<V>,
{
    fn from(observer: O) -> Self {
        Self::new(observer)
    }
}

impl<O, V> ObserverNotifiedErasedLocal<O, V>
where
    O: ObserverNotified<V>,
{
    // pub fn into_inner(self) -> O {
    //     self.0
    // }
}

impl<O, V> AsRef<O> for ObserverNotifiedErasedLocal<O, V>
where
    O: ObserverNotified<V>,
{
    fn as_ref(&self) -> &O {
        &self.0
    }
}

impl<O, V> AsMut<O> for ObserverNotifiedErasedLocal<O, V>
where
    O: ObserverNotified<V>,
{
    fn as_mut(&mut self) -> &mut O {
        &mut self.0
    }
}

impl<O, V> Deref for ObserverNotifiedErasedLocal<O, V>
where
    O: ObserverNotified<V>,
{
    type Target = O;

    fn deref(&self) -> &Self::Target {
        &self.0
    }
}

impl<O, V> DerefMut for ObserverNotifiedErasedLocal<O, V>
where
    O: ObserverNotified<V>,
{
    fn deref_mut(&mut self) -> &mut Self::Target {
        &mut self.0
    }
}

#[cfg(test)]
mod tests {
    use crate::observer::ObserverNotified;
    use crate::task::{Configuration, Task};
    use std::any::Any;
    use std::sync::Arc;
    use std::sync::atomic::{AtomicUsize, Ordering};

    struct RecordingNotifier(Arc<AtomicUsize>);

    impl ObserverNotified<u32> for RecordingNotifier {
        fn notify(&mut self, value: &u32) {
            assert_eq!(*value, 42);
            self.0.fetch_add(1, Ordering::Relaxed);
        }
    }

    /// A notifier erased through `into_objsafe` must still be handed the typed value.
    ///
    /// The task's output is boxed into `Box<dyn Any + Send>` for the object-safe path, so
    /// the boxed notifier has to unbox before dispatching; otherwise the erased notifier
    /// downcasts the box itself and panics.
    #[wasm_lite::wasm_lite_test]
    async fn erased_notifier_receives_typed_value() {
        let notifications = Arc::new(AtomicUsize::new(0));
        let task = Task::with_notifications(
            "erased".to_string(),
            Configuration::default(),
            Some(RecordingNotifier(notifications.clone())),
            async { 42u32 },
        );

        let mut executor = crate::current_executor::current_executor();
        let (spawned, observer) = task.into_objsafe().spawn_objsafe(&mut executor);
        let _ = spawned.await;

        assert_eq!(
            notifications.load(Ordering::Relaxed),
            1,
            "the erased notifier must be notified with the unboxed value"
        );
        drop(observer);
    }

    /// The non-Send unboxing impl must unbox too.
    #[cfg_attr(not(target_arch = "wasm32"), test)]
    #[cfg_attr(target_arch = "wasm32", wasm_lite::wasm_lite_test)]
    fn boxed_local_notifier_unboxes_the_value() {
        struct Inner(Arc<AtomicUsize>);
        impl ObserverNotified<dyn Any + 'static> for Inner {
            fn notify(&mut self, value: &(dyn Any + 'static)) {
                assert_eq!(
                    value.downcast_ref::<u32>().copied(),
                    Some(9),
                    "the erased notifier must receive the value, not the box around it"
                );
                self.0.fetch_add(1, Ordering::Relaxed);
            }
        }

        let notifications = Arc::new(AtomicUsize::new(0));
        let mut boxed: Box<dyn ObserverNotified<dyn Any + 'static>> =
            Box::new(Inner(notifications.clone()));
        let value: Box<dyn Any + 'static> = Box::new(9u32);

        ObserverNotified::notify(&mut boxed, &value);

        assert_eq!(notifications.load(Ordering::Relaxed), 1);
    }
}