4
4
5
5
use std:: {
6
6
any:: { Any , TypeId } ,
7
- cell:: UnsafeCell ,
8
7
collections:: HashMap ,
9
8
hash:: BuildHasherDefault ,
9
+ pin:: Pin ,
10
10
sync:: Mutex ,
11
11
} ;
12
12
@@ -97,56 +97,56 @@ impl std::hash::Hasher for IdentHash {
97
97
}
98
98
}
99
99
100
- type TypeIdMap = HashMap < TypeId , Box < dyn Any > , BuildHasherDefault < IdentHash > > ;
100
+ /// Safety:
101
+ /// - The `key` must equal to `(*value).type_id()`, see the safety doc in methods of [StateManager] for details.
102
+ /// - Once you insert a value, you can't remove/mutated/move it anymore, see [StateManager::try_get] for details.
103
+ type TypeIdMap = HashMap < TypeId , Pin < Box < dyn Any + Sync + Send > > , BuildHasherDefault < IdentHash > > ;
101
104
102
105
/// The Tauri state manager.
103
106
#[ derive( Debug ) ]
104
107
pub struct StateManager {
105
- map : Mutex < UnsafeCell < TypeIdMap > > ,
108
+ map : Mutex < TypeIdMap > ,
106
109
}
107
110
108
- // SAFETY: data is accessed behind a lock
109
- unsafe impl Sync for StateManager { }
110
- unsafe impl Send for StateManager { }
111
-
112
111
impl StateManager {
113
112
pub ( crate ) fn new ( ) -> Self {
114
113
Self {
115
114
map : Default :: default ( ) ,
116
115
}
117
116
}
118
117
119
- fn with_map_ref < ' a , F : FnOnce ( & ' a TypeIdMap ) -> R , R > ( & ' a self , f : F ) -> R {
120
- let map = self . map . lock ( ) . unwrap ( ) ;
121
- let map = map. get ( ) ;
122
- // SAFETY: safe to access since we are holding a lock
123
- f ( unsafe { & * map } )
124
- }
125
-
126
- fn with_map_mut < F : FnOnce ( & mut TypeIdMap ) -> R , R > ( & self , f : F ) -> R {
127
- let mut map = self . map . lock ( ) . unwrap ( ) ;
128
- let map = map. get_mut ( ) ;
129
- f ( map)
130
- }
131
-
132
118
pub ( crate ) fn set < T : Send + Sync + ' static > ( & self , state : T ) -> bool {
133
- self . with_map_mut ( |map| {
134
- let type_id = TypeId :: of :: < T > ( ) ;
135
- let already_set = map. contains_key ( & type_id) ;
136
- if !already_set {
137
- map. insert ( type_id, Box :: new ( state) as Box < dyn Any > ) ;
138
- }
139
- !already_set
140
- } )
119
+ let mut map = self . map . lock ( ) . unwrap ( ) ;
120
+ let type_id = TypeId :: of :: < T > ( ) ;
121
+ let already_set = map. contains_key ( & type_id) ;
122
+ if !already_set {
123
+ let ptr = Box :: new ( state) as Box < dyn Any + Sync + Send > ;
124
+ let pinned_ptr = Box :: into_pin ( ptr) ;
125
+ map. insert (
126
+ type_id,
127
+ // SAFETY: keep the type of the key is the same as the type of the value,
128
+ // see [try_get] methods for details.
129
+ pinned_ptr,
130
+ ) ;
131
+ }
132
+ !already_set
141
133
}
142
134
143
- pub ( crate ) fn unmanage < T : Send + Sync + ' static > ( & self ) -> Option < T > {
144
- self . with_map_mut ( |map| {
145
- let type_id = TypeId :: of :: < T > ( ) ;
146
- map
147
- . remove ( & type_id)
148
- . and_then ( |ptr| ptr. downcast ( ) . ok ( ) . map ( |b| * b) )
149
- } )
135
+ /// SAFETY: Calling this method will move the `value`,
136
+ /// which will cause references obtained through [Self::try_get] to dangle.
137
+ pub ( crate ) unsafe fn unmanage < T : Send + Sync + ' static > ( & self ) -> Option < T > {
138
+ let mut map = self . map . lock ( ) . unwrap ( ) ;
139
+ let type_id = TypeId :: of :: < T > ( ) ;
140
+ let pinned_ptr = map. remove ( & type_id) ?;
141
+ // SAFETY: The caller decides to break the immovability/safety here, then OK, just let it go.
142
+ let ptr = unsafe { Pin :: into_inner_unchecked ( pinned_ptr) } ;
143
+ let value = unsafe {
144
+ ptr
145
+ . downcast :: < T > ( )
146
+ // SAFETY: the type of the key is the same as the type of the value
147
+ . unwrap_unchecked ( )
148
+ } ;
149
+ Some ( * value)
150
150
}
151
151
152
152
/// Gets the state associated with the specified type.
@@ -158,12 +158,18 @@ impl StateManager {
158
158
159
159
/// Gets the state associated with the specified type.
160
160
pub fn try_get < T : Send + Sync + ' static > ( & self ) -> Option < State < ' _ , T > > {
161
- self . with_map_ref ( |map| {
162
- map
163
- . get ( & TypeId :: of :: < T > ( ) )
164
- . and_then ( |ptr| ptr. downcast_ref :: < T > ( ) )
165
- . map ( State )
166
- } )
161
+ let map = self . map . lock ( ) . unwrap ( ) ;
162
+ let type_id = TypeId :: of :: < T > ( ) ;
163
+ let ptr = map. get ( & type_id) ?;
164
+ let value = unsafe {
165
+ ptr
166
+ . downcast_ref :: < T > ( )
167
+ // SAFETY: the type of the key is the same as the type of the value
168
+ . unwrap_unchecked ( )
169
+ } ;
170
+ // SAFETY: We ensure the lifetime of `value` is the same as [StateManager] and `value` will not be mutated/moved.
171
+ let v_ref = unsafe { & * ( value as * const T ) } ;
172
+ Some ( State ( v_ref) )
167
173
}
168
174
}
169
175
@@ -197,8 +203,9 @@ mod tests {
197
203
let state = StateManager :: new ( ) ;
198
204
assert ! ( state. set( 1u32 ) ) ;
199
205
assert_eq ! ( * state. get:: <u32 >( ) , 1 ) ;
200
- assert ! ( state. unmanage:: <u32 >( ) . is_some( ) ) ;
201
- assert ! ( state. unmanage:: <u32 >( ) . is_none( ) ) ;
206
+ // safety: the reference returned by `try_get` is already dropped.
207
+ assert ! ( unsafe { state. unmanage:: <u32 >( ) } . is_some( ) ) ;
208
+ assert ! ( unsafe { state. unmanage:: <u32 >( ) } . is_none( ) ) ;
202
209
assert_eq ! ( state. try_get:: <u32 >( ) , None ) ;
203
210
assert ! ( state. set( 2u32 ) ) ;
204
211
assert_eq ! ( * state. get:: <u32 >( ) , 2 ) ;
0 commit comments