@@ -23,15 +23,41 @@ use std::{
23
23
} ;
24
24
25
25
#[ cfg( target_arch = "wasm32" ) ]
26
- pub use futures_util:: future:: Aborted as JoinError ;
26
+ pub use futures_util:: future:: AbortHandle ;
27
27
#[ cfg( target_arch = "wasm32" ) ]
28
28
use futures_util:: {
29
- future:: { AbortHandle , Abortable , RemoteHandle } ,
29
+ future:: { Abortable , RemoteHandle } ,
30
30
FutureExt ,
31
31
} ;
32
32
#[ cfg( not( target_arch = "wasm32" ) ) ]
33
- pub use tokio:: task:: { spawn, JoinError , JoinHandle } ;
33
+ pub use tokio:: task:: { spawn, AbortHandle , JoinError , JoinHandle } ;
34
34
35
+ #[ cfg( target_arch = "wasm32" ) ]
36
+ #[ derive( Debug ) ]
37
+ pub enum JoinError {
38
+ Cancelled ,
39
+ Panic ,
40
+ }
41
+ #[ cfg( target_arch = "wasm32" ) ]
42
+ impl JoinError {
43
+ /// Returns true if the error was caused by the task being cancelled.
44
+ ///
45
+ /// See [the module level docs] for more information on cancellation.
46
+ ///
47
+ /// [the module level docs]: crate::task#cancellation
48
+ pub fn is_cancelled ( & self ) -> bool {
49
+ matches ! ( self , JoinError :: Cancelled )
50
+ }
51
+ }
52
+ #[ cfg( target_arch = "wasm32" ) ]
53
+ impl std:: fmt:: Display for JoinError {
54
+ fn fmt ( & self , fmt : & mut std:: fmt:: Formatter < ' _ > ) -> std:: fmt:: Result {
55
+ match & self {
56
+ JoinError :: Cancelled => write ! ( fmt, "task was cancelled" ) ,
57
+ JoinError :: Panic => write ! ( fmt, "task panicked" ) ,
58
+ }
59
+ }
60
+ }
35
61
#[ cfg( target_arch = "wasm32" ) ]
36
62
pub fn spawn < F , T > ( future : F ) -> JoinHandle < T >
37
63
where
@@ -47,24 +73,28 @@ where
47
73
let _ = future. await ;
48
74
} ) ;
49
75
50
- JoinHandle { remote_handle : Some ( remote_handle) , abort_handle }
76
+ JoinHandle { remote_handle : Some ( remote_handle) , the_abort_handle : abort_handle }
51
77
}
52
78
53
79
#[ cfg( target_arch = "wasm32" ) ]
54
80
#[ derive( Debug ) ]
55
81
pub struct JoinHandle < T > {
56
82
remote_handle : Option < RemoteHandle < T > > ,
57
- abort_handle : AbortHandle ,
83
+ the_abort_handle : AbortHandle ,
58
84
}
59
85
60
86
#[ cfg( target_arch = "wasm32" ) ]
61
87
impl < T > JoinHandle < T > {
62
88
pub fn abort ( & self ) {
63
- self . abort_handle . abort ( ) ;
89
+ self . the_abort_handle . abort ( ) ;
90
+ }
91
+
92
+ pub fn abort_handle ( & self ) -> AbortHandle {
93
+ self . the_abort_handle . clone ( )
64
94
}
65
95
66
96
pub fn is_finished ( & self ) -> bool {
67
- self . abort_handle . is_aborted ( )
97
+ self . the_abort_handle . is_aborted ( )
68
98
}
69
99
}
70
100
@@ -83,17 +113,119 @@ impl<T: 'static> Future for JoinHandle<T> {
83
113
type Output = Result < T , JoinError > ;
84
114
85
115
fn poll ( mut self : Pin < & mut Self > , cx : & mut Context < ' _ > ) -> Poll < Self :: Output > {
86
- if self . abort_handle . is_aborted ( ) {
116
+ if self . the_abort_handle . is_aborted ( ) {
87
117
// The future has been aborted. It is not possible to poll it again.
88
- Poll :: Ready ( Err ( JoinError ) )
118
+ Poll :: Ready ( Err ( JoinError :: Cancelled ) )
89
119
} else if let Some ( handle) = self . remote_handle . as_mut ( ) {
90
120
Pin :: new ( handle) . poll ( cx) . map ( Ok )
91
121
} else {
92
- Poll :: Ready ( Err ( JoinError ) )
122
+ Poll :: Ready ( Err ( JoinError :: Panic ) )
93
123
}
94
124
}
95
125
}
96
126
127
+ #[ cfg( target_arch = "wasm32" ) ]
128
+ use futures_executor;
129
+
130
+ /// A handle to a runtime for executing async tasks and futures.
131
+ ///
132
+ /// This is a unified type that represents either:
133
+ /// - A `tokio::runtime::Handle` on non-WASM platforms
134
+ /// - A `WasmRuntimeHandle` on WASM platforms
135
+ ///
136
+ /// This abstraction allows code to run on both WASM and non-WASM platforms
137
+ /// without conditional compilation.
138
+ #[ cfg( not( target_arch = "wasm32" ) ) ]
139
+ pub type Handle = tokio:: runtime:: Handle ;
140
+ #[ cfg( not( target_arch = "wasm32" ) ) ]
141
+ pub type Runtime = tokio:: runtime:: Runtime ;
142
+
143
+ #[ cfg( target_arch = "wasm32" ) ]
144
+ pub type Handle = WasmRuntimeHandle ;
145
+ #[ cfg( target_arch = "wasm32" ) ]
146
+ pub type Runtime = WasmRuntimeHandle ;
147
+
148
+ #[ cfg( target_arch = "wasm32" ) ]
149
+ #[ derive( Debug ) ]
150
+ /// A dummy guard that does nothing when dropped.
151
+ /// This is used for the WASM implementation to match tokio::runtime::EnterGuard.
152
+ pub struct WasmRuntimeGuard ;
153
+
154
+ #[ cfg( target_arch = "wasm32" ) ]
155
+ impl Drop for WasmRuntimeGuard {
156
+ fn drop ( & mut self ) {
157
+ // No-op, as there's no special context to exit in WASM
158
+ }
159
+ }
160
+
161
+ #[ cfg( target_arch = "wasm32" ) ]
162
+ #[ derive( Default , Debug ) ]
163
+ /// A runtime handle implementation for WebAssembly targets.
164
+ ///
165
+ /// This implements a minimal subset of the tokio::runtime::Handle API
166
+ /// that is needed for the matrix-rust-sdk to function on WASM.
167
+ pub struct WasmRuntimeHandle ;
168
+
169
+ #[ cfg( target_arch = "wasm32" ) ]
170
+ impl WasmRuntimeHandle {
171
+ /// Spawns a future in the wasm32 bindgen runtime.
172
+ #[ track_caller]
173
+ pub fn spawn < F > ( & self , future : F ) -> JoinHandle < F :: Output >
174
+ where
175
+ F : Future + ' static ,
176
+ F :: Output : ' static ,
177
+ {
178
+ spawn ( future)
179
+ }
180
+
181
+ /// Runs the provided function on an executor dedicated to blocking
182
+ /// operations.
183
+ #[ track_caller]
184
+ pub fn spawn_blocking < F , R > ( & self , func : F ) -> JoinHandle < R >
185
+ where
186
+ F : FnOnce ( ) -> R + ' static ,
187
+ R : ' static ,
188
+ {
189
+ spawn ( async move { func ( ) } )
190
+ }
191
+
192
+ /// Runs a future to completion on the current thread.
193
+ pub fn block_on < F , T > ( & self , future : F ) -> T
194
+ where
195
+ F : Future < Output = T > ,
196
+ {
197
+ futures_executor:: block_on ( future)
198
+ }
199
+
200
+ /// Enters the runtime context.
201
+ ///
202
+ /// For WebAssembly, this is a no-op that returns a dummy guard.
203
+ pub fn enter ( & self ) -> WasmRuntimeGuard {
204
+ WasmRuntimeGuard
205
+ }
206
+ }
207
+
208
+ /// Get a runtime handle appropriate for the current target platform.
209
+ ///
210
+ /// This function returns a unified `Handle` type that works across both
211
+ /// WASM and non-WASM platforms, allowing code to be written that is
212
+ /// agnostic to the platform-specific runtime implementation.
213
+ ///
214
+ /// Returns:
215
+ /// - A `tokio::runtime::Handle` on non-WASM platforms
216
+ /// - A `WasmRuntimeHandle` on WASM platforms
217
+ pub fn get_runtime_handle ( ) -> Handle {
218
+ #[ cfg( target_arch = "wasm32" ) ]
219
+ {
220
+ WasmRuntimeHandle
221
+ }
222
+
223
+ #[ cfg( not( target_arch = "wasm32" ) ) ]
224
+ {
225
+ async_compat:: get_runtime_handle ( )
226
+ }
227
+ }
228
+
97
229
#[ cfg( test) ]
98
230
mod tests {
99
231
use assert_matches:: assert_matches;
0 commit comments