@@ -8,7 +8,6 @@ use std::thread::{self, ThreadId};
8
8
use crate :: builtin:: { Callable , Signal , Variant } ;
9
9
use crate :: classes:: object:: ConnectFlags ;
10
10
use crate :: classes:: Os ;
11
- use crate :: godot_error;
12
11
use crate :: meta:: { FromGodot , ToGodot } ;
13
12
use crate :: obj:: EngineEnum ;
14
13
@@ -34,10 +33,50 @@ pub fn godot_task(future: impl Future<Output = ()> + 'static) {
34
33
waker. wake ( ) ;
35
34
}
36
35
37
- thread_local ! { static ASYNC_RUNTIME : RefCell <AsyncRuntime > = RefCell :: new( AsyncRuntime :: new( ) ) ; }
36
+ thread_local ! { pub ( crate ) static ASYNC_RUNTIME : RefCell <AsyncRuntime > = RefCell :: new( AsyncRuntime :: new( ) ) ; }
38
37
39
- struct AsyncRuntime {
40
- tasks : Vec < Option < Pin < Box < dyn Future < Output = ( ) > > > > > ,
38
+ #[ derive( Default ) ]
39
+ enum FutureSlot < T > {
40
+ #[ default]
41
+ Empty ,
42
+ Pending ( T ) ,
43
+ Polling ,
44
+ }
45
+
46
+ impl < T > FutureSlot < T > {
47
+ fn is_empty ( & self ) -> bool {
48
+ matches ! ( self , Self :: Empty )
49
+ }
50
+
51
+ fn clear ( & mut self ) {
52
+ * self = Self :: Empty ;
53
+ }
54
+
55
+ fn take ( & mut self ) -> Self {
56
+ match self {
57
+ Self :: Empty => Self :: Empty ,
58
+ Self :: Pending ( _) => std:: mem:: replace ( self , Self :: Polling ) ,
59
+ Self :: Polling => Self :: Polling ,
60
+ }
61
+ }
62
+
63
+ fn park ( & mut self , value : T ) {
64
+ match self {
65
+ Self :: Empty => {
66
+ panic ! ( "Future slot is currently unoccupied, future can not be parked here!" ) ;
67
+ }
68
+
69
+ Self :: Pending ( _) => panic ! ( "Future slot is already occupied by a different future!" ) ,
70
+ Self :: Polling => {
71
+ * self = Self :: Pending ( value) ;
72
+ }
73
+ }
74
+ }
75
+ }
76
+
77
+ #[ derive( Default ) ]
78
+ pub ( crate ) struct AsyncRuntime {
79
+ tasks : Vec < FutureSlot < Pin < Box < dyn Future < Output = ( ) > > > > > ,
41
80
}
42
81
43
82
impl AsyncRuntime {
@@ -52,35 +91,41 @@ impl AsyncRuntime {
52
91
. tasks
53
92
. iter_mut ( )
54
93
. enumerate ( )
55
- . find ( |( _, slot) | slot. is_none ( ) ) ;
94
+ . find ( |( _, slot) | slot. is_empty ( ) ) ;
56
95
57
96
let boxed = Box :: pin ( future) ;
58
97
59
98
match slot {
60
99
Some ( ( index, slot) ) => {
61
- * slot = Some ( boxed) ;
100
+ * slot = FutureSlot :: Pending ( boxed) ;
62
101
index
63
102
}
64
103
None => {
65
- self . tasks . push ( Some ( boxed) ) ;
104
+ self . tasks . push ( FutureSlot :: Pending ( boxed) ) ;
66
105
self . tasks . len ( ) - 1
67
106
}
68
107
}
69
108
}
70
109
71
- fn get_task ( & mut self , index : usize ) -> Option < Pin < & mut ( dyn Future < Output = ( ) > + ' static ) > > {
110
+ fn get_task (
111
+ & mut self ,
112
+ index : usize ,
113
+ ) -> FutureSlot < Pin < Box < dyn Future < Output = ( ) > + ' static > > > {
72
114
let slot = self . tasks . get_mut ( index) ;
73
115
74
- slot. and_then ( |inner| inner. as_mut ( ) )
75
- . map ( |fut| fut. as_mut ( ) )
116
+ slot. map ( |inner| inner. take ( ) ) . unwrap_or_default ( )
76
117
}
77
118
78
119
fn clear_task ( & mut self , index : usize ) {
79
120
if index >= self . tasks . len ( ) {
80
121
return ;
81
122
}
82
123
83
- self . tasks [ 0 ] = None ;
124
+ self . tasks [ 0 ] . clear ( ) ;
125
+ }
126
+
127
+ fn park_task ( & mut self , index : usize , future : Pin < Box < dyn Future < Output = ( ) > > > ) {
128
+ self . tasks [ index] . park ( future) ;
84
129
}
85
130
}
86
131
@@ -101,27 +146,36 @@ impl GodotWaker {
101
146
impl Wake for GodotWaker {
102
147
fn wake ( self : std:: sync:: Arc < Self > ) {
103
148
let callable = Callable :: from_fn ( "GodotWaker::wake" , move |_args| {
149
+ let current_thread = thread:: current ( ) . id ( ) ;
150
+
151
+ if self . thread_id != current_thread {
152
+ panic ! ( "trying to poll future on a different thread!\n Current Thread: {:?}, Future Thread: {:?}" , current_thread, self . thread_id) ;
153
+ }
154
+
104
155
let waker: Waker = self . clone ( ) . into ( ) ;
105
156
let mut ctx = Context :: from_waker ( & waker) ;
106
157
107
- ASYNC_RUNTIME . with_borrow_mut ( |rt| {
108
- let current_thread = thread:: current ( ) . id ( ) ;
158
+ // take future out of the runtime.
159
+ let mut future = ASYNC_RUNTIME . with_borrow_mut ( |rt| {
160
+ match rt. get_task ( self . runtime_index ) {
161
+ FutureSlot :: Empty => {
162
+ panic ! ( "Future no longer exists when waking it! This is a bug!" ) ;
163
+ } ,
109
164
110
- if self . thread_id != current_thread {
111
- panic ! ( "trying to poll future on a different thread!\n Current Thread: {:?}, Future Thread: {:?}" , current_thread, self . thread_id) ;
165
+ FutureSlot :: Polling => {
166
+ panic ! ( "The same GodotWaker has been called recursively, this is not expected!" ) ;
167
+ }
168
+
169
+ FutureSlot :: Pending ( future) => future
112
170
}
171
+ } ) ;
113
172
114
- let Some ( future) = rt. get_task ( self . runtime_index ) else {
115
- godot_error ! ( "Future no longer exists! This is a bug!" ) ;
116
- return ;
117
- } ;
173
+ let result = future. as_mut ( ) . poll ( & mut ctx) ;
118
174
119
- // this does currently not support nested tasks.
120
- let result = future. poll ( & mut ctx) ;
121
- match result {
122
- Poll :: Pending => ( ) ,
123
- Poll :: Ready ( ( ) ) => rt. clear_task ( self . runtime_index ) ,
124
- }
175
+ // update runtime.
176
+ ASYNC_RUNTIME . with_borrow_mut ( |rt| match result {
177
+ Poll :: Pending => rt. park_task ( self . runtime_index , future) ,
178
+ Poll :: Ready ( ( ) ) => rt. clear_task ( self . runtime_index ) ,
125
179
} ) ;
126
180
127
181
Ok ( Variant :: nil ( ) )
@@ -186,11 +240,17 @@ impl<R: FromSignalArgs> Future for SignalFuture<R> {
186
240
187
241
impl < R : FromSignalArgs > Drop for SignalFuture < R > {
188
242
fn drop ( & mut self ) {
189
- if !self . signal . is_connected ( self . callable . clone ( ) ) {
243
+ if !self . callable . is_valid ( ) {
190
244
return ;
191
245
}
192
246
193
- self . signal . disconnect ( self . callable . clone ( ) ) ;
247
+ if self . signal . object ( ) . is_none ( ) {
248
+ return ;
249
+ }
250
+
251
+ if self . signal . is_connected ( self . callable . clone ( ) ) {
252
+ self . signal . disconnect ( self . callable . clone ( ) ) ;
253
+ }
194
254
}
195
255
}
196
256
0 commit comments