executor_core/web.rs
1//! Integration for Web/WASM environments.
2//!
3//! This module provides executor implementations for web browsers and other
4//! WASM environments using `wasm-bindgen-futures`.
5
6use crate::{Executor, LocalExecutor, Task};
7use core::{
8 future::Future,
9 pin::Pin,
10 task::{Context, Poll},
11};
12use wasm_bindgen_futures::spawn_local;
13
14
15/// Web-based executor implementation for WASM targets.
16///
17/// This executor uses `wasm-bindgen-futures::spawn_local` to execute futures
18/// in web environments. Both `Send` and non-`Send` futures are handled the same
19/// way since web environments are single-threaded.
20///
21/// ## Panic Handling
22///
23/// Unlike other executors, the web executor cannot catch panics due to WASM limitations.
24/// If a spawned task panics, the entire WASM module will terminate. This is a fundamental
25/// limitation of the WASM environment and cannot be worked around.
26///
27#[derive(Clone, Copy, Debug)]
28pub struct WebExecutor;
29
30impl WebExecutor {
31 /// Create a new [`WebExecutor`].
32 pub fn new() -> Self {
33 Self
34 }
35}
36
37impl Default for WebExecutor {
38 fn default() -> Self {
39 Self::new()
40 }
41}
42
43/// Task wrapper for web/WASM environment.
44///
45/// This task type provides task management for web environments where
46/// panic catching is not available. Unlike other task implementations,
47/// panics cannot be caught and will terminate the entire WASM module.
48///
49/// This is a simple wrapper that just awaits the spawned future directly.
50pub struct WebTask<T> {
51 _marker: core::marker::PhantomData<T>,
52}
53
54impl<T> core::fmt::Debug for WebTask<T> {
55 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
56 f.debug_struct("WebTask").finish_non_exhaustive()
57 }
58}
59
60impl<T: 'static> WebTask<T> {
61 fn new() -> Self {
62 Self {
63 _marker: core::marker::PhantomData,
64 }
65 }
66}
67
68impl<T> Future for WebTask<T> {
69 type Output = T;
70
71 fn poll(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll<Self::Output> {
72 // Web tasks complete immediately since they're spawned via spawn_local
73 // This shouldn't actually be called since we don't store the future
74 Poll::Pending
75 }
76}
77
78impl<T: 'static> Task<T> for WebTask<T> {
79 fn poll_result(
80 self: Pin<&mut Self>,
81 _cx: &mut Context<'_>,
82 ) -> Poll<Result<T, crate::Error>> {
83 // Web tasks complete immediately since they're spawned via spawn_local
84 // This shouldn't actually be called since we don't store the future
85 Poll::Pending
86 }
87
88 fn poll_cancel(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll<()> {
89 // In web environment, tasks can't be cancelled once spawned
90 Poll::Ready(())
91 }
92}
93
94
95impl LocalExecutor for WebExecutor {
96 type Task<T: 'static> = WebTask<T>;
97
98 fn spawn_local<Fut>(&self, fut: Fut) -> Self::Task<Fut::Output>
99 where
100 Fut: Future + 'static,
101 {
102 // Spawn the future directly using wasm-bindgen-futures
103 // We need to wrap it to discard the result since spawn_local expects ()
104 spawn_local(async move {
105 let _ = fut.await;
106 });
107
108 // Return a placeholder task since web tasks can't be awaited
109 WebTask::new()
110 }
111}
112
113impl Executor for WebExecutor {
114 type Task<T: Send + 'static> = WebTask<T>;
115
116 fn spawn<Fut>(&self, fut: Fut) -> Self::Task<Fut::Output>
117 where
118 Fut: Future<Output: Send> + Send + 'static,
119 {
120 // In web environment, we use spawn_local even for Send futures
121 // since web workers don't have the same threading model as native
122 // Spawn the future directly using wasm-bindgen-futures
123 // We need to wrap it to discard the result since spawn_local expects ()
124 spawn_local(async move {
125 let _ = fut.await;
126 });
127
128 // Return a placeholder task since web tasks can't be awaited
129 WebTask::new()
130 }
131}