execution-context 0.1.0

An experimental .NET inspired execution context
Documentation
use im::hashmap::HashMap;
use std::any::TypeId;
use std::fmt;
use std::hash::{BuildHasherDefault, Hasher};
use std::mem;
use std::sync::Arc;
use std::ops::Deref;

use ctx::ExecutionContext;

/// A macro to create a `static` of type `FlowLocal`
///
/// This macro is intentionally similar to the `thread_local!`, and creates a
/// `static` which has a `get_mut` method to access the data on a flow.
///
/// The data associated with each flow local is per-flow, so different flows
/// will contain different values.
#[macro_export]
macro_rules! flow_local {
    (static $NAME:ident : $t:ty = $e:expr) => {
        static $NAME: $crate::FlowLocal<$t> = {
            fn __init() -> $t {
                $e
            }
            fn __key() -> ::std::any::TypeId {
                struct __A;
                ::std::any::TypeId::of::<__A>()
            }
            $crate::FlowLocal {
                __init: __init,
                __key: __key,
            }
        };
    };
}

/// Am immutable wrapper around a value from a flow local.
///
/// This is returned when a flow local is accessed and is internally
/// reference counted.  Cloning this value is reasonably efficient.
#[derive(Clone)]
pub struct FlowBox<T: ?Sized>(Arc<Box<T>>);

pub(crate) type LocalMap = HashMap<TypeId, Box<Opaque>, BuildHasherDefault<IdHasher>>;

pub(crate) trait Opaque: Send + Sync {}
impl<T: Send + Sync> Opaque for T {}

/// A key for flow-local data stored in the execution context.
///
/// This type is generated by the `flow_local!` macro and performs very
/// similarly to the `thread_local!` macro and `std::thread::FlowLocal` types.
/// Data associated with a `FlowLocal<T>` is stored inside the current
/// execution context and the data is destroyed when the execution context
/// is destroyed.
///
/// Flow-local data can migrate between threads and hence requires a `Send`
/// bound. Additionally, flow-local data also requires the `'static` bound to
/// ensure it lives long enough. When a key is accessed for the first time the
/// flow's data is initialized with the provided initialization expression to
/// the macro.
pub struct FlowLocal<T> {
    // "private" fields which have to be public to get around macro hygiene, not
    // included in the stability story for this type. Can change at any time.
    #[doc(hidden)]
    pub __key: fn() -> TypeId,
    #[doc(hidden)]
    pub __init: fn() -> T,
}

impl<T> fmt::Debug for FlowLocal<T> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "FlowLocal")
    }
}

pub(crate) struct IdHasher {
    id: u64,
}

impl Default for IdHasher {
    fn default() -> IdHasher {
        IdHasher { id: 0 }
    }
}

impl Hasher for IdHasher {
    fn write(&mut self, _bytes: &[u8]) {
        // TODO: need to do something sensible
        panic!("can only hash u64");
    }

    fn write_u64(&mut self, u: u64) {
        self.id = u;
    }

    fn finish(&self) -> u64 {
        self.id
    }
}

impl<T: Send + 'static> FlowLocal<T> {
    /// Access this flow-local.
    ///
    /// This function will access this flow-local key to retrieve the data
    /// associated with the current flow and this key. If this is the first time
    /// this key has been accessed on this flow, then the key will be
    /// initialized with the initialization expression provided at the time the
    /// `flow_local!` macro was called.
    pub fn get(&self) -> FlowBox<T> {
        let key = (self.__key)();

        if let Some(rv) = ExecutionContext::get_local_value(key) {
            return unsafe { mem::transmute(rv) };
        };

        let arc = Arc::new(Box::new((self.__init)()));
        ExecutionContext::set_local_value(key, unsafe {
            mem::forget(arc.clone());
            mem::transmute(arc.clone())
        });
        FlowBox(arc)
    }

    /// Sets a new value for the flow-local.
    pub fn set(&self, value: T) {
        let key = (self.__key)();
        let arc = Arc::new(Box::new(value));
        ExecutionContext::set_local_value(key, unsafe {
            mem::forget(arc.clone());
            mem::transmute(arc.clone())
        });
    }
}

impl<T: fmt::Display + ?Sized> fmt::Display for FlowBox<T> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        fmt::Display::fmt(&**self, f)
    }
}

impl<T: fmt::Debug + ?Sized> fmt::Debug for FlowBox<T> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        fmt::Debug::fmt(&**self, f)
    }
}

impl<T: ?Sized> fmt::Pointer for FlowBox<T> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        // It's not possible to extract the inner Uniq directly from the FlowBox,
        // instead we cast it to a *const which aliases the Unique
        let ptr: *const T = &**self;
        fmt::Pointer::fmt(&ptr, f)
    }
}

impl<T: ?Sized> Deref for FlowBox<T> {
    type Target = T;

    fn deref(&self) -> &T {
        &*self.0
    }
}