//! Various utilities used for other implementations use once_cell::sync::OnceCell; use std::any::Any; use std::mem::ManuallyDrop; use std::thread::{self, ThreadId}; use self::list::AppendList; /// A wrapper for user data, able to store any type, and correctly /// handling access from a wrong thread #[derive(Debug)] pub struct UserData { inner: OnceCell, } #[derive(Debug)] enum UserDataInner { ThreadSafe(Box), NonThreadSafe(Box>, ThreadId), } // UserData itself is always threadsafe, as it only gives access to its // content if it is send+sync or we are on the right thread unsafe impl Send for UserData {} unsafe impl Sync for UserData {} impl UserData { /// Create a new UserData instance pub const fn new() -> UserData { UserData { inner: OnceCell::new() } } /// Sets the UserData to a given value /// /// The provided closure is called to init the UserData, /// does nothing is the UserData had already been set. pub fn set T>(&self, f: F) { self.inner.get_or_init(|| { UserDataInner::NonThreadSafe(Box::new(ManuallyDrop::new(f())), thread::current().id()) }); } /// Sets the UserData to a given threadsafe value /// /// The provided closure is called to init the UserData, /// does nothing is the UserData had already been set. pub fn set_threadsafe T>(&self, f: F) { self.inner.get_or_init(|| UserDataInner::ThreadSafe(Box::new(f()))); } /// Attempt to access the wrapped user data /// /// Will return `None` if either: /// /// - The requested type `T` does not match the type used for construction /// - This `UserData` has been created using the non-threadsafe variant and access /// is attempted from an other thread than the one it was created on pub fn get(&self) -> Option<&T> { match self.inner.get() { Some(&UserDataInner::ThreadSafe(ref val)) => ::downcast_ref::(&**val), Some(&UserDataInner::NonThreadSafe(ref val, threadid)) => { // only give access if we are on the right thread if threadid == thread::current().id() { ::downcast_ref::(&***val) } else { None } } None => None, } } } impl Drop for UserData { fn drop(&mut self) { // only drop non-Send user data if we are on the right thread, leak it otherwise if let Some(&mut UserDataInner::NonThreadSafe(ref mut val, threadid)) = self.inner.get_mut() { if threadid == thread::current().id() { unsafe { ManuallyDrop::drop(&mut **val); } } } } } /// A storage able to store several values of `UserData` /// of different types. It behaves similarly to a `TypeMap`. #[derive(Debug)] pub struct UserDataMap { list: AppendList, } impl UserDataMap { /// Create a new map pub fn new() -> UserDataMap { UserDataMap { list: AppendList::new() } } /// Attempt to access the wrapped user data of a given type /// /// Will return `None` if no value of type `T` is stored in this `UserDataMap` /// and accessible from this thread pub fn get(&self) -> Option<&T> { for user_data in &self.list { if let Some(val) = user_data.get::() { return Some(val); } } None } /// Insert a value in the map if it is not already there /// /// This is the non-threadsafe variant, the type you insert don't have to be /// threadsafe, but they will not be visible from other threads (even if they are /// actually threadsafe). /// /// If the value does not already exists, the closure is called to create it and /// this function returns `true`. If the value already exists, the closure is not /// called, and this function returns `false`. pub fn insert_if_missing T>(&self, init: F) -> bool { if self.get::().is_some() { return false; } let data = UserData::new(); data.set(init); self.list.append(data); true } /// Insert a value in the map if it is not already there /// /// This is the threadsafe variant, the type you insert must be threadsafe and will /// be visible from all threads. /// /// If the value does not already exists, the closure is called to create it and /// this function returns `true`. If the value already exists, the closure is not /// called, and this function returns `false`. pub fn insert_if_missing_threadsafe T>( &self, init: F, ) -> bool { if self.get::().is_some() { return false; } let data = UserData::new(); data.set_threadsafe(init); self.list.append(data); true } } impl Default for UserDataMap { fn default() -> UserDataMap { UserDataMap::new() } } mod list { /* * This is a lock-free append-only list, it is used as an implementation * detail of the UserDataMap. * * It was extracted from https://github.com/Diggsey/lockless under MIT license * Copyright © Diggory Blake */ use std::sync::atomic::{AtomicPtr, Ordering}; use std::{mem, ptr}; type NodePtr = Option>>; #[derive(Debug)] struct Node { value: T, next: AppendList, } #[derive(Debug)] pub struct AppendList(AtomicPtr>); impl AppendList { fn node_into_raw(ptr: NodePtr) -> *mut Node { match ptr { Some(b) => Box::into_raw(b), None => ptr::null_mut(), } } unsafe fn node_from_raw(ptr: *mut Node) -> NodePtr { if ptr.is_null() { None } else { Some(Box::from_raw(ptr)) } } fn new_internal(ptr: NodePtr) -> Self { AppendList(AtomicPtr::new(Self::node_into_raw(ptr))) } pub fn new() -> Self { Self::new_internal(None) } pub fn append(&self, value: T) { self.append_list(AppendList::new_internal(Some(Box::new(Node { value, next: AppendList::new(), })))); } unsafe fn append_ptr(&self, p: *mut Node) { loop { match self.0.compare_exchange_weak( ptr::null_mut(), p, Ordering::AcqRel, Ordering::Acquire, ) { Ok(_) => return, Err(head) => { if !head.is_null() { return (*head).next.append_ptr(p); } } } } } pub fn append_list(&self, other: AppendList) { let p = other.0.load(Ordering::Acquire); mem::forget(other); unsafe { self.append_ptr(p) }; } pub fn iter(&self) -> AppendListIterator { AppendListIterator(&self.0) } pub fn iter_mut(&mut self) -> AppendListMutIterator { AppendListMutIterator(&mut self.0) } } impl<'a, T> IntoIterator for &'a AppendList { type Item = &'a T; type IntoIter = AppendListIterator<'a, T>; fn into_iter(self) -> AppendListIterator<'a, T> { self.iter() } } impl<'a, T> IntoIterator for &'a mut AppendList { type Item = &'a mut T; type IntoIter = AppendListMutIterator<'a, T>; fn into_iter(self) -> AppendListMutIterator<'a, T> { self.iter_mut() } } impl Drop for AppendList { fn drop(&mut self) { unsafe { Self::node_from_raw(mem::replace(self.0.get_mut(), ptr::null_mut())) }; } } #[derive(Debug)] pub struct AppendListIterator<'a, T: 'a>(&'a AtomicPtr>); impl<'a, T: 'a> Iterator for AppendListIterator<'a, T> { type Item = &'a T; fn next(&mut self) -> Option<&'a T> { let p = self.0.load(Ordering::Acquire); if p.is_null() { None } else { unsafe { self.0 = &(*p).next.0; Some(&(*p).value) } } } } #[derive(Debug)] pub struct AppendListMutIterator<'a, T: 'a>(&'a mut AtomicPtr>); impl<'a, T: 'a> Iterator for AppendListMutIterator<'a, T> { type Item = &'a mut T; fn next(&mut self) -> Option<&'a mut T> { let p = self.0.load(Ordering::Acquire); if p.is_null() { None } else { unsafe { self.0 = &mut (*p).next.0; Some(&mut (*p).value) } } } } } #[cfg(test)] mod tests { use super::UserDataMap; #[test] fn insert_twice() { let map = UserDataMap::new(); assert_eq!(map.get::(), None); assert!(map.insert_if_missing(|| 42usize)); assert!(!map.insert_if_missing(|| 43usize)); assert_eq!(map.get::(), Some(&42)); } }