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