xref: /wasmtime-44.0.1/crates/c-api/src/store.rs (revision b221fca7)
1 use crate::{ForeignData, wasm_engine_t, wasmtime_error_t, wasmtime_val_t};
2 use std::cell::UnsafeCell;
3 use std::ffi::c_void;
4 use std::sync::Arc;
5 use wasmtime::{
6     AsContext, AsContextMut, Caller, Store, StoreContext, StoreContextMut, StoreLimits,
7     StoreLimitsBuilder, UpdateDeadline, Val,
8 };
9 
10 // Store-related type aliases for `wasm.h` APIs. Not for use with `wasmtime.h`
11 // APIs!
12 pub type WasmStoreData = ();
13 pub type WasmStore = Store<WasmStoreData>;
14 pub type WasmStoreContext<'a> = StoreContext<'a, WasmStoreData>;
15 pub type WasmStoreContextMut<'a> = StoreContextMut<'a, WasmStoreData>;
16 
17 /// This representation of a `Store` is used to implement the `wasm.h` API (and
18 /// *not* the `wasmtime.h` API!)
19 ///
20 /// This is stored alongside `Func` and such for `wasm.h` so each object is
21 /// independently owned. The usage of `Arc` here is mostly to just get it to be
22 /// safe to drop across multiple threads, but otherwise acquiring the `context`
23 /// values from this struct is considered unsafe due to it being unknown how the
24 /// aliasing is working on the C side of things.
25 ///
26 /// The aliasing requirements are documented in the C API `wasm.h` itself (at
27 /// least Wasmtime's implementation).
28 #[derive(Clone)]
29 pub struct WasmStoreRef {
30     store: Arc<UnsafeCell<WasmStore>>,
31 }
32 
33 impl WasmStoreRef {
34     pub unsafe fn context(&self) -> WasmStoreContext<'_> {
35         (*self.store.get()).as_context()
36     }
37 
38     pub unsafe fn context_mut(&mut self) -> WasmStoreContextMut<'_> {
39         (*self.store.get()).as_context_mut()
40     }
41 }
42 
43 #[repr(C)]
44 #[derive(Clone)]
45 pub struct wasm_store_t {
46     pub(crate) store: WasmStoreRef,
47 }
48 
49 wasmtime_c_api_macros::declare_own!(wasm_store_t);
50 
51 #[unsafe(no_mangle)]
52 pub extern "C" fn wasm_store_new(engine: &wasm_engine_t) -> Box<wasm_store_t> {
53     let engine = &engine.engine;
54     let store = Store::new(engine, ());
55     Box::new(wasm_store_t {
56         store: WasmStoreRef {
57             store: Arc::new(UnsafeCell::new(store)),
58         },
59     })
60 }
61 
62 // Store-related type aliases for `wasmtime.h` APIs. Not for use with `wasm.h`
63 // APIs!
64 pub type WasmtimeStore = Store<WasmtimeStoreData>;
65 pub type WasmtimeStoreContext<'a> = StoreContext<'a, WasmtimeStoreData>;
66 pub type WasmtimeStoreContextMut<'a> = StoreContextMut<'a, WasmtimeStoreData>;
67 pub type WasmtimeCaller<'a> = Caller<'a, WasmtimeStoreData>;
68 
69 /// Representation of a `Store` for `wasmtime.h` This notably tries to move more
70 /// burden of aliasing on the caller rather than internally, allowing for a more
71 /// raw representation of contexts and such that requires less `unsafe` in the
72 /// implementation.
73 ///
74 /// Note that this notably carries `WasmtimeStoreData` as a payload which allows
75 /// storing foreign data and configuring WASI as well.
76 #[repr(C)]
77 pub struct wasmtime_store_t {
78     pub(crate) store: WasmtimeStore,
79 }
80 
81 wasmtime_c_api_macros::declare_own!(wasmtime_store_t);
82 
83 pub struct WasmtimeStoreData {
84     foreign: crate::ForeignData,
85     #[cfg(feature = "wasi")]
86     pub(crate) wasi: Option<wasmtime_wasi::preview1::WasiP1Ctx>,
87 
88     /// Temporary storage for usage during a wasm->host call to store values
89     /// in a slice we pass to the C API.
90     pub hostcall_val_storage: Vec<wasmtime_val_t>,
91 
92     /// Temporary storage for usage during host->wasm calls, same as above but
93     /// for a different direction.
94     pub wasm_val_storage: Vec<Val>,
95 
96     /// Limits for the store.
97     pub store_limits: StoreLimits,
98 
99     #[cfg(feature = "component-model")]
100     pub(crate) resource_table: wasmtime::component::ResourceTable,
101 
102     #[cfg(all(feature = "component-model", feature = "wasi"))]
103     pub(crate) wasip2: Option<wasmtime_wasi::p2::WasiCtx>,
104 }
105 
106 #[cfg(all(feature = "component-model", feature = "wasi"))]
107 impl wasmtime_wasi::p2::IoView for WasmtimeStoreData {
108     fn table(&mut self) -> &mut wasmtime_wasi::ResourceTable {
109         &mut self.resource_table
110     }
111 }
112 
113 #[cfg(all(feature = "component-model", feature = "wasi"))]
114 impl wasmtime_wasi::p2::WasiView for WasmtimeStoreData {
115     fn ctx(&mut self) -> &mut wasmtime_wasi::p2::WasiCtx {
116         self.wasip2.as_mut().unwrap()
117     }
118 }
119 
120 #[unsafe(no_mangle)]
121 pub extern "C" fn wasmtime_store_new(
122     engine: &wasm_engine_t,
123     data: *mut c_void,
124     finalizer: Option<extern "C" fn(*mut c_void)>,
125 ) -> Box<wasmtime_store_t> {
126     Box::new(wasmtime_store_t {
127         store: Store::new(
128             &engine.engine,
129             WasmtimeStoreData {
130                 foreign: ForeignData { data, finalizer },
131                 #[cfg(feature = "wasi")]
132                 wasi: None,
133                 hostcall_val_storage: Vec::new(),
134                 wasm_val_storage: Vec::new(),
135                 store_limits: StoreLimits::default(),
136                 #[cfg(feature = "component-model")]
137                 resource_table: wasmtime::component::ResourceTable::default(),
138                 #[cfg(all(feature = "component-model", feature = "wasi"))]
139                 wasip2: None,
140             },
141         ),
142     })
143 }
144 
145 pub type wasmtime_update_deadline_kind_t = u8;
146 pub const WASMTIME_UPDATE_DEADLINE_CONTINUE: wasmtime_update_deadline_kind_t = 0;
147 pub const WASMTIME_UPDATE_DEADLINE_YIELD: wasmtime_update_deadline_kind_t = 1;
148 
149 #[unsafe(no_mangle)]
150 pub extern "C" fn wasmtime_store_epoch_deadline_callback(
151     store: &mut wasmtime_store_t,
152     func: extern "C" fn(
153         WasmtimeStoreContextMut<'_>,
154         *mut c_void,
155         *mut u64,
156         *mut wasmtime_update_deadline_kind_t,
157     ) -> Option<Box<wasmtime_error_t>>,
158     data: *mut c_void,
159     finalizer: Option<extern "C" fn(*mut c_void)>,
160 ) {
161     let foreign = crate::ForeignData { data, finalizer };
162     store.store.epoch_deadline_callback(move |mut store_ctx| {
163         let _ = &foreign; // Move foreign into this closure
164         let mut delta: u64 = 0;
165         let mut kind = WASMTIME_UPDATE_DEADLINE_CONTINUE;
166         let result = (func)(
167             store_ctx.as_context_mut(),
168             foreign.data,
169             &mut delta as *mut u64,
170             &mut kind as *mut wasmtime_update_deadline_kind_t,
171         );
172         match result {
173             Some(err) => Err((*err).into()),
174             None if kind == WASMTIME_UPDATE_DEADLINE_CONTINUE => {
175                 Ok(UpdateDeadline::Continue(delta))
176             }
177             #[cfg(feature = "async")]
178             None if kind == WASMTIME_UPDATE_DEADLINE_YIELD => Ok(UpdateDeadline::Yield(delta)),
179             _ => panic!("unknown wasmtime_update_deadline_kind_t: {kind}"),
180         }
181     });
182 }
183 
184 #[unsafe(no_mangle)]
185 pub extern "C" fn wasmtime_store_context(
186     store: &mut wasmtime_store_t,
187 ) -> WasmtimeStoreContextMut<'_> {
188     store.store.as_context_mut()
189 }
190 
191 #[unsafe(no_mangle)]
192 pub extern "C" fn wasmtime_store_limiter(
193     store: &mut wasmtime_store_t,
194     memory_size: i64,
195     table_elements: i64,
196     instances: i64,
197     tables: i64,
198     memories: i64,
199 ) {
200     let mut limiter = StoreLimitsBuilder::new();
201     if memory_size >= 0 {
202         limiter = limiter.memory_size(memory_size as usize);
203     }
204     if table_elements >= 0 {
205         limiter = limiter.table_elements(table_elements as usize);
206     }
207     if instances >= 0 {
208         limiter = limiter.instances(instances as usize);
209     }
210     if tables >= 0 {
211         limiter = limiter.tables(tables as usize);
212     }
213     if memories >= 0 {
214         limiter = limiter.memories(memories as usize);
215     }
216     store.store.data_mut().store_limits = limiter.build();
217     store.store.limiter(|data| &mut data.store_limits);
218 }
219 
220 #[unsafe(no_mangle)]
221 pub extern "C" fn wasmtime_context_get_data(store: WasmtimeStoreContext<'_>) -> *mut c_void {
222     store.data().foreign.data
223 }
224 
225 #[unsafe(no_mangle)]
226 pub extern "C" fn wasmtime_context_set_data(
227     mut store: WasmtimeStoreContextMut<'_>,
228     data: *mut c_void,
229 ) {
230     store.data_mut().foreign.data = data;
231 }
232 
233 #[cfg(feature = "wasi")]
234 #[unsafe(no_mangle)]
235 pub extern "C" fn wasmtime_context_set_wasi(
236     mut context: WasmtimeStoreContextMut<'_>,
237     wasi: Box<crate::wasi_config_t>,
238 ) -> Option<Box<wasmtime_error_t>> {
239     crate::handle_result(wasi.into_wasi_ctx(), |wasi| {
240         context.data_mut().wasi = Some(wasi);
241     })
242 }
243 
244 #[cfg(all(feature = "component-model", feature = "wasi"))]
245 #[unsafe(no_mangle)]
246 pub unsafe extern "C" fn wasmtime_context_set_wasip2(
247     mut context: WasmtimeStoreContextMut<'_>,
248     mut config: Box<crate::wasmtime_wasip2_config_t>,
249 ) {
250     context.data_mut().wasip2 = Some(config.builder.build());
251 }
252 
253 #[unsafe(no_mangle)]
254 pub extern "C" fn wasmtime_context_gc(mut context: WasmtimeStoreContextMut<'_>) {
255     context.gc(None);
256 }
257 
258 #[unsafe(no_mangle)]
259 pub extern "C" fn wasmtime_context_set_fuel(
260     mut store: WasmtimeStoreContextMut<'_>,
261     fuel: u64,
262 ) -> Option<Box<wasmtime_error_t>> {
263     crate::handle_result(store.set_fuel(fuel), |()| {})
264 }
265 
266 #[unsafe(no_mangle)]
267 pub extern "C" fn wasmtime_context_get_fuel(
268     store: WasmtimeStoreContext<'_>,
269     fuel: &mut u64,
270 ) -> Option<Box<wasmtime_error_t>> {
271     crate::handle_result(store.get_fuel(), |amt| {
272         *fuel = amt;
273     })
274 }
275 
276 #[unsafe(no_mangle)]
277 pub extern "C" fn wasmtime_context_set_epoch_deadline(
278     mut store: WasmtimeStoreContextMut<'_>,
279     ticks_beyond_current: u64,
280 ) {
281     store.set_epoch_deadline(ticks_beyond_current);
282 }
283