@@ -19,6 +19,9 @@ use crate::obj::bounds::DynMemory;
19
19
use crate :: obj:: { Bounds , Gd , GodotClass , InstanceId } ;
20
20
use sys:: { ffi_methods, GodotFfi } ;
21
21
22
+ #[ cfg( since_api = "4.2" ) ]
23
+ pub use futures:: * ;
24
+
22
25
/// A `Signal` represents a signal of an Object instance in Godot.
23
26
///
24
27
/// Signals are composed of a reference to an `Object` and the name of the signal on this object.
@@ -213,3 +216,310 @@ impl fmt::Display for Signal {
213
216
write ! ( f, "{}" , self . to_variant( ) )
214
217
}
215
218
}
219
+
220
+ // ----------------------------------------------------------------------------------------------------------------------------------------
221
+ // Implementation of a rust future for Godot Signals
222
+ #[ cfg( since_api = "4.2" ) ]
223
+ mod futures {
224
+ use std:: fmt:: Display ;
225
+ use std:: future:: Future ;
226
+ use std:: pin:: Pin ;
227
+ use std:: sync:: { Arc , Mutex } ;
228
+ use std:: task:: { Context , Poll , Waker } ;
229
+
230
+ use crate :: builtin:: { Callable , RustCallable , Variant } ;
231
+ use crate :: classes:: object:: ConnectFlags ;
232
+ use crate :: meta:: FromGodot ;
233
+ use crate :: obj:: EngineEnum ;
234
+
235
+ use super :: Signal ;
236
+
237
+ pub struct SignalFuture < R : FromSignalArgs > {
238
+ state : Arc < Mutex < ( Option < R > , Option < Waker > ) > > ,
239
+ callable : Callable ,
240
+ signal : Signal ,
241
+ }
242
+
243
+ impl < R : FromSignalArgs > SignalFuture < R > {
244
+ fn new ( signal : Signal ) -> Self {
245
+ let state = Arc :: new ( Mutex :: new ( ( None , Option :: < Waker > :: None ) ) ) ;
246
+ let callback_state = state. clone ( ) ;
247
+
248
+ // the callable currently requires that the return value is Sync + Send
249
+ let callable = Callable :: from_local_fn ( "async_task" , move |args : & [ & Variant ] | {
250
+ let mut lock = callback_state. lock ( ) . unwrap ( ) ;
251
+ let waker = lock. 1 . take ( ) ;
252
+
253
+ lock. 0 . replace ( R :: from_args ( args) ) ;
254
+ drop ( lock) ;
255
+
256
+ if let Some ( waker) = waker {
257
+ waker. wake ( ) ;
258
+ }
259
+
260
+ Ok ( Variant :: nil ( ) )
261
+ } ) ;
262
+
263
+ signal. connect ( & callable, ConnectFlags :: ONE_SHOT . ord ( ) as i64 ) ;
264
+
265
+ Self {
266
+ state,
267
+ callable,
268
+ signal,
269
+ }
270
+ }
271
+ }
272
+
273
+ impl < R : FromSignalArgs > Future for SignalFuture < R > {
274
+ type Output = R ;
275
+
276
+ fn poll ( self : Pin < & mut Self > , cx : & mut Context < ' _ > ) -> Poll < Self :: Output > {
277
+ let mut lock = self . state . lock ( ) . unwrap ( ) ;
278
+
279
+ if let Some ( result) = lock. 0 . take ( ) {
280
+ return Poll :: Ready ( result) ;
281
+ }
282
+
283
+ lock. 1 . replace ( cx. waker ( ) . clone ( ) ) ;
284
+
285
+ Poll :: Pending
286
+ }
287
+ }
288
+
289
+ impl < R : FromSignalArgs > Drop for SignalFuture < R > {
290
+ fn drop ( & mut self ) {
291
+ if !self . callable . is_valid ( ) {
292
+ return ;
293
+ }
294
+
295
+ if self . signal . object ( ) . is_none ( ) {
296
+ return ;
297
+ }
298
+
299
+ if self . signal . is_connected ( & self . callable ) {
300
+ self . signal . disconnect ( & self . callable ) ;
301
+ }
302
+ }
303
+ }
304
+
305
+ struct GuaranteedSignalFutureResolver < R > {
306
+ state : Arc < Mutex < ( GuaranteedSignalFutureState < R > , Option < Waker > ) > > ,
307
+ }
308
+
309
+ impl < R > Clone for GuaranteedSignalFutureResolver < R > {
310
+ fn clone ( & self ) -> Self {
311
+ Self {
312
+ state : self . state . clone ( ) ,
313
+ }
314
+ }
315
+ }
316
+
317
+ impl < R > GuaranteedSignalFutureResolver < R > {
318
+ fn new ( state : Arc < Mutex < ( GuaranteedSignalFutureState < R > , Option < Waker > ) > > ) -> Self {
319
+ Self { state }
320
+ }
321
+ }
322
+
323
+ impl < R > std:: hash:: Hash for GuaranteedSignalFutureResolver < R > {
324
+ fn hash < H : std:: hash:: Hasher > ( & self , state : & mut H ) {
325
+ state. write_usize ( Arc :: as_ptr ( & self . state ) as usize ) ;
326
+ }
327
+ }
328
+
329
+ impl < R > PartialEq for GuaranteedSignalFutureResolver < R > {
330
+ fn eq ( & self , other : & Self ) -> bool {
331
+ Arc :: ptr_eq ( & self . state , & other. state )
332
+ }
333
+ }
334
+
335
+ impl < R : FromSignalArgs > RustCallable for GuaranteedSignalFutureResolver < R > {
336
+ fn invoke ( & mut self , args : & [ & Variant ] ) -> Result < Variant , ( ) > {
337
+ let mut lock = self . state . lock ( ) . unwrap ( ) ;
338
+ let waker = lock. 1 . take ( ) ;
339
+
340
+ lock. 0 = GuaranteedSignalFutureState :: Ready ( R :: from_args ( args) ) ;
341
+ drop ( lock) ;
342
+
343
+ if let Some ( waker) = waker {
344
+ waker. wake ( ) ;
345
+ }
346
+
347
+ Ok ( Variant :: nil ( ) )
348
+ }
349
+ }
350
+
351
+ impl < R > Display for GuaranteedSignalFutureResolver < R > {
352
+ fn fmt ( & self , f : & mut std:: fmt:: Formatter < ' _ > ) -> std:: fmt:: Result {
353
+ write ! (
354
+ f,
355
+ "GuaranteedSignalFutureResolver::<{}>" ,
356
+ std:: any:: type_name:: <R >( )
357
+ )
358
+ }
359
+ }
360
+
361
+ // this resolver will resolve the future when it's being dropped (i.e. the engine removes all connected signal callables). This is very unusual.
362
+ impl < R > Drop for GuaranteedSignalFutureResolver < R > {
363
+ fn drop ( & mut self ) {
364
+ let mut lock = self . state . lock ( ) . unwrap ( ) ;
365
+
366
+ if !matches ! ( lock. 0 , GuaranteedSignalFutureState :: Pending ) {
367
+ return ;
368
+ }
369
+
370
+ lock. 0 = GuaranteedSignalFutureState :: Dead ;
371
+
372
+ if let Some ( ref waker) = lock. 1 {
373
+ waker. wake_by_ref ( ) ;
374
+ }
375
+ }
376
+ }
377
+
378
+ #[ derive( Default ) ]
379
+ enum GuaranteedSignalFutureState < T > {
380
+ #[ default]
381
+ Pending ,
382
+ Ready ( T ) ,
383
+ Dead ,
384
+ Dropped ,
385
+ }
386
+
387
+ impl < T > GuaranteedSignalFutureState < T > {
388
+ fn take ( & mut self ) -> Self {
389
+ let new_value = match self {
390
+ Self :: Pending => Self :: Pending ,
391
+ Self :: Ready ( _) | Self :: Dead => Self :: Dead ,
392
+ Self :: Dropped => Self :: Dropped ,
393
+ } ;
394
+
395
+ std:: mem:: replace ( self , new_value)
396
+ }
397
+ }
398
+
399
+ /// The guaranteed signal future will always resolve, but might resolve to `None` if the owning object is freed
400
+ /// before the signal is emitted.
401
+ ///
402
+ /// This is inconsistent with how awaiting signals in Godot work and how async works in rust. The behavior was requested as part of some
403
+ /// user feedback for the initial POC.
404
+ pub struct GuaranteedSignalFuture < R : FromSignalArgs > {
405
+ state : Arc < Mutex < ( GuaranteedSignalFutureState < R > , Option < Waker > ) > > ,
406
+ callable : GuaranteedSignalFutureResolver < R > ,
407
+ signal : Signal ,
408
+ }
409
+
410
+ impl < R : FromSignalArgs > GuaranteedSignalFuture < R > {
411
+ fn new ( signal : Signal ) -> Self {
412
+ let state = Arc :: new ( Mutex :: new ( (
413
+ GuaranteedSignalFutureState :: Pending ,
414
+ Option :: < Waker > :: None ,
415
+ ) ) ) ;
416
+
417
+ // the callable currently requires that the return value is Sync + Send
418
+ let callable = GuaranteedSignalFutureResolver :: new ( state. clone ( ) ) ;
419
+
420
+ signal. connect (
421
+ & Callable :: from_custom ( callable. clone ( ) ) ,
422
+ ConnectFlags :: ONE_SHOT . ord ( ) as i64 ,
423
+ ) ;
424
+
425
+ Self {
426
+ state,
427
+ callable,
428
+ signal,
429
+ }
430
+ }
431
+ }
432
+
433
+ impl < R : FromSignalArgs > Future for GuaranteedSignalFuture < R > {
434
+ type Output = Option < R > ;
435
+
436
+ fn poll ( self : Pin < & mut Self > , cx : & mut Context < ' _ > ) -> Poll < Self :: Output > {
437
+ let mut lock = self . state . lock ( ) . unwrap ( ) ;
438
+
439
+ lock. 1 . replace ( cx. waker ( ) . clone ( ) ) ;
440
+
441
+ let value = lock. 0 . take ( ) ;
442
+
443
+ match value {
444
+ GuaranteedSignalFutureState :: Pending => Poll :: Pending ,
445
+ GuaranteedSignalFutureState :: Dropped => unreachable ! ( ) ,
446
+ GuaranteedSignalFutureState :: Dead => Poll :: Ready ( None ) ,
447
+ GuaranteedSignalFutureState :: Ready ( value) => Poll :: Ready ( Some ( value) ) ,
448
+ }
449
+ }
450
+ }
451
+
452
+ impl < R : FromSignalArgs > Drop for GuaranteedSignalFuture < R > {
453
+ fn drop ( & mut self ) {
454
+ if self . signal . object ( ) . is_none ( ) {
455
+ return ;
456
+ }
457
+
458
+ self . state . lock ( ) . unwrap ( ) . 0 = GuaranteedSignalFutureState :: Dropped ;
459
+
460
+ let gd_callable = Callable :: from_custom ( self . callable . clone ( ) ) ;
461
+
462
+ if self . signal . is_connected ( & gd_callable) {
463
+ self . signal . disconnect ( & gd_callable) ;
464
+ }
465
+ }
466
+ }
467
+
468
+ pub trait FromSignalArgs : Sync + Send + ' static {
469
+ fn from_args ( args : & [ & Variant ] ) -> Self ;
470
+ }
471
+
472
+ impl < R : FromGodot + Sync + Send + ' static > FromSignalArgs for R {
473
+ fn from_args ( args : & [ & Variant ] ) -> Self {
474
+ args. first ( )
475
+ . map ( |arg| ( * arg) . to_owned ( ) )
476
+ . unwrap_or_default ( )
477
+ . to ( )
478
+ }
479
+ }
480
+
481
+ // more of these should be generated via macro to support more than two signal arguments
482
+ impl < R1 : FromGodot + Sync + Send + ' static , R2 : FromGodot + Sync + Send + ' static >
483
+ FromSignalArgs for ( R1 , R2 )
484
+ {
485
+ fn from_args ( args : & [ & Variant ] ) -> Self {
486
+ ( args[ 0 ] . to ( ) , args[ 0 ] . to ( ) )
487
+ }
488
+ }
489
+
490
+ impl Signal {
491
+ pub fn to_guaranteed_future < R : FromSignalArgs > ( & self ) -> GuaranteedSignalFuture < R > {
492
+ GuaranteedSignalFuture :: new ( self . clone ( ) )
493
+ }
494
+
495
+ pub fn to_future < R : FromSignalArgs > ( & self ) -> SignalFuture < R > {
496
+ SignalFuture :: new ( self . clone ( ) )
497
+ }
498
+ }
499
+
500
+ #[ cfg( test) ]
501
+ mod tests {
502
+ use std:: {
503
+ hash:: { DefaultHasher , Hash , Hasher } ,
504
+ sync:: Arc ,
505
+ } ;
506
+
507
+ use super :: GuaranteedSignalFutureResolver ;
508
+
509
+ #[ test]
510
+ fn guaranteed_future_waker_cloned_hash ( ) {
511
+ let waker_a = GuaranteedSignalFutureResolver :: < u8 > :: new ( Arc :: default ( ) ) ;
512
+ let waker_b = waker_a. clone ( ) ;
513
+
514
+ let mut hasher = DefaultHasher :: new ( ) ;
515
+ waker_a. hash ( & mut hasher) ;
516
+ let hash_a = hasher. finish ( ) ;
517
+
518
+ let mut hasher = DefaultHasher :: new ( ) ;
519
+ waker_b. hash ( & mut hasher) ;
520
+ let hash_b = hasher. finish ( ) ;
521
+
522
+ assert_eq ! ( hash_a, hash_b) ;
523
+ }
524
+ }
525
+ }
0 commit comments