7
7
8
8
use godot_ffi as sys;
9
9
10
- use crate :: builtin:: { inner, StringName , Variant , VariantArray } ;
10
+ use crate :: builtin:: { inner, GString , StringName , Variant , VariantArray } ;
11
11
use crate :: classes:: Object ;
12
- use crate :: meta:: { CallContext , GodotType , ToGodot } ;
12
+ use crate :: meta:: { AsArg , CallContext , GodotType , ToGodot } ;
13
13
use crate :: obj:: bounds:: DynMemory ;
14
14
use crate :: obj:: Bounds ;
15
15
use crate :: obj:: { Gd , GodotClass , InstanceId } ;
@@ -84,43 +84,75 @@ impl Callable {
84
84
}
85
85
}
86
86
87
- /// Create a callable from a Rust function or closure.
87
+ /// Create callable from **single-threaded** Rust function or closure.
88
88
///
89
89
/// `name` is used for the string representation of the closure, which helps debugging.
90
90
///
91
- /// Callables created through multiple `from_fn()` calls are never equal, even if they refer to the same function. If you want to use
92
- /// equality, either clone an existing `Callable` instance, or define your own `PartialEq` impl with [`Callable::from_custom`].
91
+ /// This constructor only allows the callable to be invoked from the same thread as creating it. If you need to invoke it from any thread,
92
+ /// use [`from_sync_fn`][Self::from_sync_fn] instead (requires crate feature `experimental-threads`; only enable if really needed).
93
+ #[ cfg( since_api = "4.2" ) ]
94
+ pub fn from_local_fn < F , S > ( name : S , rust_function : F ) -> Self
95
+ where
96
+ F : ' static + FnMut ( & [ & Variant ] ) -> Result < Variant , ( ) > ,
97
+ S : AsArg < GString > ,
98
+ {
99
+ meta:: arg_into_owned!( name) ;
100
+
101
+ Self :: from_fn_wrapper ( FnWrapper {
102
+ rust_function,
103
+ name,
104
+ thread_id : Some ( std:: thread:: current ( ) . id ( ) ) ,
105
+ } )
106
+ }
107
+
108
+ /// Create callable from **thread-safe** Rust function or closure.
109
+ ///
110
+ /// `name` is used for the string representation of the closure, which helps debugging.
111
+ ///
112
+ /// This constructor requires `Send` + `Sync` bound and allows the callable to be invoked from any thread. If you guarantee that you invoke
113
+ /// it from the same thread as creating it, use [`from_local_fn`][Self::from_local_fn] instead.
114
+ ///
115
+ /// Callables created through multiple `from_local_fn` or `from_sync_fn()` calls are never equal, even if they refer to the same function.
116
+ /// If you want to use equality, either clone an existing `Callable` instance, or define your own `PartialEq` impl with
117
+ /// [`Callable::from_custom`].
93
118
///
94
119
/// # Example
95
120
/// ```no_run
96
121
/// # use godot::prelude::*;
97
- /// let callable = Callable::from_fn ("sum", |args: &[&Variant]| {
122
+ /// let callable = Callable::from_sync_fn ("sum", |args: &[&Variant]| {
98
123
/// let sum: i32 = args.iter().map(|arg| arg.to::<i32>()).sum();
99
124
/// Ok(sum.to_variant())
100
125
/// });
101
126
/// ```
102
127
#[ cfg( since_api = "4.2" ) ]
103
- pub fn from_fn < F , S > ( name : S , rust_function : F ) -> Self
128
+ #[ cfg( feature = "experimental-threads" ) ]
129
+ pub fn from_sync_fn < F , S > ( name : S , rust_function : F ) -> Self
104
130
where
105
131
F : ' static + Send + Sync + FnMut ( & [ & Variant ] ) -> Result < Variant , ( ) > ,
106
- S : Into < crate :: builtin :: GString > ,
132
+ S : AsArg < GString > ,
107
133
{
108
- let userdata = CallableUserdata {
109
- inner : FnWrapper {
110
- rust_function,
111
- name : name. into ( ) ,
112
- } ,
113
- } ;
134
+ meta:: arg_into_owned!( name) ;
114
135
115
- let info = CallableCustomInfo {
116
- callable_userdata : Box :: into_raw ( Box :: new ( userdata) ) as * mut std:: ffi:: c_void ,
117
- call_func : Some ( rust_callable_call_fn :: < F > ) ,
118
- free_func : Some ( rust_callable_destroy :: < FnWrapper < F > > ) ,
119
- to_string_func : Some ( rust_callable_to_string_named :: < F > ) ,
120
- ..Self :: default_callable_custom_info ( )
121
- } ;
136
+ Self :: from_fn_wrapper ( FnWrapper {
137
+ rust_function,
138
+ name,
139
+ thread_id : None ,
140
+ } )
141
+ }
122
142
123
- Self :: from_custom_info ( info)
143
+ #[ deprecated = "Now split into from_local_fn (single-threaded) and from_sync_fn (multi-threaded)." ]
144
+ #[ cfg( since_api = "4.2" ) ]
145
+ pub fn from_fn < F , S > ( name : S , rust_function : F ) -> Self
146
+ where
147
+ F : ' static + Send + Sync + FnMut ( & [ & Variant ] ) -> Result < Variant , ( ) > ,
148
+ S : Into < GString > ,
149
+ {
150
+ // Do not call from_sync_fn() since that is feature-gated, but this isn't due to compatibility.
151
+ Self :: from_fn_wrapper ( FnWrapper {
152
+ rust_function,
153
+ name : name. into ( ) ,
154
+ thread_id : None ,
155
+ } )
124
156
}
125
157
126
158
/// Create a highly configurable callable from Rust.
@@ -146,6 +178,24 @@ impl Callable {
146
178
Self :: from_custom_info ( info)
147
179
}
148
180
181
+ #[ cfg( since_api = "4.2" ) ]
182
+ fn from_fn_wrapper < F > ( inner : FnWrapper < F > ) -> Self
183
+ where
184
+ F : FnMut ( & [ & Variant ] ) -> Result < Variant , ( ) > ,
185
+ {
186
+ let userdata = CallableUserdata { inner } ;
187
+
188
+ let info = CallableCustomInfo {
189
+ callable_userdata : Box :: into_raw ( Box :: new ( userdata) ) as * mut std:: ffi:: c_void ,
190
+ call_func : Some ( rust_callable_call_fn :: < F > ) ,
191
+ free_func : Some ( rust_callable_destroy :: < FnWrapper < F > > ) ,
192
+ to_string_func : Some ( rust_callable_to_string_named :: < F > ) ,
193
+ ..Self :: default_callable_custom_info ( )
194
+ } ;
195
+
196
+ Self :: from_custom_info ( info)
197
+ }
198
+
149
199
#[ cfg( since_api = "4.2" ) ]
150
200
fn from_custom_info ( mut info : CallableCustomInfo ) -> Callable {
151
201
// SAFETY: callable_custom_create() is a valid way of creating callables.
@@ -330,7 +380,7 @@ unsafe impl GodotFfi for Callable {
330
380
}
331
381
}
332
382
333
- crate :: meta:: impl_godot_as_self!( Callable ) ;
383
+ meta:: impl_godot_as_self!( Callable ) ;
334
384
335
385
impl fmt:: Debug for Callable {
336
386
fn fmt ( & self , f : & mut fmt:: Formatter < ' _ > ) -> fmt:: Result {
@@ -365,6 +415,7 @@ mod custom_callable {
365
415
use super :: * ;
366
416
use crate :: builtin:: GString ;
367
417
use std:: hash:: Hash ;
418
+ use std:: thread:: ThreadId ;
368
419
369
420
pub struct CallableUserdata < T > {
370
421
pub inner : T ,
@@ -380,8 +431,11 @@ mod custom_callable {
380
431
}
381
432
382
433
pub ( crate ) struct FnWrapper < F > {
383
- pub ( crate ) rust_function : F ,
384
- pub ( crate ) name : GString ,
434
+ pub ( super ) rust_function : F ,
435
+ pub ( super ) name : GString ,
436
+
437
+ /// `None` if the callable is multi-threaded ([`Callable::from_sync_fn`]).
438
+ pub ( super ) thread_id : Option < ThreadId > ,
385
439
}
386
440
387
441
/// Represents a custom callable object defined in Rust.
@@ -419,7 +473,7 @@ mod custom_callable {
419
473
// Get the RustCallable again inside closure so it doesn't have to be UnwindSafe.
420
474
let c: & mut C = CallableUserdata :: inner_from_raw ( callable_userdata) ;
421
475
let result = c. invoke ( arg_refs) ;
422
- crate :: meta:: varcall_return_checked ( result, r_return, r_error) ;
476
+ meta:: varcall_return_checked ( result, r_return, r_error) ;
423
477
Ok ( ( ) )
424
478
} ) ;
425
479
}
@@ -444,8 +498,21 @@ mod custom_callable {
444
498
crate :: private:: handle_varcall_panic ( & ctx, & mut * r_error, move || {
445
499
// Get the FnWrapper again inside closure so the FnMut doesn't have to be UnwindSafe.
446
500
let w: & mut FnWrapper < F > = CallableUserdata :: inner_from_raw ( callable_userdata) ;
501
+
502
+ if w. thread_id
503
+ . is_some_and ( |tid| tid != std:: thread:: current ( ) . id ( ) )
504
+ {
505
+ // NOTE: this panic is currently not propagated to the caller, but results in an error message and Nil return.
506
+ // See comments in itest callable_call() for details.
507
+ panic ! (
508
+ "Callable '{}' created with from_local_fn() must be called from the same thread it was created in.\n \
509
+ If you need to call it from any thread, use from_sync_fn() instead (requires `experimental-threads` feature).",
510
+ w. name
511
+ ) ;
512
+ }
513
+
447
514
let result = ( w. rust_function ) ( arg_refs) ;
448
- crate :: meta:: varcall_return_checked ( result, r_return, r_error) ;
515
+ meta:: varcall_return_checked ( result, r_return, r_error) ;
449
516
Ok ( ( ) )
450
517
} ) ;
451
518
}
0 commit comments