5
5
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
6
6
*/
7
7
8
- #! [ allow ( dead_code ) ] // FIXME
9
-
8
+ use crate :: builtin :: meta :: ClassName ;
9
+ use crate :: builtin :: StringName ;
10
10
use crate :: init:: InitLevel ;
11
11
use crate :: log;
12
12
use crate :: obj:: * ;
13
+ use crate :: out;
13
14
use crate :: private:: as_storage;
14
15
use crate :: storage:: InstanceStorage ;
15
16
use godot_ffi as sys;
16
17
17
- use sys:: interface_fn;
18
+ use sys:: { interface_fn, Global , GlobalGuard , GlobalLockError } ;
18
19
19
- use crate :: builtin:: meta:: ClassName ;
20
- use crate :: builtin:: StringName ;
21
- use crate :: out;
22
20
use std:: any:: Any ;
23
21
use std:: collections:: HashMap ;
24
- use std:: sync:: { Mutex , MutexGuard , TryLockError } ;
25
22
use std:: { fmt, ptr} ;
26
23
27
- // For now, that variable is needed for class unregistering. It's populated during class
28
- // registering. There is no actual concurrency here, because Godot call register/unregister in main
29
- // thread - Mutex is just casual way to ensure safety in this performance non-critical path.
30
- // Note that we panic on concurrent access instead of blocking - that's fail fast approach. If that
31
- // happen, most likely something changed on Godot side and analysis required to adopt these changes.
32
- static LOADED_CLASSES : Mutex < Option < HashMap < InitLevel , Vec < ClassName > > > > = Mutex :: new ( None ) ;
24
+ // Needed for class unregistering. The variable is populated during class registering. There is no actual concurrency here, because Godot
25
+ // calls register/unregister in the main thread. Mutex is just casual way to ensure safety in this non-performance-critical path.
26
+ // Note that we panic on concurrent access instead of blocking (fail-fast approach). If that happens, most likely something changed on Godot
27
+ // side and analysis required to adopt these changes.
28
+ static LOADED_CLASSES : Global < HashMap < InitLevel , Vec < ClassName > > > = Global :: default ( ) ;
33
29
34
30
// TODO(bromeon): some information coming from the proc-macro API is deferred through PluginComponent, while others is directly
35
31
// translated to code. Consider moving more code to the PluginComponent, which allows for more dynamic registration and will
@@ -182,6 +178,7 @@ struct ClassRegistrationInfo {
182
178
godot_params : sys:: GDExtensionClassCreationInfo ,
183
179
#[ cfg( since_api = "4.2" ) ]
184
180
godot_params : sys:: GDExtensionClassCreationInfo2 ,
181
+ #[ allow( dead_code) ] // Currently unused; may be useful for diagnostics in the future.
185
182
init_level : InitLevel ,
186
183
is_editor_plugin : bool ,
187
184
}
@@ -276,9 +273,7 @@ pub fn auto_register_classes(init_level: InitLevel) {
276
273
fill_class_info ( elem. component . clone ( ) , class_info) ;
277
274
} ) ;
278
275
279
- let mut loaded_classes_guard = get_loaded_classes_with_mutex ( ) ;
280
- let loaded_classes_by_level = loaded_classes_guard. get_or_insert_with ( HashMap :: default) ;
281
-
276
+ let mut loaded_classes_by_level = global_loaded_classes ( ) ;
282
277
for info in map. into_values ( ) {
283
278
out ! (
284
279
"Register class: {} at level `{init_level:?}`" ,
@@ -298,26 +293,25 @@ pub fn auto_register_classes(init_level: InitLevel) {
298
293
}
299
294
300
295
pub fn unregister_classes ( init_level : InitLevel ) {
301
- let mut loaded_classes_guard = get_loaded_classes_with_mutex ( ) ;
302
- let loaded_classes_by_level = loaded_classes_guard. get_or_insert_with ( HashMap :: default) ;
296
+ let mut loaded_classes_by_level = global_loaded_classes ( ) ;
303
297
let loaded_classes_current_level = loaded_classes_by_level
304
298
. remove ( & init_level)
305
299
. unwrap_or_default ( ) ;
306
300
out ! ( "Unregistering classes of level {init_level:?}..." ) ;
307
301
for class_name in loaded_classes_current_level. iter ( ) . rev ( ) {
308
- unregister_class_raw ( class_name) ;
302
+ unregister_class_raw ( * class_name) ;
309
303
}
310
304
}
311
305
312
- fn get_loaded_classes_with_mutex ( ) -> MutexGuard < ' static , Option < HashMap < InitLevel , Vec < ClassName > > > >
313
- {
306
+ fn global_loaded_classes ( ) -> GlobalGuard < ' static , HashMap < InitLevel , Vec < ClassName > > > {
314
307
match LOADED_CLASSES . try_lock ( ) {
315
308
Ok ( it) => it,
316
309
Err ( err) => match err {
317
- TryLockError :: Poisoned ( _err ) => panic ! (
318
- "LOADED_CLASSES poisoned. seems like class registration or deregistration panicked. "
310
+ GlobalLockError :: Poisoned { .. } => panic ! (
311
+ "global lock for loaded classes poisoned; class registration or deregistration may have panicked"
319
312
) ,
320
- TryLockError :: WouldBlock => panic ! ( "unexpected concurrent access detected to CLASSES" ) ,
313
+ GlobalLockError :: WouldBlock => panic ! ( "unexpected concurrent access to global lock for loaded classes" ) ,
314
+ GlobalLockError :: InitFailed => unreachable ! ( "global lock for loaded classes not initialized" ) ,
321
315
} ,
322
316
}
323
317
}
@@ -494,7 +488,7 @@ fn register_class_raw(mut info: ClassRegistrationInfo) {
494
488
assert ! ( !info. is_editor_plugin) ;
495
489
}
496
490
497
- fn unregister_class_raw ( class_name : & ClassName ) {
491
+ fn unregister_class_raw ( class_name : ClassName ) {
498
492
out ! ( "Unregister class: {class_name}" ) ;
499
493
unsafe {
500
494
#[ allow( clippy:: let_unit_value) ]
0 commit comments