55 * file, You can obtain one at https://mozilla.org/MPL/2.0/.
66 */
77
8- #! [ allow ( dead_code ) ] // FIXME
9-
8+ use crate :: builtin :: meta :: ClassName ;
9+ use crate :: builtin :: StringName ;
1010use crate :: init:: InitLevel ;
1111use crate :: log;
1212use crate :: obj:: * ;
13+ use crate :: out;
1314use crate :: private:: as_storage;
1415use crate :: storage:: InstanceStorage ;
1516use godot_ffi as sys;
1617
17- use sys:: interface_fn;
18+ use sys:: { interface_fn, Global , GlobalGuard , GlobalLockError } ;
1819
19- use crate :: builtin:: meta:: ClassName ;
20- use crate :: builtin:: StringName ;
21- use crate :: out;
2220use std:: any:: Any ;
2321use std:: collections:: HashMap ;
24- use std:: sync:: { Mutex , MutexGuard , TryLockError } ;
2522use std:: { fmt, ptr} ;
2623
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 ( ) ;
3329
3430// TODO(bromeon): some information coming from the proc-macro API is deferred through PluginComponent, while others is directly
3531// translated to code. Consider moving more code to the PluginComponent, which allows for more dynamic registration and will
@@ -182,6 +178,7 @@ struct ClassRegistrationInfo {
182178 godot_params : sys:: GDExtensionClassCreationInfo ,
183179 #[ cfg( since_api = "4.2" ) ]
184180 godot_params : sys:: GDExtensionClassCreationInfo2 ,
181+ #[ allow( dead_code) ] // Currently unused; may be useful for diagnostics in the future.
185182 init_level : InitLevel ,
186183 is_editor_plugin : bool ,
187184}
@@ -276,9 +273,7 @@ pub fn auto_register_classes(init_level: InitLevel) {
276273 fill_class_info ( elem. component . clone ( ) , class_info) ;
277274 } ) ;
278275
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 ( ) ;
282277 for info in map. into_values ( ) {
283278 out ! (
284279 "Register class: {} at level `{init_level:?}`" ,
@@ -298,26 +293,25 @@ pub fn auto_register_classes(init_level: InitLevel) {
298293}
299294
300295pub 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 ( ) ;
303297 let loaded_classes_current_level = loaded_classes_by_level
304298 . remove ( & init_level)
305299 . unwrap_or_default ( ) ;
306300 out ! ( "Unregistering classes of level {init_level:?}..." ) ;
307301 for class_name in loaded_classes_current_level. iter ( ) . rev ( ) {
308- unregister_class_raw ( class_name) ;
302+ unregister_class_raw ( * class_name) ;
309303 }
310304}
311305
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 > > > {
314307 match LOADED_CLASSES . try_lock ( ) {
315308 Ok ( it) => it,
316309 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"
319312 ) ,
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" ) ,
321315 } ,
322316 }
323317}
@@ -494,7 +488,7 @@ fn register_class_raw(mut info: ClassRegistrationInfo) {
494488 assert ! ( !info. is_editor_plugin) ;
495489}
496490
497- fn unregister_class_raw ( class_name : & ClassName ) {
491+ fn unregister_class_raw ( class_name : ClassName ) {
498492 out ! ( "Unregister class: {class_name}" ) ;
499493 unsafe {
500494 #[ allow( clippy:: let_unit_value) ]
0 commit comments