@@ -2,19 +2,61 @@ use crate::archetype::ArchetypeComponentId;
2
2
use crate :: component:: { ComponentId , ComponentTicks , Components , TickCells } ;
3
3
use crate :: storage:: { Column , SparseSet , TableRow } ;
4
4
use bevy_ptr:: { OwningPtr , Ptr , UnsafeCellDeref } ;
5
+ use std:: { mem:: ManuallyDrop , thread:: ThreadId } ;
5
6
6
7
/// The type-erased backing storage and metadata for a single resource within a [`World`].
7
8
///
9
+ /// If `SEND` is false, values of this type will panic if dropped from a different thread.
10
+ ///
8
11
/// [`World`]: crate::world::World
9
- pub struct ResourceData {
10
- column : Column ,
12
+ pub struct ResourceData < const SEND : bool > {
13
+ column : ManuallyDrop < Column > ,
14
+ type_name : String ,
11
15
id : ArchetypeComponentId ,
16
+ origin_thread_id : Option < ThreadId > ,
17
+ }
18
+
19
+ impl < const SEND : bool > Drop for ResourceData < SEND > {
20
+ fn drop ( & mut self ) {
21
+ if self . is_present ( ) {
22
+ // If this thread is already panicking, panicking again will cause
23
+ // the entire process to abort. In this case we choose to avoid
24
+ // dropping or checking this altogether and just leak the column.
25
+ if std:: thread:: panicking ( ) {
26
+ return ;
27
+ }
28
+ self . validate_access ( ) ;
29
+ }
30
+ // SAFETY: Drop is only called once upon dropping the ResourceData
31
+ // and is inaccessible after this as the parent ResourceData has
32
+ // been dropped. The validate_access call above will check that the
33
+ // data is dropped on the thread it was inserted from.
34
+ unsafe {
35
+ ManuallyDrop :: drop ( & mut self . column ) ;
36
+ }
37
+ }
12
38
}
13
39
14
- impl ResourceData {
40
+ impl < const SEND : bool > ResourceData < SEND > {
15
41
/// The only row in the underlying column.
16
42
const ROW : TableRow = TableRow :: new ( 0 ) ;
17
43
44
+ #[ inline]
45
+ fn validate_access ( & self ) {
46
+ if SEND {
47
+ return ;
48
+ }
49
+ if self . origin_thread_id != Some ( std:: thread:: current ( ) . id ( ) ) {
50
+ // Panic in tests, as testing for aborting is nearly impossible
51
+ panic ! (
52
+ "Attempted to access or drop non-send resource {} from thread {:?} on a thread {:?}. This is not allowed. Aborting." ,
53
+ self . type_name,
54
+ self . origin_thread_id,
55
+ std:: thread:: current( ) . id( )
56
+ ) ;
57
+ }
58
+ }
59
+
18
60
/// Returns true if the resource is populated.
19
61
#[ inline]
20
62
pub fn is_present ( & self ) -> bool {
@@ -28,9 +70,16 @@ impl ResourceData {
28
70
}
29
71
30
72
/// Gets a read-only pointer to the underlying resource, if available.
73
+ ///
74
+ /// # Panics
75
+ /// If `SEND` is false, this will panic if a value is present and is not accessed from the
76
+ /// original thread it was inserted from.
31
77
#[ inline]
32
78
pub fn get_data ( & self ) -> Option < Ptr < ' _ > > {
33
- self . column . get_data ( Self :: ROW )
79
+ self . column . get_data ( Self :: ROW ) . map ( |res| {
80
+ self . validate_access ( ) ;
81
+ res
82
+ } )
34
83
}
35
84
36
85
/// Gets a read-only reference to the change ticks of the underlying resource, if available.
@@ -39,83 +88,98 @@ impl ResourceData {
39
88
self . column . get_ticks ( Self :: ROW )
40
89
}
41
90
91
+ /// # Panics
92
+ /// If `SEND` is false, this will panic if a value is present and is not accessed from the
93
+ /// original thread it was inserted in.
42
94
#[ inline]
43
95
pub ( crate ) fn get_with_ticks ( & self ) -> Option < ( Ptr < ' _ > , TickCells < ' _ > ) > {
44
- self . column . get ( Self :: ROW )
96
+ self . column . get ( Self :: ROW ) . map ( |res| {
97
+ self . validate_access ( ) ;
98
+ res
99
+ } )
45
100
}
46
101
47
102
/// Inserts a value into the resource. If a value is already present
48
103
/// it will be replaced.
49
104
///
50
- /// # Safety
51
- /// `value` must be valid for the underlying type for the resource.
52
- ///
53
- /// The underlying type must be [`Send`] or be inserted from the main thread.
54
- /// This can be validated with [`World::validate_non_send_access_untyped`].
105
+ /// # Panics
106
+ /// If `SEND` is false, this will panic if a value is present and is not replaced from
107
+ /// the original thread it was inserted in.
55
108
///
56
- /// [`World::validate_non_send_access_untyped`]: crate::world::World::validate_non_send_access_untyped
109
+ /// # Safety
110
+ /// - `value` must be valid for the underlying type for the resource.
57
111
#[ inline]
58
112
pub ( crate ) unsafe fn insert ( & mut self , value : OwningPtr < ' _ > , change_tick : u32 ) {
59
113
if self . is_present ( ) {
114
+ self . validate_access ( ) ;
60
115
self . column . replace ( Self :: ROW , value, change_tick) ;
61
116
} else {
117
+ if !SEND {
118
+ self . origin_thread_id = Some ( std:: thread:: current ( ) . id ( ) ) ;
119
+ }
62
120
self . column . push ( value, ComponentTicks :: new ( change_tick) ) ;
63
121
}
64
122
}
65
123
66
124
/// Inserts a value into the resource with a pre-existing change tick. If a
67
125
/// value is already present it will be replaced.
68
126
///
69
- /// # Safety
70
- /// `value` must be valid for the underlying type for the resource.
71
- ///
72
- /// The underlying type must be [`Send`] or be inserted from the main thread.
73
- /// This can be validated with [`World::validate_non_send_access_untyped`].
127
+ /// # Panics
128
+ /// If `SEND` is false, this will panic if a value is present and is not replaced from
129
+ /// the original thread it was inserted in.
74
130
///
75
- /// [`World::validate_non_send_access_untyped`]: crate::world::World::validate_non_send_access_untyped
131
+ /// # Safety
132
+ /// - `value` must be valid for the underlying type for the resource.
76
133
#[ inline]
77
134
pub ( crate ) unsafe fn insert_with_ticks (
78
135
& mut self ,
79
136
value : OwningPtr < ' _ > ,
80
137
change_ticks : ComponentTicks ,
81
138
) {
82
139
if self . is_present ( ) {
140
+ self . validate_access ( ) ;
83
141
self . column . replace_untracked ( Self :: ROW , value) ;
84
142
* self . column . get_added_ticks_unchecked ( Self :: ROW ) . deref_mut ( ) = change_ticks. added ;
85
143
* self
86
144
. column
87
145
. get_changed_ticks_unchecked ( Self :: ROW )
88
146
. deref_mut ( ) = change_ticks. changed ;
89
147
} else {
148
+ if !SEND {
149
+ self . origin_thread_id = Some ( std:: thread:: current ( ) . id ( ) ) ;
150
+ }
90
151
self . column . push ( value, change_ticks) ;
91
152
}
92
153
}
93
154
94
155
/// Removes a value from the resource, if present.
95
156
///
96
- /// # Safety
97
- /// The underlying type must be [`Send`] or be removed from the main thread.
98
- /// This can be validated with [`World::validate_non_send_access_untyped`].
99
- ///
100
- /// The removed value must be used or dropped.
101
- ///
102
- /// [`World::validate_non_send_access_untyped`]: crate::world::World::validate_non_send_access_untyped
157
+ /// # Panics
158
+ /// If `SEND` is false, this will panic if a value is present and is not removed from the
159
+ /// original thread it was inserted from.
103
160
#[ inline]
104
161
#[ must_use = "The returned pointer to the removed component should be used or dropped" ]
105
- pub ( crate ) unsafe fn remove ( & mut self ) -> Option < ( OwningPtr < ' _ > , ComponentTicks ) > {
106
- self . column . swap_remove_and_forget ( Self :: ROW )
162
+ pub ( crate ) fn remove ( & mut self ) -> Option < ( OwningPtr < ' _ > , ComponentTicks ) > {
163
+ if SEND {
164
+ self . column . swap_remove_and_forget ( Self :: ROW )
165
+ } else {
166
+ self . is_present ( )
167
+ . then ( || self . validate_access ( ) )
168
+ . and_then ( |_| self . column . swap_remove_and_forget ( Self :: ROW ) )
169
+ }
107
170
}
108
171
109
172
/// Removes a value from the resource, if present, and drops it.
110
173
///
111
- /// # Safety
112
- /// The underlying type must be [`Send`] or be removed from the main thread.
113
- /// This can be validated with [`World::validate_non_send_access_untyped`].
114
- ///
115
- /// [`World::validate_non_send_access_untyped`]: crate::world::World::validate_non_send_access_untyped
174
+ /// # Panics
175
+ /// If `SEND` is false, this will panic if a value is present and is not
176
+ /// accessed from the original thread it was inserted in.
116
177
#[ inline]
117
- pub ( crate ) unsafe fn remove_and_drop ( & mut self ) {
118
- self . column . clear ( ) ;
178
+ pub ( crate ) fn remove_and_drop ( & mut self ) {
179
+ if self . is_present ( ) {
180
+ self . validate_access ( ) ;
181
+ self . column . clear ( ) ;
182
+ }
119
183
}
120
184
}
121
185
@@ -124,11 +188,11 @@ impl ResourceData {
124
188
/// [`Resource`]: crate::system::Resource
125
189
/// [`World`]: crate::world::World
126
190
#[ derive( Default ) ]
127
- pub struct Resources {
128
- resources : SparseSet < ComponentId , ResourceData > ,
191
+ pub struct Resources < const SEND : bool > {
192
+ resources : SparseSet < ComponentId , ResourceData < SEND > > ,
129
193
}
130
194
131
- impl Resources {
195
+ impl < const SEND : bool > Resources < SEND > {
132
196
/// The total number of resources stored in the [`World`]
133
197
///
134
198
/// [`World`]: crate::world::World
@@ -138,7 +202,7 @@ impl Resources {
138
202
}
139
203
140
204
/// Iterate over all resources that have been initialized, i.e. given a [`ComponentId`]
141
- pub fn iter ( & self ) -> impl Iterator < Item = ( ComponentId , & ResourceData ) > {
205
+ pub fn iter ( & self ) -> impl Iterator < Item = ( ComponentId , & ResourceData < SEND > ) > {
142
206
self . resources . iter ( ) . map ( |( id, data) | ( * id, data) )
143
207
}
144
208
@@ -153,31 +217,37 @@ impl Resources {
153
217
154
218
/// Gets read-only access to a resource, if it exists.
155
219
#[ inline]
156
- pub fn get ( & self , component_id : ComponentId ) -> Option < & ResourceData > {
220
+ pub fn get ( & self , component_id : ComponentId ) -> Option < & ResourceData < SEND > > {
157
221
self . resources . get ( component_id)
158
222
}
159
223
160
224
/// Gets mutable access to a resource, if it exists.
161
225
#[ inline]
162
- pub ( crate ) fn get_mut ( & mut self , component_id : ComponentId ) -> Option < & mut ResourceData > {
226
+ pub ( crate ) fn get_mut ( & mut self , component_id : ComponentId ) -> Option < & mut ResourceData < SEND > > {
163
227
self . resources . get_mut ( component_id)
164
228
}
165
229
166
230
/// Fetches or initializes a new resource and returns back it's underlying column.
167
231
///
168
232
/// # Panics
169
233
/// Will panic if `component_id` is not valid for the provided `components`
234
+ /// If `SEND` is false, this will panic if `component_id`'s `ComponentInfo` is not registered as being `Send` + `Sync`.
170
235
pub ( crate ) fn initialize_with (
171
236
& mut self ,
172
237
component_id : ComponentId ,
173
238
components : & Components ,
174
239
f : impl FnOnce ( ) -> ArchetypeComponentId ,
175
- ) -> & mut ResourceData {
240
+ ) -> & mut ResourceData < SEND > {
176
241
self . resources . get_or_insert_with ( component_id, || {
177
242
let component_info = components. get_info ( component_id) . unwrap ( ) ;
243
+ if SEND {
244
+ assert ! ( component_info. is_send_and_sync( ) ) ;
245
+ }
178
246
ResourceData {
179
- column : Column :: with_capacity ( component_info, 1 ) ,
247
+ column : ManuallyDrop :: new ( Column :: with_capacity ( component_info, 1 ) ) ,
248
+ type_name : String :: from ( component_info. name ( ) ) ,
180
249
id : f ( ) ,
250
+ origin_thread_id : None ,
181
251
}
182
252
} )
183
253
}
0 commit comments