wasmer_wasix/syscalls/wasix/
context_create.rs

1use crate::{WasiEnv, WasiError};
2use futures::FutureExt;
3use tracing::instrument;
4use wasmer::{
5    AsStoreRef, Function, FunctionEnvMut, MemorySize, RuntimeError, StoreMut, TypedFunction, Value,
6    WasmPtr,
7};
8use wasmer_wasix_types::wasi::Errno;
9
10/// Return the function corresponding to the given entrypoint index if it exists and has the signature `() -> ()`
11pub fn lookup_typechecked_entrypoint(
12    data: &WasiEnv,
13    mut store: &mut StoreMut<'_>,
14    entrypoint_id: u32,
15) -> Result<Function, Errno> {
16    let entrypoint = match data
17        .inner()
18        .indirect_function_table_lookup(&mut store, entrypoint_id)
19    {
20        Ok(func) => func,
21        Err(e) => {
22            tracing::trace!(
23                "Failed to lookup entrypoint function {}: {:?}",
24                entrypoint_id,
25                e
26            );
27            return Err(Errno::Inval);
28        }
29    };
30
31    // TODO: Remove this check and return a TypedFunction once all backends support types
32    #[cfg(not(feature = "js"))]
33    {
34        let entrypoint_type = entrypoint.ty(&store);
35        if !entrypoint_type.params().is_empty() && !entrypoint_type.results().is_empty() {
36            tracing::trace!(
37                "Entrypoint function {entrypoint_id} has invalid signature: expected () -> (), got {:?} -> {:?}",
38                entrypoint_type.params(),
39                entrypoint_type.results()
40            );
41            return Err(Errno::Inval);
42        }
43    }
44
45    Ok(entrypoint)
46}
47
48/// Create a new context.
49///
50/// Creates a new context in the suspended state. On its first resumption,
51/// `entrypoint` is invoked within that context.
52///
53/// Refer to the wasix-libc [`wasix/context.h`] header for authoritative
54/// documentation.
55///
56/// [`wasix/context.h`]: https://github.com/wasix-org/wasix-libc/blob/main/libc-bottom-half/headers/public/wasix/context.h
57#[instrument(level = "trace", skip(ctx), ret)]
58pub fn context_create<M: MemorySize>(
59    mut ctx: FunctionEnvMut<'_, WasiEnv>,
60    new_context_ptr: WasmPtr<u64, M>,
61    entrypoint: u32,
62) -> Result<Errno, WasiError> {
63    WasiEnv::do_pending_operations(&mut ctx)?;
64
65    // Verify that we are in an async context
66    // We need to do this first, before we borrow the store mutably
67    let Some(async_store) = ctx.as_store_async() else {
68        tracing::warn!(
69            "The WASIX context-switching API is only available in engines supporting async execution"
70        );
71        return Ok(Errno::Notsup);
72    };
73
74    let (data, mut store) = ctx.data_and_store_mut();
75
76    // Get the context-switching environment
77    let Some(environment) = &data.context_switching_environment else {
78        tracing::warn!(
79            "The WASIX context-switching API is only available in a context-switching environment"
80        );
81        return Ok(Errno::Notsup);
82    };
83
84    // Lookup and check the entrypoint function
85    let entrypoint = match lookup_typechecked_entrypoint(data, &mut store, entrypoint) {
86        Ok(func) => func,
87        Err(err) => {
88            return Ok(err);
89        }
90    };
91
92    // Create the new context
93    let new_context_id = environment.create_context(
94        entrypoint
95            .call_async(&async_store, vec![])
96            .map(|r| r.map(|_| ())),
97    );
98
99    // Write the new context ID into memory
100    let memory = unsafe { data.memory_view(&store) };
101    wasi_try_mem_ok!(new_context_ptr.write(&memory, new_context_id));
102
103    // Return success
104    return Ok(Errno::Success);
105}