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