diff --git a/crates/tauri/src/ipc/authority.rs b/crates/tauri/src/ipc/authority.rs index 8bb81e398f26..b6fd8490a3d1 100644 --- a/crates/tauri/src/ipc/authority.rs +++ b/crates/tauri/src/ipc/authority.rs @@ -742,7 +742,7 @@ impl ScopeManager { key: &str, ) -> crate::Result> { match self.global_scope_cache.try_get::>() { - Some(cached) => Ok(cached.inner().clone()), + Some(cached) => Ok(cached.clone()), None => { let mut allow = Vec::new(); let mut deny = Vec::new(); @@ -779,7 +779,7 @@ impl ScopeManager { ) -> crate::Result> { let cache = self.command_cache.get(key).unwrap(); match cache.try_get::>() { - Some(cached) => Ok(cached.inner().clone()), + Some(cached) => Ok(cached.clone()), None => { let resolved_scope = self .command_scope diff --git a/crates/tauri/src/lib.rs b/crates/tauri/src/lib.rs index 38cbbc3a58ec..6c91762ee251 100644 --- a/crates/tauri/src/lib.rs +++ b/crates/tauri/src/lib.rs @@ -663,7 +663,7 @@ pub trait Manager: sealed::ManagerBase { /// /// #[tauri::command] /// fn string_command<'r>(state: State<'r, MyString>) { - /// println!("state: {}", state.inner().0); + /// println!("state: {}", state.0); /// } /// /// tauri::Builder::default() @@ -753,13 +753,14 @@ pub trait Manager: sealed::ManagerBase { /// Gets the managed [`Env`]. fn env(&self) -> Env { - self.state::().inner().clone() + use std::ops::Deref; + self.state::().deref().clone() } /// Gets the scope for the asset protocol. #[cfg(feature = "protocol-asset")] fn asset_protocol_scope(&self) -> scope::fs::Scope { - self.state::().inner().asset_protocol.clone() + self.state::().asset_protocol.clone() } /// The path resolver. diff --git a/crates/tauri/src/state.rs b/crates/tauri/src/state.rs index 74e6651c6ebb..54409c2135a4 100644 --- a/crates/tauri/src/state.rs +++ b/crates/tauri/src/state.rs @@ -6,7 +6,8 @@ use std::{ any::{Any, TypeId}, collections::HashMap, hash::BuildHasherDefault, - pin::Pin, + marker::PhantomData, + sync::Arc, sync::Mutex, }; @@ -18,46 +19,51 @@ use crate::{ /// A guard for a state value. /// /// See [`Manager::manage`](`crate::Manager::manage`) for usage examples. -pub struct State<'r, T: Send + Sync + 'static>(&'r T); +#[derive(Clone)] +pub struct State<'r, T> { + _life: PhantomData<&'r T>, + t: Arc, +} -impl<'r, T: Send + Sync + 'static> State<'r, T> { +impl<'r, T: 'static> State<'r, T> { /// Retrieve a borrow to the underlying value with a lifetime of `'r`. /// Using this method is typically unnecessary as `State` implements /// [`std::ops::Deref`] with a [`std::ops::Deref::Target`] of `T`. + #[doc(hidden)] #[inline(always)] pub fn inner(&self) -> &'r T { - self.0 + let x: &(dyn Any + Send + Sync) = &*self.t; + // SAFETY: everything returned from this function must no longer live when fn unmanage() is called + unsafe { + &*(x + .downcast_ref::() + .expect("the type of the key should be same as the type of the value") as *const T) + } } } -impl std::ops::Deref for State<'_, T> { +impl std::ops::Deref for State<'_, T> { type Target = T; #[inline(always)] fn deref(&self) -> &T { - self.0 + self.inner() } } -impl Clone for State<'_, T> { - fn clone(&self) -> Self { - State(self.0) - } -} - -impl PartialEq for State<'_, T> { +impl PartialEq for State<'_, T> { fn eq(&self, other: &Self) -> bool { - self.0 == other.0 + self.t.downcast_ref::() == other.t.downcast_ref::() } } -impl std::fmt::Debug for State<'_, T> { +impl 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() + f.debug_tuple("State").field(&self.t).finish() } } -impl<'r, 'de: 'r, T: Send + Sync + 'static, R: Runtime> CommandArg<'de, R> for State<'r, T> { +impl<'r, 'de: 'r, T: 'static, R: Runtime> CommandArg<'de, R> for State<'r, T> { /// Grabs the [`State`] from the [`CommandItem`]. This will never fail. fn from_command(command: CommandItem<'de, R>) -> Result { command.message.state_ref().try_get().ok_or_else(|| { @@ -98,9 +104,8 @@ impl std::hash::Hasher for IdentHash { } /// Safety: -/// - The `key` must equal to `(*value).type_id()`, see the safety doc in methods of [StateManager] for details. /// - Once you insert a value, you can't remove/mutated/move it anymore, see [StateManager::try_get] for details. -type TypeIdMap = HashMap>, BuildHasherDefault>; +type TypeIdMap = HashMap, BuildHasherDefault>; /// The Tauri state manager. #[derive(Debug)] @@ -120,14 +125,8 @@ impl StateManager { let type_id = TypeId::of::(); let already_set = map.contains_key(&type_id); if !already_set { - let ptr = Box::new(state) as Box; - let pinned_ptr = Box::into_pin(ptr); - map.insert( - type_id, - // SAFETY: keep the type of the key is the same as the type of the value, - // see [try_get] methods for details. - pinned_ptr, - ); + let state = Arc::new(state) as Arc; + map.insert(type_id, state); } !already_set } @@ -137,39 +136,29 @@ impl StateManager { pub(crate) unsafe fn unmanage(&self) -> Option { let mut map = self.map.lock().unwrap(); let type_id = TypeId::of::(); - let pinned_ptr = map.remove(&type_id)?; - // SAFETY: The caller decides to break the immovability/safety here, then OK, just let it go. - let ptr = unsafe { Pin::into_inner_unchecked(pinned_ptr) }; - let value = unsafe { - ptr - .downcast::() - // SAFETY: the type of the key is the same as the type of the value - .unwrap_unchecked() - }; - Some(*value) + let state = map.remove(&type_id)?; + let value = state + .downcast::() + .expect("the type of the key should be same as the type of the value"); + Arc::into_inner(value) } /// Gets the state associated with the specified type. - pub fn get(&self) -> State<'_, T> { + pub fn get(&self) -> State<'_, T> { self .try_get() .unwrap_or_else(|| panic!("state not found for type {}", std::any::type_name::())) } /// Gets the state associated with the specified type. - pub fn try_get(&self) -> Option> { + pub fn try_get(&self) -> Option> { let map = self.map.lock().unwrap(); let type_id = TypeId::of::(); - let ptr = map.get(&type_id)?; - let value = unsafe { - ptr - .downcast_ref::() - // SAFETY: the type of the key is the same as the type of the value - .unwrap_unchecked() - }; - // SAFETY: We ensure the lifetime of `value` is the same as [StateManager] and `value` will not be mutated/moved. - let v_ref = unsafe { &*(value as *const T) }; - Some(State(v_ref)) + let state = map.get(&type_id)?; + Some(State { + _life: PhantomData, + t: state.clone(), + }) } } @@ -302,4 +291,13 @@ mod tests { assert!(*drop_flag_a.read().unwrap()); assert!(*drop_flag_b.read().unwrap()); } + #[test] + fn t_sound_unmanage() { + let state = StateManager::new(); + state.set(0u8); + let void: super::State<'_, u8> = state.get::(); + let _r: u8 = *void; + unsafe { state.unmanage::() }; + drop(void); + } } diff --git a/examples/api/src-tauri/tauri-plugin-sample/src/lib.rs b/examples/api/src-tauri/tauri-plugin-sample/src/lib.rs index 894b3481f7e1..2177403aff7f 100644 --- a/examples/api/src-tauri/tauri-plugin-sample/src/lib.rs +++ b/examples/api/src-tauri/tauri-plugin-sample/src/lib.rs @@ -6,7 +6,7 @@ use serde::Deserialize; use std::path::PathBuf; use tauri::{ plugin::{Builder, TauriPlugin}, - Manager, Runtime, + Manager, Runtime, State, }; pub use models::*; @@ -28,12 +28,12 @@ pub use error::*; /// Extensions to [`tauri::App`], [`tauri::AppHandle`] and [`tauri::Window`] to access the sample APIs. pub trait SampleExt { - fn sample(&self) -> &Sample; + fn sample(&self) -> State<'_, Sample>; } impl> crate::SampleExt for T { - fn sample(&self) -> &Sample { - self.state::>().inner() + fn sample(&self) -> State<'_, Sample> { + self.state::>() } } diff --git a/examples/commands/commands.rs b/examples/commands/commands.rs index 93691c59dca1..5e1525b994b2 100644 --- a/examples/commands/commands.rs +++ b/examples/commands/commands.rs @@ -23,5 +23,5 @@ pub fn simple_command(the_argument: String) { #[command] pub fn stateful_command(the_argument: Option, state: State<'_, super::MyState>) { - println!("{:?} {:?}", the_argument, state.inner()); + println!("{:?} {:?}", the_argument, *state); } diff --git a/examples/commands/main.rs b/examples/commands/main.rs index aa0641de093b..e1e541293547 100644 --- a/examples/commands/main.rs +++ b/examples/commands/main.rs @@ -51,7 +51,7 @@ async fn async_stateful_command( the_argument: Option, state: State<'_, MyState>, ) -> Result<(), ()> { - println!("{:?} {:?}", the_argument, state.inner()); + println!("{:?} {:?}", the_argument, *state); Ok(()) } // ------------------------ Raw future commands ------------------------ @@ -141,7 +141,7 @@ fn stateful_command_with_result( the_argument: Option, state: State<'_, MyState>, ) -> Result { - println!("{:?} {:?}", the_argument, state.inner()); + println!("{:?} {:?}", the_argument, *state); dbg!(the_argument.ok_or(MyError::FooError)) } @@ -160,7 +160,7 @@ fn stateful_command_with_result_snake( the_argument: Option, state: State<'_, MyState>, ) -> Result { - println!("{:?} {:?}", the_argument, state.inner()); + println!("{:?} {:?}", the_argument, *state); dbg!(the_argument.ok_or(MyError::FooError)) } @@ -177,7 +177,7 @@ async fn async_stateful_command_with_result( the_argument: Option, state: State<'_, MyState>, ) -> Result { - println!("{:?} {:?}", the_argument, state.inner()); + println!("{:?} {:?}", the_argument, *state); Ok(the_argument.unwrap_or_default()) }