use std::{
any::{Any, TypeId},
collections::HashMap,
hash::BuildHasherDefault,
pin::Pin,
sync::Mutex,
};
use crate::{
ipc::{CommandArg, CommandItem, InvokeError},
Runtime,
};
pub struct State<'r, T: Send + Sync + 'static>(&'r T);
impl<'r, T: Send + Sync + 'static> State<'r, T> {
#[inline(always)]
pub fn inner(&self) -> &'r T {
self.0
}
}
impl<T: Send + Sync + 'static> std::ops::Deref for State<'_, T> {
type Target = T;
#[inline(always)]
fn deref(&self) -> &T {
self.0
}
}
impl<T: Send + Sync + 'static> Clone for State<'_, T> {
fn clone(&self) -> Self {
State(self.0)
}
}
impl<T: Send + Sync + 'static + PartialEq> PartialEq for State<'_, T> {
fn eq(&self, other: &Self) -> bool {
self.0 == other.0
}
}
impl<T: Send + Sync + std::fmt::Debug> std::fmt::Debug for State<'_, T> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_tuple("State").field(&self.0).finish()
}
}
impl<'r, 'de: 'r, T: Send + Sync + 'static, R: Runtime> CommandArg<'de, R> for State<'r, T> {
fn from_command(command: CommandItem<'de, R>) -> Result<Self, InvokeError> {
command.message.state_ref().try_get().ok_or_else(|| {
InvokeError::from_anyhow(anyhow::anyhow!(
"state not managed for field `{}` on command `{}`. You must call `.manage()` before using this command",
command.key, command.name
))
})
}
}
#[derive(Default)]
struct IdentHash(u64);
impl std::hash::Hasher for IdentHash {
fn finish(&self) -> u64 {
self.0
}
fn write(&mut self, bytes: &[u8]) {
for byte in bytes {
self.write_u8(*byte);
}
}
fn write_u8(&mut self, i: u8) {
self.0 = (self.0 << 8) | (i as u64);
}
fn write_u64(&mut self, i: u64) {
self.0 = i;
}
}
type TypeIdMap = HashMap<TypeId, Pin<Box<dyn Any + Sync + Send>>, BuildHasherDefault<IdentHash>>;
#[derive(Debug)]
pub struct StateManager {
map: Mutex<TypeIdMap>,
}
impl StateManager {
pub(crate) fn new() -> Self {
Self {
map: Default::default(),
}
}
pub(crate) fn set<T: Send + Sync + 'static>(&self, state: T) -> bool {
let mut map = self.map.lock().unwrap();
let type_id = TypeId::of::<T>();
let already_set = map.contains_key(&type_id);
if !already_set {
let ptr = Box::new(state) as Box<dyn Any + Sync + Send>;
let pinned_ptr = Box::into_pin(ptr);
map.insert(
type_id,
pinned_ptr,
);
}
!already_set
}
pub(crate) unsafe fn unmanage<T: Send + Sync + 'static>(&self) -> Option<T> {
let mut map = self.map.lock().unwrap();
let type_id = TypeId::of::<T>();
let pinned_ptr = map.remove(&type_id)?;
let ptr = unsafe { Pin::into_inner_unchecked(pinned_ptr) };
let value = unsafe {
ptr
.downcast::<T>()
.unwrap_unchecked()
};
Some(*value)
}
pub fn get<T: Send + Sync + 'static>(&self) -> State<'_, T> {
self
.try_get()
.unwrap_or_else(|| panic!("state not found for type {}", std::any::type_name::<T>()))
}
pub fn try_get<T: Send + Sync + 'static>(&self) -> Option<State<'_, T>> {
let map = self.map.lock().unwrap();
let type_id = TypeId::of::<T>();
let ptr = map.get(&type_id)?;
let value = unsafe {
ptr
.downcast_ref::<T>()
.unwrap_unchecked()
};
let v_ref = unsafe { &*(value as *const T) };
Some(State(v_ref))
}
}
#[cfg(test)]
mod tests {
use super::StateManager;
use std::sync::{Arc, RwLock};
use std::thread;
struct DroppingStruct(Arc<RwLock<bool>>);
struct DroppingStructWrap(#[allow(dead_code)] DroppingStruct);
impl Drop for DroppingStruct {
fn drop(&mut self) {
*self.0.write().unwrap() = true;
}
}
#[test]
#[should_panic(expected = "state not found for type core::option::Option<alloc::string::String>")]
fn get_panics() {
let state = StateManager::new();
state.get::<Option<String>>();
}
#[test]
fn simple_set_get() {
let state = StateManager::new();
assert!(state.set(1u32));
assert_eq!(*state.get::<u32>(), 1);
}
#[test]
fn simple_set_get_unmanage() {
let state = StateManager::new();
assert!(state.set(1u32));
assert_eq!(*state.get::<u32>(), 1);
assert!(unsafe { state.unmanage::<u32>() }.is_some());
assert!(unsafe { state.unmanage::<u32>() }.is_none());
assert_eq!(state.try_get::<u32>(), None);
assert!(state.set(2u32));
assert_eq!(*state.get::<u32>(), 2);
}
#[test]
fn dst_set_get() {
let state = StateManager::new();
assert!(state.set::<[u32; 4]>([1, 2, 3, 4u32]));
assert_eq!(*state.get::<[u32; 4]>(), [1, 2, 3, 4]);
}
#[test]
fn set_get_remote() {
let state = Arc::new(StateManager::new());
let sate_ = Arc::clone(&state);
thread::spawn(move || {
sate_.set(10isize);
})
.join()
.unwrap();
assert_eq!(*state.get::<isize>(), 10);
}
#[test]
fn two_put_get() {
let state = StateManager::new();
assert!(state.set("Hello, world!".to_string()));
let s_old = state.get::<String>();
assert_eq!(*s_old, "Hello, world!");
assert!(!state.set::<String>("Bye bye!".into()));
assert_eq!(*state.get::<String>(), "Hello, world!");
assert_eq!(state.get::<String>(), s_old);
}
#[test]
fn many_puts_only_one_succeeds() {
let state = Arc::new(StateManager::new());
let mut threads = vec![];
for _ in 0..1000 {
let state_ = Arc::clone(&state);
threads.push(thread::spawn(move || state_.set(10i64)))
}
let results: Vec<bool> = threads.into_iter().map(|t| t.join().unwrap()).collect();
assert_eq!(results.into_iter().filter(|&b| b).count(), 1);
assert_eq!(*state.get::<i64>(), 10);
}
#[test]
fn test_no_drop_on_set() {
let state = StateManager::new();
let drop_flag = Arc::new(RwLock::new(false));
let dropping_struct = DroppingStruct(drop_flag.clone());
let _drop_flag_ignore = Arc::new(RwLock::new(false));
let _dropping_struct_ignore = DroppingStruct(_drop_flag_ignore);
state.set::<DroppingStruct>(dropping_struct);
assert!(!state.set::<DroppingStruct>(_dropping_struct_ignore));
assert!(!*drop_flag.read().unwrap());
}
#[test]
fn drop_inners_on_drop() {
let drop_flag_a = Arc::new(RwLock::new(false));
let dropping_struct_a = DroppingStruct(drop_flag_a.clone());
let drop_flag_b = Arc::new(RwLock::new(false));
let dropping_struct_b = DroppingStructWrap(DroppingStruct(drop_flag_b.clone()));
{
let state = StateManager::new();
state.set(dropping_struct_a);
assert!(!*drop_flag_a.read().unwrap());
state.set(dropping_struct_b);
assert!(!*drop_flag_a.read().unwrap());
assert!(!*drop_flag_b.read().unwrap());
}
assert!(*drop_flag_a.read().unwrap());
assert!(*drop_flag_b.read().unwrap());
}
}