1 /**
2  * \file wasmtime/store.hh
3  */
4 
5 #ifndef WASMTIME_STORE_HH
6 #define WASMTIME_STORE_HH
7 
8 #include <any>
9 #include <memory>
10 #include <optional>
11 #include <wasmtime/conf.h>
12 #include <wasmtime/engine.hh>
13 #include <wasmtime/error.hh>
14 #include <wasmtime/helpers.hh>
15 #include <wasmtime/store.h>
16 #include <wasmtime/wasi.hh>
17 
18 namespace wasmtime {
19 
20 class Caller;
21 
22 /// \brief An enum for the behavior before extending the epoch deadline.
23 enum class DeadlineKind {
24   /// Directly continue to updating the deadline and executing WebAssembly.
25   Continue = WASMTIME_UPDATE_DEADLINE_CONTINUE,
26   /// Yield control (via async support) then update the deadline.
27   Yield = WASMTIME_UPDATE_DEADLINE_YIELD,
28 };
29 
30 /**
31  * \brief Owner of all WebAssembly objects
32  *
33  * A `Store` owns all WebAssembly objects such as instances, globals, functions,
34  * memories, etc. A `Store` is one of the main central points about working with
35  * WebAssembly since it's an argument to almost all APIs. The `Store` serves as
36  * a form of "context" to give meaning to the pointers of `Func` and friends.
37  *
38  * A `Store` can be sent between threads but it cannot generally be shared
39  * concurrently between threads. Memory associated with WebAssembly instances
40  * will be deallocated when the `Store` is deallocated.
41  */
42 class Store {
43   WASMTIME_OWN_WRAPPER(Store, wasmtime_store);
44 
45 private:
46   static void finalizer(void *ptr) {
47     std::unique_ptr<std::any> _ptr(static_cast<std::any *>(ptr));
48   }
49 
50 public:
51   /// Creates a new `Store` within the provided `Engine`.
52   explicit Store(Engine &engine)
53       : ptr(wasmtime_store_new(engine.capi(), nullptr, finalizer)) {}
54 
55   /**
56    * \brief An interior pointer into a `Store`.
57    *
58    * A `Context` object is created from either a `Store` or a `Caller`. It is an
59    * interior pointer into a `Store` and cannot be used outside the lifetime of
60    * the original object it was created from.
61    *
62    * This object is an argument to most APIs in Wasmtime but typically doesn't
63    * need to be constructed explicitly since it can be created from a `Store&`
64    * or a `Caller&`.
65    */
66   class Context {
67     friend class Global;
68     friend class Table;
69     friend class Memory;
70     friend class Func;
71     friend class Instance;
72     friend class Linker;
73     friend class ExternRef;
74     friend class AnyRef;
75     friend class Val;
76     friend class Store;
77     wasmtime_context_t *ptr;
78 
79   public:
80     /// Creates a context from the raw C API pointer.
81     explicit Context(wasmtime_context_t *ptr) : ptr(ptr) {}
82 
83     /// Creates a context referencing the provided `Store`.
84     Context(Store &store) : Context(wasmtime_store_context(store.ptr.get())) {}
85     /// Creates a context referencing the provided `Store`.
86     Context(Store *store) : Context(*store) {}
87     /// Creates a context referencing the provided `Caller`.
88     Context(Caller &caller);
89     /// Creates a context referencing the provided `Caller`.
90     Context(Caller *caller);
91 
92     /// Runs a garbage collection pass in the referenced store to collect loose
93     /// `externref` values, if any are available.
94     Result<std::monostate> gc() {
95       auto *error = wasmtime_context_gc(ptr);
96       if (error != nullptr) {
97         return Error(error);
98       }
99       return std::monostate();
100     }
101 
102     /// Injects fuel to be consumed within this store.
103     ///
104     /// Stores start with 0 fuel and if `Config::consume_fuel` is enabled then
105     /// this is required if you want to let WebAssembly actually execute.
106     ///
107     /// Returns an error if fuel consumption isn't enabled.
108     Result<std::monostate> set_fuel(uint64_t fuel) {
109       auto *error = wasmtime_context_set_fuel(ptr, fuel);
110       if (error != nullptr) {
111         return Error(error);
112       }
113       return std::monostate();
114     }
115 
116     /// Returns the amount of fuel consumed so far by executing WebAssembly.
117     ///
118     /// Returns `std::nullopt` if fuel consumption is not enabled.
119     Result<uint64_t> get_fuel() const {
120       uint64_t fuel = 0;
121       auto *error = wasmtime_context_get_fuel(ptr, &fuel);
122       if (error != nullptr) {
123         return Error(error);
124       }
125       return fuel;
126     }
127 
128     /// Set user specified data associated with this store.
129     void set_data(std::any data) const {
130       finalizer(static_cast<std::any *>(wasmtime_context_get_data(ptr)));
131       wasmtime_context_set_data(
132           ptr, std::make_unique<std::any>(std::move(data)).release());
133     }
134 
135     /// Get user specified data associated with this store.
136     std::any &get_data() const {
137       return *static_cast<std::any *>(wasmtime_context_get_data(ptr));
138     }
139 
140 #ifdef WASMTIME_FEATURE_WASI
141     /// Configures the WASI state used by this store.
142     ///
143     /// This will only have an effect if used in conjunction with
144     /// `Linker::define_wasi` because otherwise no host functions will use the
145     /// WASI state.
146     Result<std::monostate> set_wasi(WasiConfig config) {
147       auto *error = wasmtime_context_set_wasi(ptr, config.capi_release());
148       if (error != nullptr) {
149         return Error(error);
150       }
151       return std::monostate();
152     }
153 #endif // WASMTIME_FEATURE_WASI
154 
155     /// Configures this store's epoch deadline to be the specified number of
156     /// ticks beyond the engine's current epoch.
157     ///
158     /// By default the deadline is the current engine's epoch, immediately
159     /// interrupting code if epoch interruption is enabled. This must be called
160     /// to extend the deadline to allow interruption.
161     void set_epoch_deadline(uint64_t ticks_beyond_current) {
162       wasmtime_context_set_epoch_deadline(ptr, ticks_beyond_current);
163     }
164 
165     /// \brief Returns the underlying C API pointer.
166     const wasmtime_context_t *capi() const { return ptr; }
167 
168     /// \brief Returns the underlying C API pointer.
169     wasmtime_context_t *capi() { return ptr; }
170   };
171 
172   /// \brief Provides limits for a store. Used by hosts to limit resource
173   /// consumption of instances. Use negative value to keep the default value
174   /// for the limit.
175   ///
176   /// \param memory_size the maximum number of bytes a linear memory can grow
177   /// to. Growing a linear memory beyond this limit will fail. By default,
178   /// linear memory will not be limited.
179   ///
180   /// \param table_elements the maximum number of elements in a table.
181   /// Growing a table beyond this limit will fail. By default, table elements
182   /// will not be limited.
183   ///
184   /// \param instances the maximum number of instances that can be created
185   /// for a Store. Module instantiation will fail if this limit is exceeded.
186   /// This value defaults to 10,000.
187   ///
188   /// \param tables the maximum number of tables that can be created for a
189   /// Store. Module instantiation will fail if this limit is exceeded. This
190   /// value defaults to 10,000.
191   ///
192   /// \param memories the maximum number of linear
193   /// memories that can be created for a Store. Instantiation will fail with an
194   /// error if this limit is exceeded. This value defaults to 10,000.
195   ///
196   /// Use any negative value for the parameters that should be kept on
197   /// the default values.
198   ///
199   /// Note that the limits are only used to limit the creation/growth of
200   /// resources in the future, this does not retroactively attempt to apply
201   /// limits to the store.
202   void limiter(int64_t memory_size, int64_t table_elements, int64_t instances,
203                int64_t tables, int64_t memories) {
204     wasmtime_store_limiter(ptr.get(), memory_size, table_elements, instances,
205                            tables, memories);
206   }
207 
208   /// \brief Configures epoch deadline callback to C function.
209   ///
210   /// This function configures a store-local callback function that will be
211   /// called when the running WebAssembly function has exceeded its epoch
212   /// deadline. That function can:
213   /// - return an error to terminate the function
214   /// - set the delta argument and return DeadlineKind::Continue to update the
215   ///   epoch deadline delta and resume function execution.
216   /// - set the delta argument, update the epoch deadline, and return
217   ///   DeadlineKind::Yield to yield (via async support) and resume function
218   ///   execution.
219   template <typename F,
220             std::enable_if_t<std::is_invocable_r_v<Result<DeadlineKind>, F,
221                                                    Context, uint64_t &>,
222                              bool> = true>
223   void epoch_deadline_callback(F &&f) {
224     wasmtime_store_epoch_deadline_callback(
225         ptr.get(), raw_epoch_callback<std::remove_reference_t<F>>,
226         std::make_unique<std::remove_reference_t<F>>(std::forward<F>(f))
227             .release(),
228         raw_epoch_finalizer<std::remove_reference_t<F>>);
229   }
230 
231   /// Explicit function to acquire a `Context` from this store.
232   Context context() { return this; }
233 
234   /// Runs a garbage collection pass in the referenced store to collect loose
235   /// GC-managed objects, if any are available.
236   Result<std::monostate> gc() { return context().gc(); }
237 
238 private:
239   template <typename F>
240   static wasmtime_error_t *
241   raw_epoch_callback(wasmtime_context_t *context, void *data,
242                      uint64_t *epoch_deadline_delta,
243                      wasmtime_update_deadline_kind_t *update_kind) {
244     auto &callback = *static_cast<F *>(data);
245     Context ctx(context);
246     auto result = callback(ctx, *epoch_deadline_delta);
247 
248     if (!result) {
249       return result.err().capi_release();
250     }
251     *update_kind = static_cast<wasmtime_update_deadline_kind_t>(result.ok());
252     return nullptr;
253   }
254 
255   template <typename F> static void raw_epoch_finalizer(void *data) {
256     std::unique_ptr<F> _ptr(static_cast<F *>(data));
257   }
258 };
259 
260 } // namespace wasmtime
261 
262 #endif // WASMTIME_STORE_HH
263