Skip to content
4 changes: 2 additions & 2 deletions crates/tauri/src/ipc/authority.rs
Original file line number Diff line number Diff line change
Expand Up @@ -742,7 +742,7 @@ impl ScopeManager {
key: &str,
) -> crate::Result<ScopeValue<T>> {
match self.global_scope_cache.try_get::<ScopeValue<T>>() {
Some(cached) => Ok(cached.inner().clone()),
Some(cached) => Ok(cached.clone()),
None => {
let mut allow = Vec::new();
let mut deny = Vec::new();
Expand Down Expand Up @@ -779,7 +779,7 @@ impl ScopeManager {
) -> crate::Result<ScopeValue<T>> {
let cache = self.command_cache.get(key).unwrap();
match cache.try_get::<ScopeValue<T>>() {
Some(cached) => Ok(cached.inner().clone()),
Some(cached) => Ok(cached.clone()),
None => {
let resolved_scope = self
.command_scope
Expand Down
7 changes: 4 additions & 3 deletions crates/tauri/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -663,7 +663,7 @@ pub trait Manager<R: Runtime>: sealed::ManagerBase<R> {
///
/// #[tauri::command]
/// fn string_command<'r>(state: State<'r, MyString>) {
/// println!("state: {}", state.inner().0);
/// println!("state: {}", state.0);
/// }
///
/// tauri::Builder::default()
Expand Down Expand Up @@ -753,13 +753,14 @@ pub trait Manager<R: Runtime>: sealed::ManagerBase<R> {

/// Gets the managed [`Env`].
fn env(&self) -> Env {
self.state::<Env>().inner().clone()
use std::ops::Deref;
self.state::<Env>().deref().clone()
}

/// Gets the scope for the asset protocol.
#[cfg(feature = "protocol-asset")]
fn asset_protocol_scope(&self) -> scope::fs::Scope {
self.state::<Scopes>().inner().asset_protocol.clone()
self.state::<Scopes>().asset_protocol.clone()
}

/// The path resolver.
Expand Down
96 changes: 47 additions & 49 deletions crates/tauri/src/state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,8 @@ use std::{
any::{Any, TypeId},
collections::HashMap,
hash::BuildHasherDefault,
pin::Pin,
marker::PhantomData,
sync::Arc,
sync::Mutex,
};

Expand All @@ -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<dyn Any + Send + Sync>,
}

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::<T>()
.expect("the type of the key should be same as the type of the value") as *const T)
}
}
}

impl<T: Send + Sync + 'static> std::ops::Deref for State<'_, T> {
impl<T: 'static> std::ops::Deref for State<'_, T> {
type Target = T;

#[inline(always)]
fn deref(&self) -> &T {
self.0
self.inner()
}
}

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> {
impl<T: 'static + PartialEq> PartialEq for State<'_, T> {
fn eq(&self, other: &Self) -> bool {
self.0 == other.0
self.t.downcast_ref::<T>() == other.t.downcast_ref::<T>()
}
}

impl<T: Send + Sync + std::fmt::Debug> std::fmt::Debug for State<'_, T> {
impl<T: 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()
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<Self, InvokeError> {
command.message.state_ref().try_get().ok_or_else(|| {
Expand Down Expand Up @@ -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<TypeId, Pin<Box<dyn Any + Sync + Send>>, BuildHasherDefault<IdentHash>>;
type TypeIdMap = HashMap<TypeId, Arc<dyn Any + Sync + Send>, BuildHasherDefault<IdentHash>>;

/// The Tauri state manager.
#[derive(Debug)]
Expand All @@ -120,14 +125,8 @@ impl StateManager {
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,
// 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<dyn Any + Sync + Send>;
map.insert(type_id, state);
}
!already_set
}
Expand All @@ -137,39 +136,29 @@ impl StateManager {
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)?;
// 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::<T>()
// 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::<T>()
.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<T: Send + Sync + 'static>(&self) -> State<'_, T> {
pub fn get<T: 'static>(&self) -> State<'_, T> {
self
.try_get()
.unwrap_or_else(|| panic!("state not found for type {}", std::any::type_name::<T>()))
}

/// Gets the state associated with the specified type.
pub fn try_get<T: Send + Sync + 'static>(&self) -> Option<State<'_, T>> {
pub fn try_get<T: '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>()
// 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(),
})
}
}

Expand Down Expand Up @@ -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::<u8>();
let _r: u8 = *void;
unsafe { state.unmanage::<u8>() };
drop(void);
}
}
8 changes: 4 additions & 4 deletions examples/api/src-tauri/tauri-plugin-sample/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ use serde::Deserialize;
use std::path::PathBuf;
use tauri::{
plugin::{Builder, TauriPlugin},
Manager, Runtime,
Manager, Runtime, State,
};

pub use models::*;
Expand All @@ -28,12 +28,12 @@ pub use error::*;

/// Extensions to [`tauri::App`], [`tauri::AppHandle`] and [`tauri::Window`] to access the sample APIs.
pub trait SampleExt<R: Runtime> {
fn sample(&self) -> &Sample<R>;
fn sample(&self) -> State<'_, Sample<R>>;
}

impl<R: Runtime, T: Manager<R>> crate::SampleExt<R> for T {
fn sample(&self) -> &Sample<R> {
self.state::<Sample<R>>().inner()
fn sample(&self) -> State<'_, Sample<R>> {
self.state::<Sample<R>>()
}
}

Expand Down
2 changes: 1 addition & 1 deletion examples/commands/commands.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,5 +23,5 @@ pub fn simple_command(the_argument: String) {

#[command]
pub fn stateful_command(the_argument: Option<String>, state: State<'_, super::MyState>) {
println!("{:?} {:?}", the_argument, state.inner());
println!("{:?} {:?}", the_argument, *state);
}
8 changes: 4 additions & 4 deletions examples/commands/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ async fn async_stateful_command(
the_argument: Option<String>,
state: State<'_, MyState>,
) -> Result<(), ()> {
println!("{:?} {:?}", the_argument, state.inner());
println!("{:?} {:?}", the_argument, *state);
Ok(())
}
// ------------------------ Raw future commands ------------------------
Expand Down Expand Up @@ -141,7 +141,7 @@ fn stateful_command_with_result(
the_argument: Option<String>,
state: State<'_, MyState>,
) -> Result<String, MyError> {
println!("{:?} {:?}", the_argument, state.inner());
println!("{:?} {:?}", the_argument, *state);
dbg!(the_argument.ok_or(MyError::FooError))
}

Expand All @@ -160,7 +160,7 @@ fn stateful_command_with_result_snake(
the_argument: Option<String>,
state: State<'_, MyState>,
) -> Result<String, MyError> {
println!("{:?} {:?}", the_argument, state.inner());
println!("{:?} {:?}", the_argument, *state);
dbg!(the_argument.ok_or(MyError::FooError))
}

Expand All @@ -177,7 +177,7 @@ async fn async_stateful_command_with_result(
the_argument: Option<String>,
state: State<'_, MyState>,
) -> Result<String, MyError> {
println!("{:?} {:?}", the_argument, state.inner());
println!("{:?} {:?}", the_argument, *state);
Ok(the_argument.unwrap_or_default())
}

Expand Down