1use core::panic;
9use std::fmt::Display;
10use std::future::{Future, IntoFuture};
11use std::pin::Pin;
12use std::sync::{Arc, Mutex};
13use std::task::{Context, Poll, Waker};
14use std::thread::ThreadId;
15
16use crate::builtin::{Callable, RustCallable, Signal, Variant};
17use crate::classes::object::ConnectFlags;
18use crate::global::godot_error;
19use crate::meta::InParamTuple;
20use crate::meta::sealed::Sealed;
21use crate::obj::{Gd, GodotClass, WithSignals};
22use crate::signal::TypedSignal;
23use crate::sys;
24
25#[rustfmt::skip] pub(crate) use crate::impl_dynamic_send;
29
30pub struct SignalFuture<R: InParamTuple + IntoDynamicSend>(FallibleSignalFuture<R>);
49
50impl<R: InParamTuple + IntoDynamicSend> SignalFuture<R> {
51 fn new(signal: Signal) -> Self {
52 Self(FallibleSignalFuture::new(signal))
53 }
54}
55
56impl<R: InParamTuple + IntoDynamicSend> Future for SignalFuture<R> {
57 type Output = R;
58
59 fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
60 let poll_result = self.get_mut().0.poll(cx);
61
62 match poll_result {
63 Poll::Pending => Poll::Pending,
64 Poll::Ready(Ok(value)) => Poll::Ready(value),
65 Poll::Ready(Err(FallibleSignalFutureError)) if crate::task::is_engine_exiting() => {
69 Poll::Pending
70 }
71 Poll::Ready(Err(FallibleSignalFutureError)) => panic!(
72 "the signal object of a SignalFuture was freed, while the future was still waiting for the signal to be emitted"
73 ),
74 }
75 }
76}
77
78struct SignalFutureData<T> {
80 state: SignalFutureState<T>,
81 waker: Option<Waker>,
82}
83
84impl<T> Default for SignalFutureData<T> {
85 fn default() -> Self {
86 Self {
87 state: Default::default(),
88 waker: None,
89 }
90 }
91}
92
93pub struct SignalFutureResolver<R: IntoDynamicSend> {
95 data: Arc<Mutex<SignalFutureData<R::Target>>>,
96}
97
98impl<R: IntoDynamicSend> Clone for SignalFutureResolver<R> {
99 fn clone(&self) -> Self {
100 Self {
101 data: self.data.clone(),
102 }
103 }
104}
105
106#[cfg(feature = "itest")] #[cfg_attr(published_docs, doc(cfg(feature = "itest")))]
108pub fn create_test_signal_future_resolver<R: IntoDynamicSend>() -> SignalFutureResolver<R> {
109 SignalFutureResolver {
110 data: Arc::new(Mutex::new(SignalFutureData::default())),
111 }
112}
113
114impl<R: IntoDynamicSend> SignalFutureResolver<R> {
115 fn new(data: Arc<Mutex<SignalFutureData<R::Target>>>) -> Self {
116 Self { data }
117 }
118}
119
120impl<R: IntoDynamicSend> std::hash::Hash for SignalFutureResolver<R> {
121 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
122 state.write_usize(Arc::as_ptr(&self.data) as usize);
123 }
124}
125
126impl<R: IntoDynamicSend> PartialEq for SignalFutureResolver<R> {
127 fn eq(&self, other: &Self) -> bool {
128 Arc::ptr_eq(&self.data, &other.data)
129 }
130}
131
132impl<R: InParamTuple + IntoDynamicSend> RustCallable for SignalFutureResolver<R> {
133 fn invoke(&mut self, args: &[&Variant]) -> Variant {
134 let waker = {
135 let mut data = self.data.lock().unwrap();
136 data.state = SignalFutureState::Ready(R::from_variant_array(args).into_dynamic_send());
137
138 data.waker.take()
140 };
141
142 if let Some(waker) = waker {
143 waker.wake();
144 }
145
146 Variant::nil()
147 }
148}
149
150impl<R: IntoDynamicSend> Display for SignalFutureResolver<R> {
151 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
152 write!(f, "SignalFutureResolver::<{}>", std::any::type_name::<R>())
153 }
154}
155
156impl<R: IntoDynamicSend> Drop for SignalFutureResolver<R> {
159 fn drop(&mut self) {
160 let mut data = self.data.lock().unwrap();
161
162 if !matches!(data.state, SignalFutureState::Pending) {
163 return;
165 }
166
167 if crate::task::is_engine_exiting() {
170 return;
171 }
172
173 data.state = SignalFutureState::Dead;
175
176 if let Some(ref waker) = data.waker {
179 waker.wake_by_ref();
180 }
181 }
182}
183
184#[derive(Default)]
185enum SignalFutureState<T> {
186 #[default]
187 Pending,
188 Ready(T),
189 Dead,
190 Dropped,
191}
192
193impl<T> SignalFutureState<T> {
194 fn take(&mut self) -> Self {
195 let new_value = match self {
196 Self::Pending => Self::Pending,
197 Self::Ready(_) | Self::Dead => Self::Dead,
198 Self::Dropped => Self::Dropped,
199 };
200
201 std::mem::replace(self, new_value)
202 }
203}
204
205pub struct FallibleSignalFuture<R: InParamTuple + IntoDynamicSend> {
215 data: Arc<Mutex<SignalFutureData<R::Target>>>,
216 callable: SignalFutureResolver<R>,
217 signal: Signal,
218}
219
220impl<R: InParamTuple + IntoDynamicSend> FallibleSignalFuture<R> {
221 fn new(signal: Signal) -> Self {
222 sys::strict_assert!(
223 !signal.is_null(),
224 "Failed to create future for invalid signal:\n\
225 Either the signal object was already freed, or it\n\
226 was not registered in the object before being used.",
227 );
228
229 let data = Arc::new(Mutex::new(SignalFutureData::default()));
230
231 let callable = SignalFutureResolver::new(data.clone());
233
234 signal.connect_flags(
235 &Callable::from_custom(callable.clone()),
236 ConnectFlags::ONE_SHOT,
237 );
238
239 Self {
240 data,
241 callable,
242 signal,
243 }
244 }
245
246 fn poll(&mut self, cx: &mut Context<'_>) -> Poll<Result<R, FallibleSignalFutureError>> {
247 let mut data = self.data.lock().unwrap();
248
249 data.waker.replace(cx.waker().clone());
250
251 let value = data.state.take();
252
253 drop(data);
255
256 match value {
257 SignalFutureState::Pending => Poll::Pending,
258 SignalFutureState::Dropped => unreachable!(),
259 SignalFutureState::Dead => Poll::Ready(Err(FallibleSignalFutureError)),
260 SignalFutureState::Ready(value) => {
261 let Some(value) = DynamicSend::extract_if_safe(value) else {
262 panic!(
263 "the awaited signal was not emitted on the main-thread, but contained a non Send argument"
264 );
265 };
266
267 Poll::Ready(Ok(value))
268 }
269 }
270 }
271}
272
273#[derive(Debug)]
277pub struct FallibleSignalFutureError;
278
279impl Display for FallibleSignalFutureError {
280 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
281 write!(
282 f,
283 "The signal object was freed before the awaited signal was emitted"
284 )
285 }
286}
287
288impl std::error::Error for FallibleSignalFutureError {}
289
290impl<R: InParamTuple + IntoDynamicSend> Future for FallibleSignalFuture<R> {
291 type Output = Result<R, FallibleSignalFutureError>;
292
293 fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
294 self.get_mut().poll(cx)
295 }
296}
297
298impl<R: InParamTuple + IntoDynamicSend> Drop for FallibleSignalFuture<R> {
299 fn drop(&mut self) {
300 if self.signal.is_null() {
302 return;
303 }
304
305 let mut data_lock = self.data.lock().unwrap();
306
307 let prev_state = std::mem::replace(&mut data_lock.state, SignalFutureState::Dropped);
308
309 drop(data_lock);
310
311 if !matches!(prev_state, SignalFutureState::Pending) {
316 return;
317 }
318
319 let gd_callable = Callable::from_custom(self.callable.clone());
321
322 let cleanup = || {
334 if let Some(mut object) = self.signal.object() {
335 let signal_name = self.signal.name();
336
337 if object.is_connected(&signal_name, &gd_callable) {
338 object.disconnect(&signal_name, &gd_callable);
339 }
340 }
341 };
342
343 let context = || "FallibleSignalFuture::drop: object freed concurrently".to_string();
344 let _ = crate::private::handle_panic(context, cleanup);
345 }
346}
347
348impl Signal {
349 pub fn to_fallible_future<R: InParamTuple + IntoDynamicSend>(&self) -> FallibleSignalFuture<R> {
357 FallibleSignalFuture::new(self.clone())
358 }
359
360 pub fn to_future<R: InParamTuple + IntoDynamicSend>(&self) -> SignalFuture<R> {
368 SignalFuture::new(self.clone())
369 }
370}
371
372impl<C: WithSignals, R: InParamTuple + IntoDynamicSend> TypedSignal<'_, C, R> {
373 pub fn to_fallible_future(&self) -> FallibleSignalFuture<R> {
378 FallibleSignalFuture::new(self.to_untyped())
379 }
380
381 pub fn to_future(&self) -> SignalFuture<R> {
386 SignalFuture::new(self.to_untyped())
387 }
388}
389
390impl<C: WithSignals, R: InParamTuple + IntoDynamicSend> IntoFuture for &TypedSignal<'_, C, R> {
391 type Output = R;
392
393 type IntoFuture = SignalFuture<R>;
394
395 fn into_future(self) -> Self::IntoFuture {
396 self.to_future()
397 }
398}
399
400pub trait IntoDynamicSend: Sealed + 'static {
405 type Target: DynamicSend<Inner = Self>;
406
407 fn into_dynamic_send(self) -> Self::Target;
408}
409
410pub unsafe trait DynamicSend: Send + Sealed {
420 type Inner;
421
422 fn extract_if_safe(self) -> Option<Self::Inner>;
423}
424
425pub struct ThreadConfined<T> {
429 value: Option<T>,
430 thread_id: ThreadId,
431}
432
433unsafe impl<T> Send for ThreadConfined<T> {}
435
436impl<T> ThreadConfined<T> {
437 pub(crate) fn new(value: T) -> Self {
438 Self {
439 value: Some(value),
440 thread_id: std::thread::current().id(),
441 }
442 }
443
444 pub(crate) fn extract(mut self) -> Option<T> {
448 if self.is_original_thread() {
449 self.value.take()
450 } else {
451 None }
453 }
454
455 fn is_original_thread(&self) -> bool {
456 self.thread_id == std::thread::current().id()
457 }
458}
459
460impl<T> Drop for ThreadConfined<T> {
461 fn drop(&mut self) {
462 if !self.is_original_thread() {
463 std::mem::forget(self.value.take());
464
465 godot_error!(
467 "Dropped ThreadConfined<T> on a different thread than it was created on. The inner T value will be leaked."
468 );
469 }
470 }
471}
472
473unsafe impl<T: GodotClass> DynamicSend for ThreadConfined<Gd<T>> {
474 type Inner = Gd<T>;
475
476 fn extract_if_safe(self) -> Option<Self::Inner> {
477 self.extract()
478 }
479}
480
481impl<T: GodotClass> Sealed for ThreadConfined<Gd<T>> {}
482
483impl<T: GodotClass> IntoDynamicSend for Gd<T> {
484 type Target = ThreadConfined<Self>;
485
486 fn into_dynamic_send(self) -> Self::Target {
487 ThreadConfined::new(self)
488 }
489}
490
491#[macro_export(local_inner_macros)]
495macro_rules! impl_dynamic_send {
496 (Send; $($ty:ty),+) => {
497 $(
498 unsafe impl $crate::task::DynamicSend for $ty {
499 type Inner = Self;
500
501 fn extract_if_safe(self) -> Option<Self::Inner> {
502 Some(self)
503 }
504 }
505
506 impl $crate::task::IntoDynamicSend for $ty {
507 type Target = Self;
508 fn into_dynamic_send(self) -> Self::Target {
509 self
510 }
511 }
512 )+
513 };
514
515 (tuple; $($arg:ident: $ty:ident),*) => {
516 unsafe impl<$($ty: $crate::task::DynamicSend ),*> $crate::task::DynamicSend for ($($ty,)*) {
517 type Inner = ($($ty::Inner,)*);
518
519 fn extract_if_safe(self) -> Option<Self::Inner> {
520 #[allow(non_snake_case)]
521 let ($($arg,)*) = self;
522
523 #[allow(clippy::unused_unit)]
524 match ($($arg.extract_if_safe(),)*) {
525 ($(Some($arg),)*) => Some(($($arg,)*)),
526
527 #[allow(unreachable_patterns)]
528 _ => None,
529 }
530 }
531 }
532
533 impl<$($ty: $crate::task::IntoDynamicSend),*> $crate::task::IntoDynamicSend for ($($ty,)*) {
534 type Target = ($($ty::Target,)*);
535
536 fn into_dynamic_send(self) -> Self::Target {
537 #[allow(non_snake_case)]
538 let ($($arg,)*) = self;
539
540 #[allow(clippy::unused_unit)]
541 ($($arg.into_dynamic_send(),)*)
542 }
543 }
544 };
545
546 (!Send; $($ty:ident),+) => {
547 $(
548 impl $crate::meta::sealed::Sealed for $crate::task::ThreadConfined<$crate::builtin::$ty> {}
549
550 unsafe impl $crate::task::DynamicSend for $crate::task::ThreadConfined<$crate::builtin::$ty> {
551 type Inner = $crate::builtin::$ty;
552
553 fn extract_if_safe(self) -> Option<Self::Inner> {
554 self.extract()
555 }
556 }
557
558 impl $crate::task::IntoDynamicSend for $crate::builtin::$ty {
559 type Target = $crate::task::ThreadConfined<$crate::builtin::$ty>;
560
561 fn into_dynamic_send(self) -> Self::Target {
562 $crate::task::ThreadConfined::new(self)
563 }
564 }
565 )+
566 };
567}
568
569#[cfg(test)] #[cfg_attr(published_docs, doc(cfg(test)))]
570mod tests {
571 use std::sync::Arc;
572 use std::sync::atomic::{AtomicUsize, Ordering};
573 use std::thread;
574
575 use super::{SignalFutureResolver, ThreadConfined};
576 use crate::classes::Object;
577 use crate::obj::Gd;
578 use crate::sys;
579
580 #[test]
583 fn future_resolver_cloned_hash() {
584 let resolver_a = SignalFutureResolver::<(Gd<Object>, i64)>::new(Arc::default());
585 let resolver_b = resolver_a.clone();
586
587 let hash_a = sys::hash_value(&resolver_a);
588 let hash_b = sys::hash_value(&resolver_b);
589
590 assert_eq!(hash_a, hash_b);
591 }
592
593 #[test]
595 #[cfg_attr(
596 all(target_family = "wasm", not(target_feature = "atomics")),
597 ignore = "Threading not available"
598 )]
599 fn thread_confined_extract() {
600 let confined = ThreadConfined::new(772);
601 assert_eq!(confined.extract(), Some(772));
602
603 let confined = ThreadConfined::new(772);
604
605 let handle = thread::spawn(move || {
606 assert!(confined.extract().is_none());
607 });
608 handle.join().unwrap();
609 }
610
611 #[test]
612 #[cfg_attr(
613 all(target_family = "wasm", not(target_feature = "atomics")),
614 ignore = "Threading not available"
615 )]
616 fn thread_confined_leak_on_other_thread() {
617 static COUNTER: AtomicUsize = AtomicUsize::new(0);
618
619 struct DropCounter;
620 impl Drop for DropCounter {
621 fn drop(&mut self) {
622 COUNTER.fetch_add(1, Ordering::SeqCst);
623 }
624 }
625
626 let drop_counter = DropCounter;
627 let confined = ThreadConfined::new(drop_counter);
628
629 let handle = thread::spawn(move || drop(confined));
630 handle.join().unwrap();
631
632 assert_eq!(COUNTER.load(Ordering::SeqCst), 0);
634 }
635}