Skip to main content

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