10
10
//! If used from different threads then there will be runtime errors in debug mode and UB in release mode.
11
11
12
12
use std:: cell:: Cell ;
13
+
14
+ #[ cfg( any(
15
+ not( target_family = "wasm" ) ,
16
+ not( feature = "experimental-wasm-nothreads" )
17
+ ) ) ]
13
18
use std:: thread:: ThreadId ;
14
19
15
20
use super :: GodotBinding ;
16
21
use crate :: ManualInitCell ;
17
22
18
23
pub ( super ) struct BindingStorage {
24
+ // No threading when linking against Godot with a nothreads Wasm build.
25
+ // Therefore, we just need to check if the bindings were initialized, as all accesses are from the main thread.
26
+ #[ cfg( all( target_family = "wasm" , feature = "experimental-wasm-nothreads" ) ) ]
27
+ initialized : Cell < bool > ,
28
+
19
29
// Is used in to check that we've been called from the right thread, so must be thread-safe to access.
30
+ #[ cfg( any(
31
+ not( target_family = "wasm" ) ,
32
+ not( feature = "experimental-wasm-nothreads" )
33
+ ) ) ]
20
34
main_thread_id : Cell < Option < ThreadId > > ,
21
35
binding : ManualInitCell < GodotBinding > ,
22
36
}
@@ -30,6 +44,13 @@ impl BindingStorage {
30
44
#[ inline( always) ]
31
45
unsafe fn storage ( ) -> & ' static Self {
32
46
static BINDING : BindingStorage = BindingStorage {
47
+ #[ cfg( all( target_family = "wasm" , feature = "experimental-wasm-nothreads" ) ) ]
48
+ initialized : false ,
49
+
50
+ #[ cfg( any(
51
+ not( target_family = "wasm" ) ,
52
+ not( feature = "experimental-wasm-nothreads" )
53
+ ) ) ]
33
54
main_thread_id : Cell :: new ( None ) ,
34
55
binding : ManualInitCell :: new ( ) ,
35
56
} ;
@@ -49,9 +70,24 @@ impl BindingStorage {
49
70
// in which case we can tell that the storage has been initialized, and we don't access `binding`.
50
71
let storage = unsafe { Self :: storage ( ) } ;
51
72
52
- storage
53
- . main_thread_id
54
- . set ( Some ( std:: thread:: current ( ) . id ( ) ) ) ;
73
+ // 'std::thread::current()' fails when linking to a Godot build without threads. When this feature is enabled,
74
+ // we assume it is impossible to have multi-threading, so checking if we are in the main thread is not needed.
75
+ // Therefore, we assign a bogus value for the thread ID instead, so we can still check for prior initialization
76
+ // through this field later.
77
+ #[ cfg( all( target_family = "wasm" , feature = "experimental-wasm-nothreads" ) ) ]
78
+ {
79
+ storage. initialized . set ( true ) ;
80
+ }
81
+
82
+ #[ cfg( any(
83
+ not( target_family = "wasm" ) ,
84
+ not( feature = "experimental-wasm-nothreads" )
85
+ ) ) ]
86
+ {
87
+ storage
88
+ . main_thread_id
89
+ . set ( Some ( std:: thread:: current ( ) . id ( ) ) ) ;
90
+ }
55
91
56
92
// SAFETY: We are the first thread to set this binding (possibly after deinitialize), as otherwise the above set() would fail and
57
93
// return early. We also know initialize() is not called concurrently with anything else that can call another method on the binding,
@@ -70,6 +106,15 @@ impl BindingStorage {
70
106
// SAFETY: We only call this once no other operations happen anymore, i.e. no other access to the binding.
71
107
let storage = unsafe { Self :: storage ( ) } ;
72
108
109
+ #[ cfg( all( target_family = "wasm" , feature = "experimental-wasm-nothreads" ) ) ]
110
+ if !storage. initialized . get ( ) {
111
+ panic ! ( "deinitialize without prior initialize" ) ;
112
+ }
113
+
114
+ #[ cfg( any(
115
+ not( target_family = "wasm" ) ,
116
+ not( feature = "experimental-wasm-nothreads" )
117
+ ) ) ]
73
118
storage
74
119
. main_thread_id
75
120
. get ( )
@@ -92,7 +137,16 @@ impl BindingStorage {
92
137
pub unsafe fn get_binding_unchecked ( ) -> & ' static GodotBinding {
93
138
let storage = Self :: storage ( ) ;
94
139
95
- if cfg ! ( debug_assertions) {
140
+ // We only check if we are in the main thread in debug builds if we aren't building for a non-threaded Godot build,
141
+ // since we could otherwise assume there won't be multi-threading.
142
+ #[ cfg( all(
143
+ debug_assertions,
144
+ any(
145
+ not( target_family = "wasm" ) ,
146
+ not( feature = "experimental-wasm-nothreads" )
147
+ )
148
+ ) ) ]
149
+ {
96
150
let main_thread_id = storage. main_thread_id . get ( ) . expect (
97
151
"Godot engine not available; make sure you are not calling it from unit/doc tests" ,
98
152
) ;
@@ -111,6 +165,14 @@ impl BindingStorage {
111
165
pub fn is_initialized ( ) -> bool {
112
166
// SAFETY: We don't access the binding.
113
167
let storage = unsafe { Self :: storage ( ) } ;
168
+
169
+ #[ cfg( all( target_family = "wasm" , feature = "experimental-wasm-nothreads" ) ) ]
170
+ return storage. initialized . get ( ) ;
171
+
172
+ #[ cfg( any(
173
+ not( target_family = "wasm" ) ,
174
+ not( feature = "experimental-wasm-nothreads" )
175
+ ) ) ]
114
176
storage. main_thread_id . get ( ) . is_some ( )
115
177
}
116
178
}
0 commit comments