wit_bindgen/rt/async_support.rs
1#![deny(missing_docs)]
2
3extern crate std;
4use core::sync::atomic::{AtomicU32, Ordering};
5use std::boxed::Box;
6use std::collections::BTreeMap;
7use std::ffi::c_void;
8use std::future::Future;
9use std::mem;
10use std::pin::Pin;
11use std::ptr;
12use std::sync::Arc;
13use std::task::{Context, Poll, Wake, Waker};
14
15macro_rules! rtdebug {
16 ($($f:tt)*) => {
17 // Change this flag to enable debugging, right now we're not using a
18 // crate like `log` or such to reduce runtime deps. Intended to be used
19 // during development for now.
20 if false {
21 std::eprintln!($($f)*);
22 }
23 }
24}
25
26/// Helper macro to deduplicate foreign definitions of wasm functions.
27///
28/// This automatically imports when on wasm targets and then defines a dummy
29/// panicking shim for native targets to support native compilation but fail at
30/// runtime.
31macro_rules! extern_wasm {
32 (
33 $(#[$extern_attr:meta])*
34 extern "C" {
35 $(
36 $(#[$func_attr:meta])*
37 $vis:vis fn $func_name:ident ( $($args:tt)* ) $(-> $ret:ty)?;
38 )*
39 }
40 ) => {
41 $(
42 #[cfg(not(target_family = "wasm"))]
43 #[allow(unused)]
44 $vis unsafe fn $func_name($($args)*) $(-> $ret)? {
45 unreachable!();
46 }
47 )*
48
49 #[cfg(target_family = "wasm")]
50 $(#[$extern_attr])*
51 extern "C" {
52 $(
53 $(#[$func_attr])*
54 $vis fn $func_name($($args)*) $(-> $ret)?;
55 )*
56 }
57 };
58}
59
60mod abi_buffer;
61mod cabi;
62mod error_context;
63mod future_support;
64#[cfg(feature = "inter-task-wakeup")]
65mod inter_task_wakeup;
66mod stream_support;
67mod subtask;
68#[cfg(feature = "inter-task-wakeup")]
69mod unit_stream;
70mod waitable;
71mod waitable_set;
72
73#[cfg(not(feature = "inter-task-wakeup"))]
74use inter_task_wakeup_disabled as inter_task_wakeup;
75#[cfg(not(feature = "inter-task-wakeup"))]
76mod inter_task_wakeup_disabled;
77
78use self::waitable_set::WaitableSet;
79pub use abi_buffer::*;
80pub use error_context::*;
81pub use future_support::*;
82pub use stream_support::*;
83#[doc(hidden)]
84pub use subtask::Subtask;
85#[cfg(feature = "inter-task-wakeup")]
86pub use unit_stream::*;
87
88type BoxFuture<'a> = Pin<Box<dyn Future<Output = ()> + 'a>>;
89
90#[cfg(feature = "async-spawn")]
91mod spawn;
92#[cfg(feature = "async-spawn")]
93pub use spawn::spawn;
94#[cfg(not(feature = "async-spawn"))]
95mod spawn_disabled;
96#[cfg(not(feature = "async-spawn"))]
97use spawn_disabled as spawn;
98
99/// Represents a task created by either a call to an async-lifted export or a
100/// future run using `block_on` or `start_task`.
101struct FutureState<'a> {
102 /// Remaining work to do (if any) before this task can be considered "done".
103 ///
104 /// Note that we won't tell the host the task is done until this is drained
105 /// and `waitables` is empty.
106 tasks: spawn::Tasks<'a>,
107
108 /// The waitable set containing waitables created by this task, if any.
109 waitable_set: Option<WaitableSet>,
110
111 /// State of all waitables in `waitable_set`, and the ptr/callback they're
112 /// associated with.
113 //
114 // Note that this is a `BTreeMap` rather than a `HashMap` only because, as
115 // of this writing, initializing the default hasher for `HashMap` requires
116 // calling `wasi_snapshot_preview1:random_get`, which requires initializing
117 // the `wasi_snapshot_preview1` adapter when targeting `wasm32-wasip2` and
118 // later, and that's expensive enough that we'd prefer to avoid it for apps
119 // which otherwise make no use of the adapter.
120 waitables: BTreeMap<u32, (*mut c_void, unsafe extern "C" fn(*mut c_void, u32))>,
121
122 /// Raw structure used to pass to `cabi::wasip3_task_set`
123 wasip3_task: cabi::wasip3_task,
124
125 /// Rust-level state for the waker, notably a bool as to whether this has
126 /// been woken.
127 waker: Arc<FutureWaker>,
128
129 /// Clone of `waker` field, but represented as `std::task::Waker`.
130 waker_clone: Waker,
131
132 /// State related to supporting inter-task wakeup scenarios.
133 inter_task_wakeup: inter_task_wakeup::State,
134}
135
136impl FutureState<'_> {
137 fn new(future: BoxFuture<'_>) -> FutureState<'_> {
138 let waker = Arc::new(FutureWaker::default());
139 FutureState {
140 waker_clone: waker.clone().into(),
141 waker,
142 tasks: spawn::Tasks::new(future),
143 waitable_set: None,
144 waitables: BTreeMap::new(),
145 wasip3_task: cabi::wasip3_task {
146 // This pointer is filled in before calling `wasip3_task_set`.
147 ptr: ptr::null_mut(),
148 version: cabi::WASIP3_TASK_V1,
149 waitable_register,
150 waitable_unregister,
151 },
152 inter_task_wakeup: Default::default(),
153 }
154 }
155
156 fn get_or_create_waitable_set(&mut self) -> &WaitableSet {
157 self.waitable_set.get_or_insert_with(WaitableSet::new)
158 }
159
160 fn add_waitable(&mut self, waitable: u32) {
161 self.get_or_create_waitable_set().join(waitable)
162 }
163
164 fn remove_waitable(&mut self, waitable: u32) {
165 WaitableSet::remove_waitable_from_all_sets(waitable)
166 }
167
168 fn remaining_work(&self) -> bool {
169 !self.waitables.is_empty()
170 }
171
172 /// Handles the `event{0,1,2}` event codes and returns a corresponding
173 /// return code along with a flag whether this future is "done" or not.
174 fn callback(&mut self, event0: u32, event1: u32, event2: u32) -> CallbackCode {
175 match event0 {
176 EVENT_NONE => rtdebug!("EVENT_NONE"),
177 EVENT_SUBTASK => rtdebug!("EVENT_SUBTASK({event1:#x}, {event2:#x})"),
178 EVENT_STREAM_READ => rtdebug!("EVENT_STREAM_READ({event1:#x}, {event2:#x})"),
179 EVENT_STREAM_WRITE => rtdebug!("EVENT_STREAM_WRITE({event1:#x}, {event2:#x})"),
180 EVENT_FUTURE_READ => rtdebug!("EVENT_FUTURE_READ({event1:#x}, {event2:#x})"),
181 EVENT_FUTURE_WRITE => rtdebug!("EVENT_FUTURE_WRITE({event1:#x}, {event2:#x})"),
182 EVENT_CANCEL => {
183 rtdebug!("EVENT_CANCEL");
184
185 // Cancellation is mapped to destruction in Rust, so return a
186 // code/bool indicating we're done. The caller will then
187 // appropriately deallocate this `FutureState` which will
188 // transitively run all destructors.
189 return CallbackCode::Exit;
190 }
191 _ => unreachable!(),
192 }
193
194 self.with_p3_task_set(|me| {
195 // Transition our sleep state to ensure that the inter-task stream
196 // isn't used since there's no need to use that here.
197 me.waker
198 .sleep_state
199 .store(SLEEP_STATE_WOKEN, Ordering::Relaxed);
200
201 // With all of our context now configured, deliver the event
202 // notification this callback corresponds to.
203 //
204 // Note that this should happen under the reset of
205 // `waker.sleep_state` above to ensure that if a waker is woken it
206 // won't actually signal our inter-task stream since we're already
207 // in the process of handling the future.
208 if event0 != EVENT_NONE {
209 me.deliver_waitable_event(event1, event2)
210 }
211
212 // If there's still an in-progress read (e.g. `event{1,2}`) wasn't
213 // ourselves getting woken up, then cancel the read since we're
214 // processing the future here anyway.
215 me.cancel_inter_task_stream_read();
216
217 let mut context = Context::from_waker(&me.waker_clone);
218
219 loop {
220 // On each turn of this loop reset the state to "polling"
221 // which clears out any pending wakeup if one was sent. This
222 // in theory helps minimize wakeups from previous iterations
223 // happening in this iteration.
224 me.waker
225 .sleep_state
226 .store(SLEEP_STATE_POLLING, Ordering::Relaxed);
227
228 // Poll our future, seeing if it was able to make progress.
229 let poll = me.tasks.poll_next(&mut context);
230
231 match poll {
232 // A future completed, yay! Keep going to see if more have
233 // completed.
234 Poll::Ready(Some(())) => (),
235
236 // The task list is empty, but there might be remaining work
237 // in terms of waitables through the cabi interface. In this
238 // situation wait for all waitables to be resolved before
239 // signaling that our own task is done.
240 Poll::Ready(None) => {
241 assert!(me.tasks.is_empty());
242 if me.remaining_work() {
243 let waitable = me.waitable_set.as_ref().unwrap().as_raw();
244 break CallbackCode::Wait(waitable);
245 } else {
246 break CallbackCode::Exit;
247 }
248 }
249
250 // Some future within `self.tasks` is not ready yet. If our
251 // `waker` was signaled then that means this is a yield
252 // operation, otherwise it means we're blocking on
253 // something.
254 Poll::Pending => {
255 assert!(!me.tasks.is_empty());
256 if me.waker.sleep_state.load(Ordering::Relaxed) == SLEEP_STATE_WOKEN {
257 if me.remaining_work() {
258 let waitable = me.waitable_set.as_ref().unwrap().as_raw();
259 break CallbackCode::Poll(waitable);
260 }
261 break CallbackCode::Yield;
262 }
263
264 // Transition our state to "sleeping" so wakeup
265 // notifications know that they need to signal the
266 // inter-task stream.
267 me.waker
268 .sleep_state
269 .store(SLEEP_STATE_SLEEPING, Ordering::Relaxed);
270 me.read_inter_task_stream();
271 let waitable = me.waitable_set.as_ref().unwrap().as_raw();
272 break CallbackCode::Wait(waitable);
273 }
274 }
275 }
276 })
277 }
278
279 /// Deliver the `code` event to the `waitable` store within our map. This
280 /// waitable should be present because it's part of the waitable set which
281 /// is kept in-sync with our map.
282 fn deliver_waitable_event(&mut self, waitable: u32, code: u32) {
283 self.remove_waitable(waitable);
284
285 if self
286 .inter_task_wakeup
287 .consume_waitable_event(waitable, code)
288 {
289 return;
290 }
291
292 let (ptr, callback) = self.waitables.remove(&waitable).unwrap();
293 unsafe {
294 callback(ptr, code);
295 }
296 }
297
298 fn with_p3_task_set<R>(&mut self, f: impl FnOnce(&mut Self) -> R) -> R {
299 // Finish our `wasip3_task` by initializing its self-referential pointer,
300 // and then register it for the duration of this function with
301 // `wasip3_task_set`. The previous value of `wasip3_task_set` will get
302 // restored when this function returns.
303 struct ResetTask(*mut cabi::wasip3_task);
304 impl Drop for ResetTask {
305 fn drop(&mut self) {
306 unsafe {
307 cabi::wasip3_task_set(self.0);
308 }
309 }
310 }
311 let self_raw = self as *mut FutureState<'_>;
312 self.wasip3_task.ptr = self_raw.cast();
313 let prev = unsafe { cabi::wasip3_task_set(&mut self.wasip3_task) };
314 let _reset = ResetTask(prev);
315
316 f(self)
317 }
318}
319
320impl Drop for FutureState<'_> {
321 fn drop(&mut self) {
322 // If there's an active read of the inter-task stream, go ahead and
323 // cancel it, since we're about to drop the stream anyway.
324 self.cancel_inter_task_stream_read();
325
326 // If this state has active tasks then they need to be dropped which may
327 // execute arbitrary code. This arbitrary code might require the p3 APIs
328 // for managing waitables, notably around removing them. In this
329 // situation we ensure that the p3 task is set while futures are being
330 // destroyed.
331 if !self.tasks.is_empty() {
332 self.with_p3_task_set(|me| {
333 me.tasks = Default::default();
334 })
335 }
336 }
337}
338
339unsafe extern "C" fn waitable_register(
340 ptr: *mut c_void,
341 waitable: u32,
342 callback: unsafe extern "C" fn(*mut c_void, u32),
343 callback_ptr: *mut c_void,
344) -> *mut c_void {
345 let ptr = ptr.cast::<FutureState<'static>>();
346 assert!(!ptr.is_null());
347 (*ptr).add_waitable(waitable);
348 match (*ptr).waitables.insert(waitable, (callback_ptr, callback)) {
349 Some((prev, _)) => prev,
350 None => ptr::null_mut(),
351 }
352}
353
354unsafe extern "C" fn waitable_unregister(ptr: *mut c_void, waitable: u32) -> *mut c_void {
355 let ptr = ptr.cast::<FutureState<'static>>();
356 assert!(!ptr.is_null());
357 (*ptr).remove_waitable(waitable);
358 match (*ptr).waitables.remove(&waitable) {
359 Some((prev, _)) => prev,
360 None => ptr::null_mut(),
361 }
362}
363
364/// Status for "this task is actively being polled"
365const SLEEP_STATE_POLLING: u32 = 0;
366/// Status for "this task has a wakeup scheduled, no more action need be taken".
367const SLEEP_STATE_WOKEN: u32 = 1;
368/// Status for "this task is not being polled and has not been woken"
369///
370/// Wakeups on this status signal the inter-task stream.
371const SLEEP_STATE_SLEEPING: u32 = 2;
372
373#[derive(Default)]
374struct FutureWaker {
375 /// One of `SLEEP_STATE_*` indicating the current status.
376 sleep_state: AtomicU32,
377 inter_task_stream: inter_task_wakeup::WakerState,
378}
379
380impl Wake for FutureWaker {
381 fn wake(self: Arc<Self>) {
382 Self::wake_by_ref(&self)
383 }
384
385 fn wake_by_ref(self: &Arc<Self>) {
386 match self.sleep_state.swap(SLEEP_STATE_WOKEN, Ordering::Relaxed) {
387 // If this future was currently being polled, or if someone else
388 // already woke it up, then there's nothing to do.
389 SLEEP_STATE_POLLING | SLEEP_STATE_WOKEN => {}
390
391 // If this future is sleeping, however, then this is a cross-task
392 // wakeup meaning that we need to write to its wakeup stream.
393 other => {
394 assert_eq!(other, SLEEP_STATE_SLEEPING);
395 self.inter_task_stream.wake();
396 }
397 }
398 }
399}
400
401const EVENT_NONE: u32 = 0;
402const EVENT_SUBTASK: u32 = 1;
403const EVENT_STREAM_READ: u32 = 2;
404const EVENT_STREAM_WRITE: u32 = 3;
405const EVENT_FUTURE_READ: u32 = 4;
406const EVENT_FUTURE_WRITE: u32 = 5;
407const EVENT_CANCEL: u32 = 6;
408
409#[derive(PartialEq, Debug)]
410enum CallbackCode {
411 Exit,
412 Yield,
413 Wait(u32),
414 Poll(u32),
415}
416
417impl CallbackCode {
418 fn encode(self) -> u32 {
419 match self {
420 CallbackCode::Exit => 0,
421 CallbackCode::Yield => 1,
422 CallbackCode::Wait(waitable) => 2 | (waitable << 4),
423 CallbackCode::Poll(waitable) => 3 | (waitable << 4),
424 }
425 }
426}
427
428const STATUS_STARTING: u32 = 0;
429const STATUS_STARTED: u32 = 1;
430const STATUS_RETURNED: u32 = 2;
431const STATUS_STARTED_CANCELLED: u32 = 3;
432const STATUS_RETURNED_CANCELLED: u32 = 4;
433
434const BLOCKED: u32 = 0xffff_ffff;
435const COMPLETED: u32 = 0x0;
436const DROPPED: u32 = 0x1;
437const CANCELLED: u32 = 0x2;
438
439/// Return code of stream/future operations.
440#[derive(PartialEq, Debug, Copy, Clone)]
441enum ReturnCode {
442 /// The operation is blocked and has not completed.
443 Blocked,
444 /// The operation completed with the specified number of items.
445 Completed(u32),
446 /// The other end is dropped, but before that the specified number of items
447 /// were transferred.
448 Dropped(u32),
449 /// The operation was cancelled, but before that the specified number of
450 /// items were transferred.
451 Cancelled(u32),
452}
453
454impl ReturnCode {
455 fn decode(val: u32) -> ReturnCode {
456 if val == BLOCKED {
457 return ReturnCode::Blocked;
458 }
459 let amt = val >> 4;
460 match val & 0xf {
461 COMPLETED => ReturnCode::Completed(amt),
462 DROPPED => ReturnCode::Dropped(amt),
463 CANCELLED => ReturnCode::Cancelled(amt),
464 _ => panic!("unknown return code {val:#x}"),
465 }
466 }
467}
468
469/// Starts execution of the `task` provided, an asynchronous computation.
470///
471/// This is used for async-lifted exports at their definition site. The
472/// representation of the export is `task` and this function is called from the
473/// entrypoint. The code returned here is the same as the callback associated
474/// with this export, and the callback will be used if this task doesn't exit
475/// immediately with its result.
476#[doc(hidden)]
477pub fn start_task(task: impl Future<Output = ()> + 'static) -> i32 {
478 // Allocate a new `FutureState` which will track all state necessary for
479 // our exported task.
480 let state = Box::into_raw(Box::new(FutureState::new(Box::pin(task))));
481
482 // Store our `FutureState` into our context-local-storage slot and then
483 // pretend we got EVENT_NONE to kick off everything.
484 //
485 // SAFETY: we should own `context.set` as we're the root level exported
486 // task, and then `callback` is only invoked when context-local storage is
487 // valid.
488 unsafe {
489 assert!(context_get().is_null());
490 context_set(state.cast());
491 callback(EVENT_NONE, 0, 0) as i32
492 }
493}
494
495/// Handle a progress notification from the host regarding either a call to an
496/// async-lowered import or a stream/future read/write operation.
497///
498/// # Unsafety
499///
500/// This function assumes that `context_get()` returns a `FutureState`.
501#[doc(hidden)]
502pub unsafe fn callback(event0: u32, event1: u32, event2: u32) -> u32 {
503 // Acquire our context-local state, assert it's not-null, and then reset
504 // the state to null while we're running to help prevent any unintended
505 // usage.
506 let state = context_get().cast::<FutureState<'static>>();
507 assert!(!state.is_null());
508 unsafe {
509 context_set(ptr::null_mut());
510 }
511
512 // Use `state` to run the `callback` function in the context of our event
513 // codes we received. If the callback decides to exit then we're done with
514 // our future so deallocate it. Otherwise put our future back in
515 // context-local storage and forward the code.
516 unsafe {
517 let rc = (*state).callback(event0, event1, event2);
518 if rc == CallbackCode::Exit {
519 drop(Box::from_raw(state));
520 } else {
521 context_set(state.cast());
522 }
523 rtdebug!(" => (cb) {rc:?}");
524 rc.encode()
525 }
526}
527
528/// Run the specified future to completion, returning the result.
529///
530/// This uses `waitable-set.wait` to poll for progress on any in-progress calls
531/// to async-lowered imports as necessary.
532// TODO: refactor so `'static` bounds aren't necessary
533pub fn block_on<T: 'static>(future: impl Future<Output = T>) -> T {
534 let mut result = None;
535 let mut state = FutureState::new(Box::pin(async {
536 result = Some(future.await);
537 }));
538 let mut event = (EVENT_NONE, 0, 0);
539 loop {
540 match state.callback(event.0, event.1, event.2) {
541 CallbackCode::Exit => {
542 drop(state);
543 break result.unwrap();
544 }
545 CallbackCode::Yield | CallbackCode::Poll(_) => {
546 event = state.waitable_set.as_ref().unwrap().poll()
547 }
548 CallbackCode::Wait(_) => event = state.waitable_set.as_ref().unwrap().wait(),
549 }
550 }
551}
552
553/// Call the `yield` canonical built-in function.
554///
555/// This yields control to the host temporarily, allowing other tasks to make
556/// progress. It's a good idea to call this inside a busy loop which does not
557/// otherwise ever yield control the host.
558///
559/// Note that this function is a blocking function, not an `async` function.
560/// That means that this is not an async yield which allows other tasks in this
561/// component to progress, but instead this will block the current function
562/// until the host gets back around to returning from this yield. Asynchronous
563/// functions should probably use [`yield_async`] instead.
564///
565/// # Return Value
566///
567/// This function returns a `bool` which indicates whether execution should
568/// continue after this yield point. A return value of `true` means that the
569/// task was not cancelled and execution should continue. A return value of
570/// `false`, however, means that the task was cancelled while it was suspended
571/// at this yield point. The caller should return back and exit from the task
572/// ASAP in this situation.
573pub fn yield_blocking() -> bool {
574 extern_wasm! {
575 #[link(wasm_import_module = "$root")]
576 extern "C" {
577 #[link_name = "[thread-yield]"]
578 fn yield_() -> bool;
579 }
580 }
581
582 // Note that the return value from the raw intrinsic is inverted, the
583 // canonical ABI returns "did this task get cancelled" while this function
584 // works as "should work continue going".
585 unsafe { !yield_() }
586}
587
588/// The asynchronous counterpart to [`yield_blocking`].
589///
590/// This function does not block the current task but instead gives the
591/// Rust-level executor a chance to yield control back to the host temporarily.
592/// This means that other Rust-level tasks may also be able to progress during
593/// this yield operation.
594///
595/// # Return Value
596///
597/// Unlike [`yield_blocking`] this function does not return anything. If this
598/// component task is cancelled while paused at this yield point then the future
599/// will be dropped and a Rust-level destructor will take over and clean up the
600/// task. It's not necessary to do anything with the return value of this
601/// function other than ensuring that you `.await` the function call.
602pub async fn yield_async() {
603 #[derive(Default)]
604 struct Yield {
605 yielded: bool,
606 }
607
608 impl Future for Yield {
609 type Output = ();
610
611 fn poll(mut self: Pin<&mut Self>, context: &mut Context<'_>) -> Poll<()> {
612 if self.yielded {
613 Poll::Ready(())
614 } else {
615 self.yielded = true;
616 context.waker().wake_by_ref();
617 Poll::Pending
618 }
619 }
620 }
621
622 Yield::default().await;
623}
624
625/// Call the `backpressure.set` canonical built-in function.
626///
627/// When `enabled` is `true`, this tells the host to defer any new calls to this
628/// component instance until further notice (i.e. until `backpressure.set` is
629/// called again with `enabled` set to `false`).
630#[deprecated = "use backpressure_{inc,dec} instead"]
631pub fn backpressure_set(enabled: bool) {
632 extern_wasm! {
633 #[link(wasm_import_module = "$root")]
634 extern "C" {
635 #[link_name = "[backpressure-set]"]
636 fn backpressure_set(_: i32);
637 }
638 }
639
640 unsafe { backpressure_set(if enabled { 1 } else { 0 }) }
641}
642
643/// Call the `backpressure.inc` canonical built-in function.
644pub fn backpressure_inc() {
645 extern_wasm! {
646 #[link(wasm_import_module = "$root")]
647 extern "C" {
648 #[link_name = "[backpressure-inc]"]
649 fn backpressure_inc();
650 }
651 }
652
653 unsafe { backpressure_inc() }
654}
655
656/// Call the `backpressure.dec` canonical built-in function.
657pub fn backpressure_dec() {
658 extern_wasm! {
659 #[link(wasm_import_module = "$root")]
660 extern "C" {
661 #[link_name = "[backpressure-dec]"]
662 fn backpressure_dec();
663 }
664 }
665
666 unsafe { backpressure_dec() }
667}
668
669fn context_get() -> *mut u8 {
670 extern_wasm! {
671 #[link(wasm_import_module = "$root")]
672 extern "C" {
673 #[link_name = "[context-get-0]"]
674 fn get() -> *mut u8;
675 }
676 }
677
678 unsafe { get() }
679}
680
681unsafe fn context_set(value: *mut u8) {
682 extern_wasm! {
683 #[link(wasm_import_module = "$root")]
684 extern "C" {
685 #[link_name = "[context-set-0]"]
686 fn set(value: *mut u8);
687 }
688 }
689
690 unsafe { set(value) }
691}
692
693#[doc(hidden)]
694pub struct TaskCancelOnDrop {
695 _priv: (),
696}
697
698impl TaskCancelOnDrop {
699 #[doc(hidden)]
700 pub fn new() -> TaskCancelOnDrop {
701 TaskCancelOnDrop { _priv: () }
702 }
703
704 #[doc(hidden)]
705 pub fn forget(self) {
706 mem::forget(self);
707 }
708}
709
710impl Drop for TaskCancelOnDrop {
711 fn drop(&mut self) {
712 extern_wasm! {
713 #[link(wasm_import_module = "[export]$root")]
714 extern "C" {
715 #[link_name = "[task-cancel]"]
716 fn cancel();
717 }
718 }
719
720 unsafe { cancel() }
721 }
722}