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