1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
use std::sync::Arc;
use std::any::TypeId;
use std::collections::HashMap;
use std::cell::{RefCell, UnsafeCell};
use std::hash::{BuildHasherDefault, Hasher};
use join::Join;
use coroutine_impl::Coroutine;
use generator::get_local_data;

// thread local map storage
thread_local!{static LOCALMAP: LocalMap = RefCell::new(HashMap::default());}

/// coroutine local storage
pub struct CoroutineLocal {
    // current coroutine handle
    co: Coroutine,
    // when panic happens, we need to trigger the join here
    join: Arc<UnsafeCell<Join>>,
    // real local data hash map
    local_data: LocalMap,
}

impl CoroutineLocal {
    /// create coroutine local storage
    pub fn new(co: Coroutine, join: Arc<UnsafeCell<Join>>) -> Box<Self> {
        Box::new(CoroutineLocal {
            co: co,
            join: join,
            local_data: RefCell::new(HashMap::default()),
        })
    }

    // get the coroutine handle
    pub fn get_co(&self) -> &Coroutine {
        &self.co
    }

    // get the join handle
    pub fn get_join(&self) -> Arc<UnsafeCell<Join>> {
        self.join.clone()
    }
}

fn with<F: FnOnce(&LocalMap) -> R, R>(f: F) -> R {
    let ptr = get_local_data();
    if ptr.is_null() {
        LOCALMAP.with(|data| f(data))
    } else {
        let local = unsafe { &*(ptr as *mut CoroutineLocal) };
        let data = &local.local_data;
        f(data)
    }
}

pub type LocalMap = RefCell<HashMap<TypeId, Box<Opaque>, BuildHasherDefault<IdHasher>>>;

pub trait Opaque {}
impl<T> Opaque for T {}

/// A key for local data stored in a coroutine.
///
/// This type is generated by the `coroutine_local!` macro and performs very
/// similarly to the `thread_local!` macro and `std::thread::LocalKey` types.
/// Data associated with a `LocalKey<T>` is stored inside of a coroutine,
/// and the data is destroyed when the coroutine is completed.
///
/// coroutine-local data requires the `'static` bound to ensure it lives long
/// enough. When a key is accessed for the first time the coroutine's data is
/// initialized with the provided initialization expression to the macro.
pub struct LocalKey<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,
}

pub 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: 'static> LocalKey<T> {
    /// Access this coroutine-local key, running the provided closure with a
    /// reference to the value.
    ///
    /// This function will access this coroutine-local key to retrieve the data
    /// associated with the current coroutine and this key. If this is the first
    /// time this key has been accessed on this coroutine, then the key will be
    /// initialized with the initialization expression provided at the time the
    /// `coroutine_local!` macro was called.
    ///
    /// The provided closure will be provided a shared reference to the
    /// underlying data associated with this coroutine-local-key. The data itself
    /// is stored inside of the current coroutine.
    ///
    /// if it's not accessed in a coroutine context, it will use the thread local
    /// storage as a backend, so it's safe to use it in thread context
    ///
    /// # Panics
    ///
    /// This function can possibly panic for a number of reasons:
    ///
    /// * If the initialization expression is run and it panics
    /// * If the closure provided panics
    pub fn with<F, R>(&'static self, f: F) -> R
    where
        F: FnOnce(&T) -> R,
    {
        let key = (self.__key)();
        with(|data| {
            let raw_pointer = {
                let mut data = data.borrow_mut();
                let entry = data.entry(key).or_insert_with(|| Box::new((self.__init)()));
                &**entry as *const Opaque as *const T
            };
            unsafe { f(&*raw_pointer) }
        })
    }
}