1 use crate::prelude::*;
2 #[cfg(feature = "runtime")]
3 use crate::runtime::type_registry::TypeRegistry;
4 #[cfg(feature = "runtime")]
5 use crate::runtime::vm::GcRuntime;
6 use crate::sync::OnceLock;
7 use crate::Config;
8 use alloc::sync::Arc;
9 use core::sync::atomic::{AtomicU64, Ordering};
10 #[cfg(any(feature = "cranelift", feature = "winch"))]
11 use object::write::{Object, StandardSegment};
12 use object::SectionKind;
13 #[cfg(feature = "std")]
14 use std::{fs::File, path::Path};
15 use wasmparser::WasmFeatures;
16 use wasmtime_environ::obj;
17 use wasmtime_environ::{FlagValue, ObjectKind, Tunables};
18 
19 mod serialization;
20 
21 /// An `Engine` which is a global context for compilation and management of wasm
22 /// modules.
23 ///
24 /// An engine can be safely shared across threads and is a cheap cloneable
25 /// handle to the actual engine. The engine itself will be deallocated once all
26 /// references to it have gone away.
27 ///
28 /// Engines store global configuration preferences such as compilation settings,
29 /// enabled features, etc. You'll likely only need at most one of these for a
30 /// program.
31 ///
32 /// ## Engines and `Clone`
33 ///
34 /// Using `clone` on an `Engine` is a cheap operation. It will not create an
35 /// entirely new engine, but rather just a new reference to the existing engine.
36 /// In other words it's a shallow copy, not a deep copy.
37 ///
38 /// ## Engines and `Default`
39 ///
40 /// You can create an engine with default configuration settings using
41 /// `Engine::default()`. Be sure to consult the documentation of [`Config`] for
42 /// default settings.
43 #[derive(Clone)]
44 pub struct Engine {
45     inner: Arc<EngineInner>,
46 }
47 
48 struct EngineInner {
49     config: Config,
50     features: WasmFeatures,
51     tunables: Tunables,
52     #[cfg(any(feature = "cranelift", feature = "winch"))]
53     compiler: Box<dyn wasmtime_environ::Compiler>,
54     #[cfg(feature = "runtime")]
55     allocator: Box<dyn crate::runtime::vm::InstanceAllocator + Send + Sync>,
56     #[cfg(feature = "runtime")]
57     gc_runtime: Option<Arc<dyn GcRuntime>>,
58     #[cfg(feature = "runtime")]
59     profiler: Box<dyn crate::profiling_agent::ProfilingAgent>,
60     #[cfg(feature = "runtime")]
61     signatures: TypeRegistry,
62     #[cfg(feature = "runtime")]
63     epoch: AtomicU64,
64 
65     /// One-time check of whether the compiler's settings, if present, are
66     /// compatible with the native host.
67     #[cfg(any(feature = "cranelift", feature = "winch"))]
68     compatible_with_native_host: OnceLock<Result<(), String>>,
69 }
70 
71 impl Default for Engine {
72     fn default() -> Engine {
73         Engine::new(&Config::default()).unwrap()
74     }
75 }
76 
77 impl Engine {
78     /// Creates a new [`Engine`] with the specified compilation and
79     /// configuration settings.
80     ///
81     /// # Errors
82     ///
83     /// This method can fail if the `config` is invalid or some
84     /// configurations are incompatible.
85     ///
86     /// For example, feature `reference_types` will need to set
87     /// the compiler setting `enable_safepoints` and `unwind_info`
88     /// to `true`, but explicitly disable these two compiler settings
89     /// will cause errors.
90     pub fn new(config: &Config) -> Result<Engine> {
91         let config = config.clone();
92         let (tunables, features) = config.validate()?;
93 
94         #[cfg(feature = "runtime")]
95         if tunables.signals_based_traps {
96             // Ensure that crate::runtime::vm's signal handlers are
97             // configured. This is the per-program initialization required for
98             // handling traps, such as configuring signals, vectored exception
99             // handlers, etc.
100             #[cfg(all(feature = "signals-based-traps", not(miri)))]
101             crate::runtime::vm::init_traps(config.macos_use_mach_ports);
102             #[cfg(feature = "debug-builtins")]
103             crate::runtime::vm::debug_builtins::ensure_exported();
104         }
105 
106         #[cfg(any(feature = "cranelift", feature = "winch"))]
107         let (config, compiler) = config.build_compiler(&tunables, features)?;
108 
109         Ok(Engine {
110             inner: Arc::new(EngineInner {
111                 #[cfg(any(feature = "cranelift", feature = "winch"))]
112                 compiler,
113                 #[cfg(feature = "runtime")]
114                 allocator: config.build_allocator(&tunables)?,
115                 #[cfg(feature = "runtime")]
116                 gc_runtime: config.build_gc_runtime()?,
117                 #[cfg(feature = "runtime")]
118                 profiler: config.build_profiler()?,
119                 #[cfg(feature = "runtime")]
120                 signatures: TypeRegistry::new(),
121                 #[cfg(feature = "runtime")]
122                 epoch: AtomicU64::new(0),
123                 #[cfg(any(feature = "cranelift", feature = "winch"))]
124                 compatible_with_native_host: OnceLock::new(),
125                 config,
126                 tunables,
127                 features,
128             }),
129         })
130     }
131 
132     /// Returns the configuration settings that this engine is using.
133     #[inline]
134     pub fn config(&self) -> &Config {
135         &self.inner.config
136     }
137 
138     #[inline]
139     pub(crate) fn features(&self) -> WasmFeatures {
140         self.inner.features
141     }
142 
143     pub(crate) fn run_maybe_parallel<
144         A: Send,
145         B: Send,
146         E: Send,
147         F: Fn(A) -> Result<B, E> + Send + Sync,
148     >(
149         &self,
150         input: Vec<A>,
151         f: F,
152     ) -> Result<Vec<B>, E> {
153         if self.config().parallel_compilation {
154             #[cfg(feature = "parallel-compilation")]
155             {
156                 use rayon::prelude::*;
157                 return input
158                     .into_par_iter()
159                     .map(|a| f(a))
160                     .collect::<Result<Vec<B>, E>>();
161             }
162         }
163 
164         // In case the parallel-compilation feature is disabled or the parallel_compilation config
165         // was turned off dynamically fallback to the non-parallel version.
166         input
167             .into_iter()
168             .map(|a| f(a))
169             .collect::<Result<Vec<B>, E>>()
170     }
171 
172     /// Take a weak reference to this engine.
173     pub fn weak(&self) -> EngineWeak {
174         EngineWeak {
175             inner: Arc::downgrade(&self.inner),
176         }
177     }
178 
179     #[inline]
180     pub(crate) fn tunables(&self) -> &Tunables {
181         &self.inner.tunables
182     }
183 
184     /// Returns whether the engine `a` and `b` refer to the same configuration.
185     #[inline]
186     pub fn same(a: &Engine, b: &Engine) -> bool {
187         Arc::ptr_eq(&a.inner, &b.inner)
188     }
189 
190     /// Returns whether the engine is configured to support async functions.
191     #[cfg(feature = "async")]
192     #[inline]
193     pub fn is_async(&self) -> bool {
194         self.config().async_support
195     }
196 
197     /// Detects whether the bytes provided are a precompiled object produced by
198     /// Wasmtime.
199     ///
200     /// This function will inspect the header of `bytes` to determine if it
201     /// looks like a precompiled core wasm module or a precompiled component.
202     /// This does not validate the full structure or guarantee that
203     /// deserialization will succeed, instead it helps higher-levels of the
204     /// stack make a decision about what to do next when presented with the
205     /// `bytes` as an input module.
206     ///
207     /// If the `bytes` looks like a precompiled object previously produced by
208     /// [`Module::serialize`](crate::Module::serialize),
209     /// [`Component::serialize`](crate::component::Component::serialize),
210     /// [`Engine::precompile_module`], or [`Engine::precompile_component`], then
211     /// this will return `Some(...)` indicating so. Otherwise `None` is
212     /// returned.
213     pub fn detect_precompiled(&self, bytes: &[u8]) -> Option<Precompiled> {
214         serialization::detect_precompiled_bytes(bytes)
215     }
216 
217     /// Like [`Engine::detect_precompiled`], but performs the detection on a file.
218     #[cfg(feature = "std")]
219     pub fn detect_precompiled_file(&self, path: impl AsRef<Path>) -> Result<Option<Precompiled>> {
220         serialization::detect_precompiled_file(path)
221     }
222 
223     /// Returns the target triple which this engine is compiling code for
224     /// and/or running code for.
225     pub(crate) fn target(&self) -> target_lexicon::Triple {
226         // If a compiler is configured, use that target.
227         #[cfg(any(feature = "cranelift", feature = "winch"))]
228         return self.compiler().triple().clone();
229 
230         // ... otherwise it's the native target
231         #[cfg(not(any(feature = "cranelift", feature = "winch")))]
232         return target_lexicon::Triple::host();
233     }
234 
235     /// Verify that this engine's configuration is compatible with loading
236     /// modules onto the native host platform.
237     ///
238     /// This method is used as part of `Module::new` to ensure that this
239     /// engine can indeed load modules for the configured compiler (if any).
240     /// Note that if cranelift is disabled this trivially returns `Ok` because
241     /// loaded serialized modules are checked separately.
242     pub(crate) fn check_compatible_with_native_host(&self) -> Result<()> {
243         #[cfg(any(feature = "cranelift", feature = "winch"))]
244         {
245             self.inner
246                 .compatible_with_native_host
247                 .get_or_init(|| self._check_compatible_with_native_host())
248                 .clone()
249                 .map_err(anyhow::Error::msg)
250         }
251         #[cfg(not(any(feature = "cranelift", feature = "winch")))]
252         {
253             Ok(())
254         }
255     }
256 
257     fn _check_compatible_with_native_host(&self) -> Result<(), String> {
258         #[cfg(any(feature = "cranelift", feature = "winch"))]
259         {
260             use target_lexicon::{Architecture, PointerWidth, Triple};
261 
262             let compiler = self.compiler();
263 
264             let target = compiler.triple();
265             let host = Triple::host();
266             let target_matches_host = || {
267                 // If the host target and target triple match, then it's valid
268                 // to run results of compilation on this host.
269                 if host == *target {
270                     return true;
271                 }
272 
273                 // Otherwise if there's a mismatch the only allowed
274                 // configuration at this time is that any target can run Pulley,
275                 // Wasmtime's interpreter. This only works though if the
276                 // pointer-width of pulley matches the pointer-width of the
277                 // host, so check that here.
278                 match host.pointer_width() {
279                     Ok(PointerWidth::U32) => target.architecture == Architecture::Pulley32,
280                     Ok(PointerWidth::U64) => target.architecture == Architecture::Pulley64,
281                     _ => false,
282                 }
283             };
284 
285             if !target_matches_host() {
286                 return Err(format!(
287                     "target '{target}' specified in the configuration does not match the host"
288                 ));
289             }
290 
291             // Also double-check all compiler settings
292             for (key, value) in compiler.flags().iter() {
293                 self.check_compatible_with_shared_flag(key, value)?;
294             }
295             for (key, value) in compiler.isa_flags().iter() {
296                 self.check_compatible_with_isa_flag(key, value)?;
297             }
298         }
299         Ok(())
300     }
301 
302     /// Checks to see whether the "shared flag", something enabled for
303     /// individual compilers, is compatible with the native host platform.
304     ///
305     /// This is used both when validating an engine's compilation settings are
306     /// compatible with the host as well as when deserializing modules from
307     /// disk to ensure they're compatible with the current host.
308     ///
309     /// Note that most of the settings here are not configured by users that
310     /// often. While theoretically possible via `Config` methods the more
311     /// interesting flags are the ISA ones below. Typically the values here
312     /// represent global configuration for wasm features. Settings here
313     /// currently rely on the compiler informing us of all settings, including
314     /// those disabled. Settings then fall in a few buckets:
315     ///
316     /// * Some settings must be enabled, such as `preserve_frame_pointers`.
317     /// * Some settings must have a particular value, such as
318     ///   `libcall_call_conv`.
319     /// * Some settings do not matter as to their value, such as `opt_level`.
320     pub(crate) fn check_compatible_with_shared_flag(
321         &self,
322         flag: &str,
323         value: &FlagValue,
324     ) -> Result<(), String> {
325         let target = self.target();
326         let ok = match flag {
327             // These settings must all have be enabled, since their value
328             // can affect the way the generated code performs or behaves at
329             // runtime.
330             "libcall_call_conv" => *value == FlagValue::Enum("isa_default".into()),
331             "preserve_frame_pointers" => *value == FlagValue::Bool(true),
332             "enable_probestack" => *value == FlagValue::Bool(true),
333             "probestack_strategy" => *value == FlagValue::Enum("inline".into()),
334             "enable_multi_ret_implicit_sret" => *value == FlagValue::Bool(true),
335 
336             // Features wasmtime doesn't use should all be disabled, since
337             // otherwise if they are enabled it could change the behavior of
338             // generated code.
339             "enable_llvm_abi_extensions" => *value == FlagValue::Bool(false),
340             "enable_pinned_reg" => *value == FlagValue::Bool(false),
341             "use_colocated_libcalls" => *value == FlagValue::Bool(false),
342             "use_pinned_reg_as_heap_base" => *value == FlagValue::Bool(false),
343 
344             // If reference types (or anything that depends on reference types,
345             // like typed function references and GC) are enabled this must be
346             // enabled, otherwise this setting can have any value.
347             "enable_safepoints" => {
348                 if self.features().contains(WasmFeatures::REFERENCE_TYPES) {
349                     *value == FlagValue::Bool(true)
350                 } else {
351                     return Ok(())
352                 }
353             }
354 
355             // Windows requires unwind info as part of its ABI.
356             "unwind_info" => {
357                 if target.operating_system == target_lexicon::OperatingSystem::Windows {
358                     *value == FlagValue::Bool(true)
359                 } else {
360                     return Ok(())
361                 }
362             }
363 
364             // These settings don't affect the interface or functionality of
365             // the module itself, so their configuration values shouldn't
366             // matter.
367             "enable_heap_access_spectre_mitigation"
368             | "enable_table_access_spectre_mitigation"
369             | "enable_nan_canonicalization"
370             | "enable_jump_tables"
371             | "enable_float"
372             | "enable_verifier"
373             | "enable_pcc"
374             | "regalloc_checker"
375             | "regalloc_verbose_logs"
376             | "regalloc_algorithm"
377             | "is_pic"
378             | "bb_padding_log2_minus_one"
379             | "machine_code_cfg_info"
380             | "tls_model" // wasmtime doesn't use tls right now
381             | "stack_switch_model" // wasmtime doesn't use stack switching right now
382             | "opt_level" // opt level doesn't change semantics
383             | "enable_alias_analysis" // alias analysis-based opts don't change semantics
384             | "probestack_size_log2" // probestack above asserted disabled
385             | "regalloc" // shouldn't change semantics
386             | "enable_incremental_compilation_cache_checks" // shouldn't change semantics
387             | "enable_atomics" => return Ok(()),
388 
389             // Everything else is unknown and needs to be added somewhere to
390             // this list if encountered.
391             _ => {
392                 return Err(format!("unknown shared setting {flag:?} configured to {value:?}"))
393             }
394         };
395 
396         if !ok {
397             return Err(format!(
398                 "setting {flag:?} is configured to {value:?} which is not supported",
399             ));
400         }
401         Ok(())
402     }
403 
404     /// Same as `check_compatible_with_native_host` except used for ISA-specific
405     /// flags. This is used to test whether a configured ISA flag is indeed
406     /// available on the host platform itself.
407     pub(crate) fn check_compatible_with_isa_flag(
408         &self,
409         flag: &str,
410         value: &FlagValue,
411     ) -> Result<(), String> {
412         match value {
413             // ISA flags are used for things like CPU features, so if they're
414             // disabled then it's compatible with the native host.
415             FlagValue::Bool(false) => return Ok(()),
416 
417             // Fall through below where we test at runtime that features are
418             // available.
419             FlagValue::Bool(true) => {}
420 
421             // Pulley's pointer_width must match the host.
422             FlagValue::Enum("pointer32") => {
423                 return if cfg!(target_pointer_width = "32") {
424                     Ok(())
425                 } else {
426                     Err("wrong host pointer width".to_string())
427                 }
428             }
429             FlagValue::Enum("pointer64") => {
430                 return if cfg!(target_pointer_width = "64") {
431                     Ok(())
432                 } else {
433                     Err("wrong host pointer width".to_string())
434                 }
435             }
436 
437             // Only `bool` values are supported right now, other settings would
438             // need more support here.
439             _ => {
440                 return Err(format!(
441                     "isa-specific feature {flag:?} configured to unknown value {value:?}"
442                 ))
443             }
444         }
445 
446         let host_feature = match flag {
447             // aarch64 features to detect
448             "has_lse" => "lse",
449             "has_pauth" => "paca",
450             "has_fp16" => "fp16",
451 
452             // aarch64 features which don't need detection
453             // No effect on its own.
454             "sign_return_address_all" => return Ok(()),
455             // The pointer authentication instructions act as a `NOP` when
456             // unsupported, so it is safe to enable them.
457             "sign_return_address" => return Ok(()),
458             // No effect on its own.
459             "sign_return_address_with_bkey" => return Ok(()),
460             // The `BTI` instruction acts as a `NOP` when unsupported, so it
461             // is safe to enable it regardless of whether the host supports it
462             // or not.
463             "use_bti" => return Ok(()),
464 
465             // s390x features to detect
466             "has_vxrs_ext2" => "vxrs_ext2",
467             "has_mie2" => "mie2",
468 
469             // x64 features to detect
470             "has_cmpxchg16b" => "cmpxchg16b",
471             "has_sse3" => "sse3",
472             "has_ssse3" => "ssse3",
473             "has_sse41" => "sse4.1",
474             "has_sse42" => "sse4.2",
475             "has_popcnt" => "popcnt",
476             "has_avx" => "avx",
477             "has_avx2" => "avx2",
478             "has_fma" => "fma",
479             "has_bmi1" => "bmi1",
480             "has_bmi2" => "bmi2",
481             "has_avx512bitalg" => "avx512bitalg",
482             "has_avx512dq" => "avx512dq",
483             "has_avx512f" => "avx512f",
484             "has_avx512vl" => "avx512vl",
485             "has_avx512vbmi" => "avx512vbmi",
486             "has_lzcnt" => "lzcnt",
487 
488             _ => {
489                 // FIXME: should enumerate risc-v features and plumb them
490                 // through to the `detect_host_feature` function.
491                 if cfg!(target_arch = "riscv64") && flag != "not_a_flag" {
492                     return Ok(());
493                 }
494                 return Err(format!(
495                     "don't know how to test for target-specific flag {flag:?} at runtime"
496                 ));
497             }
498         };
499 
500         let detect = match self.config().detect_host_feature {
501             Some(detect) => detect,
502             None => {
503                 return Err(format!(
504                     "cannot determine if host feature {host_feature:?} is \
505                      available at runtime, configure a probing function with \
506                      `Config::detect_host_feature`"
507                 ))
508             }
509         };
510 
511         match detect(host_feature) {
512             Some(true) => Ok(()),
513             Some(false) => Err(format!(
514                 "compilation setting {flag:?} is enabled, but not \
515                  available on the host",
516             )),
517             None => Err(format!(
518                 "failed to detect if target-specific flag {flag:?} is \
519                  available at runtime"
520             )),
521         }
522     }
523 }
524 
525 #[cfg(any(feature = "cranelift", feature = "winch"))]
526 impl Engine {
527     pub(crate) fn compiler(&self) -> &dyn wasmtime_environ::Compiler {
528         &*self.inner.compiler
529     }
530 
531     /// Ahead-of-time (AOT) compiles a WebAssembly module.
532     ///
533     /// The `bytes` provided must be in one of two formats:
534     ///
535     /// * A [binary-encoded][binary] WebAssembly module. This is always supported.
536     /// * A [text-encoded][text] instance of the WebAssembly text format.
537     ///   This is only supported when the `wat` feature of this crate is enabled.
538     ///   If this is supplied then the text format will be parsed before validation.
539     ///   Note that the `wat` feature is enabled by default.
540     ///
541     /// This method may be used to compile a module for use with a different target
542     /// host. The output of this method may be used with
543     /// [`Module::deserialize`](crate::Module::deserialize) on hosts compatible
544     /// with the [`Config`](crate::Config) associated with this [`Engine`].
545     ///
546     /// The output of this method is safe to send to another host machine for later
547     /// execution. As the output is already a compiled module, translation and code
548     /// generation will be skipped and this will improve the performance of constructing
549     /// a [`Module`](crate::Module) from the output of this method.
550     ///
551     /// [binary]: https://webassembly.github.io/spec/core/binary/index.html
552     /// [text]: https://webassembly.github.io/spec/core/text/index.html
553     pub fn precompile_module(&self, bytes: &[u8]) -> Result<Vec<u8>> {
554         crate::CodeBuilder::new(self)
555             .wasm_binary_or_text(bytes, None)?
556             .compile_module_serialized()
557     }
558 
559     /// Same as [`Engine::precompile_module`] except for a
560     /// [`Component`](crate::component::Component)
561     #[cfg(feature = "component-model")]
562     pub fn precompile_component(&self, bytes: &[u8]) -> Result<Vec<u8>> {
563         crate::CodeBuilder::new(self)
564             .wasm_binary_or_text(bytes, None)?
565             .compile_component_serialized()
566     }
567 
568     /// Produces a blob of bytes by serializing the `engine`'s configuration data to
569     /// be checked, perhaps in a different process, with the `check_compatible`
570     /// method below.
571     ///
572     /// The blob of bytes is inserted into the object file specified to become part
573     /// of the final compiled artifact.
574     pub(crate) fn append_compiler_info(&self, obj: &mut Object<'_>) {
575         serialization::append_compiler_info(self, obj, &serialization::Metadata::new(&self))
576     }
577 
578     #[cfg(any(feature = "cranelift", feature = "winch"))]
579     pub(crate) fn append_bti(&self, obj: &mut Object<'_>) {
580         let section = obj.add_section(
581             obj.segment_name(StandardSegment::Data).to_vec(),
582             obj::ELF_WASM_BTI.as_bytes().to_vec(),
583             SectionKind::ReadOnlyData,
584         );
585         let contents = if self.compiler().is_branch_protection_enabled() {
586             1
587         } else {
588             0
589         };
590         obj.append_section_data(section, &[contents], 1);
591     }
592 }
593 
594 /// Return value from the [`Engine::detect_precompiled`] API.
595 #[derive(PartialEq, Eq, Copy, Clone, Debug)]
596 pub enum Precompiled {
597     /// The input bytes look like a precompiled core wasm module.
598     Module,
599     /// The input bytes look like a precompiled wasm component.
600     Component,
601 }
602 
603 #[cfg(feature = "runtime")]
604 impl Engine {
605     /// Eagerly initialize thread-local functionality shared by all [`Engine`]s.
606     ///
607     /// Wasmtime's implementation on some platforms may involve per-thread
608     /// setup that needs to happen whenever WebAssembly is invoked. This setup
609     /// can take on the order of a few hundred microseconds, whereas the
610     /// overhead of calling WebAssembly is otherwise on the order of a few
611     /// nanoseconds. This setup cost is paid once per-OS-thread. If your
612     /// application is sensitive to the latencies of WebAssembly function
613     /// calls, even those that happen first on a thread, then this function
614     /// can be used to improve the consistency of each call into WebAssembly
615     /// by explicitly frontloading the cost of the one-time setup per-thread.
616     ///
617     /// Note that this function is not required to be called in any embedding.
618     /// Wasmtime will automatically initialize thread-local-state as necessary
619     /// on calls into WebAssembly. This is provided for use cases where the
620     /// latency of WebAssembly calls are extra-important, which is not
621     /// necessarily true of all embeddings.
622     pub fn tls_eager_initialize() {
623         crate::runtime::vm::tls_eager_initialize();
624     }
625 
626     pub(crate) fn allocator(&self) -> &dyn crate::runtime::vm::InstanceAllocator {
627         self.inner.allocator.as_ref()
628     }
629 
630     pub(crate) fn gc_runtime(&self) -> Result<&Arc<dyn GcRuntime>> {
631         if let Some(rt) = &self.inner.gc_runtime {
632             Ok(rt)
633         } else {
634             bail!("no GC runtime: GC disabled at compile time or configuration time")
635         }
636     }
637 
638     pub(crate) fn profiler(&self) -> &dyn crate::profiling_agent::ProfilingAgent {
639         self.inner.profiler.as_ref()
640     }
641 
642     #[cfg(feature = "cache")]
643     pub(crate) fn cache_config(&self) -> &wasmtime_cache::CacheConfig {
644         &self.config().cache_config
645     }
646 
647     pub(crate) fn signatures(&self) -> &TypeRegistry {
648         &self.inner.signatures
649     }
650 
651     pub(crate) fn epoch_counter(&self) -> &AtomicU64 {
652         &self.inner.epoch
653     }
654 
655     pub(crate) fn current_epoch(&self) -> u64 {
656         self.epoch_counter().load(Ordering::Relaxed)
657     }
658 
659     /// Increments the epoch.
660     ///
661     /// When using epoch-based interruption, currently-executing Wasm
662     /// code within this engine will trap or yield "soon" when the
663     /// epoch deadline is reached or exceeded. (The configuration, and
664     /// the deadline, are set on the `Store`.) The intent of the
665     /// design is for this method to be called by the embedder at some
666     /// regular cadence, for example by a thread that wakes up at some
667     /// interval, or by a signal handler.
668     ///
669     /// See [`Config::epoch_interruption`](crate::Config::epoch_interruption)
670     /// for an introduction to epoch-based interruption and pointers
671     /// to the other relevant methods.
672     ///
673     /// When performing `increment_epoch` in a separate thread, consider using
674     /// [`Engine::weak`] to hold an [`EngineWeak`](crate::EngineWeak) and
675     /// performing [`EngineWeak::upgrade`](crate::EngineWeak::upgrade) on each
676     /// tick, so that the epoch ticking thread does not keep an [`Engine`] alive
677     /// longer than any of its consumers.
678     ///
679     /// ## Signal Safety
680     ///
681     /// This method is signal-safe: it does not make any syscalls, and
682     /// performs only an atomic increment to the epoch value in
683     /// memory.
684     pub fn increment_epoch(&self) {
685         self.inner.epoch.fetch_add(1, Ordering::Relaxed);
686     }
687 
688     /// Returns a [`std::hash::Hash`] that can be used to check precompiled WebAssembly compatibility.
689     ///
690     /// The outputs of [`Engine::precompile_module`] and [`Engine::precompile_component`]
691     /// are compatible with a different [`Engine`] instance only if the two engines use
692     /// compatible [`Config`]s. If this Hash matches between two [`Engine`]s then binaries
693     /// from one are guaranteed to deserialize in the other.
694     #[cfg(any(feature = "cranelift", feature = "winch"))]
695     pub fn precompile_compatibility_hash(&self) -> impl std::hash::Hash + '_ {
696         crate::compile::HashedEngineCompileEnv(self)
697     }
698 
699     /// Executes `f1` and `f2` in parallel if parallel compilation is enabled at
700     /// both runtime and compile time, otherwise runs them synchronously.
701     #[allow(dead_code)] // only used for the component-model feature right now
702     pub(crate) fn join_maybe_parallel<T, U>(
703         &self,
704         f1: impl FnOnce() -> T + Send,
705         f2: impl FnOnce() -> U + Send,
706     ) -> (T, U)
707     where
708         T: Send,
709         U: Send,
710     {
711         if self.config().parallel_compilation {
712             #[cfg(feature = "parallel-compilation")]
713             return rayon::join(f1, f2);
714         }
715         (f1(), f2())
716     }
717 
718     /// Loads a `CodeMemory` from the specified in-memory slice, copying it to a
719     /// uniquely owned mmap.
720     ///
721     /// The `expected` marker here is whether the bytes are expected to be a
722     /// precompiled module or a component.
723     pub(crate) fn load_code_bytes(
724         &self,
725         bytes: &[u8],
726         expected: ObjectKind,
727     ) -> Result<Arc<crate::CodeMemory>> {
728         self.load_code(crate::runtime::vm::MmapVec::from_slice(bytes)?, expected)
729     }
730 
731     /// Like `load_code_bytes`, but creates a mmap from a file on disk.
732     #[cfg(feature = "std")]
733     pub(crate) fn load_code_file(
734         &self,
735         file: File,
736         expected: ObjectKind,
737     ) -> Result<Arc<crate::CodeMemory>> {
738         self.load_code(
739             crate::runtime::vm::MmapVec::from_file(file)
740                 .with_context(|| "Failed to create file mapping".to_string())?,
741             expected,
742         )
743     }
744 
745     pub(crate) fn load_code(
746         &self,
747         mmap: crate::runtime::vm::MmapVec,
748         expected: ObjectKind,
749     ) -> Result<Arc<crate::CodeMemory>> {
750         serialization::check_compatible(self, &mmap, expected)?;
751         let mut code = crate::CodeMemory::new(mmap)?;
752         code.publish()?;
753         Ok(Arc::new(code))
754     }
755 
756     /// Unload process-related trap/signal handlers and destroy this engine.
757     ///
758     /// This method is not safe and is not widely applicable. It is not required
759     /// to be called and is intended for use cases such as unloading a dynamic
760     /// library from a process. It is difficult to invoke this method correctly
761     /// and it requires careful coordination to do so.
762     ///
763     /// # Panics
764     ///
765     /// This method will panic if this `Engine` handle is not the last remaining
766     /// engine handle.
767     ///
768     /// # Aborts
769     ///
770     /// This method will abort the process on some platforms in some situations
771     /// where unloading the handler cannot be performed and an unrecoverable
772     /// state is reached. For example on Unix platforms with signal handling
773     /// the process will be aborted if the current signal handlers are not
774     /// Wasmtime's.
775     ///
776     /// # Unsafety
777     ///
778     /// This method is not generally safe to call and has a number of
779     /// preconditions that must be met to even possibly be safe. Even with these
780     /// known preconditions met there may be other unknown invariants to uphold
781     /// as well.
782     ///
783     /// * There must be no other instances of `Engine` elsewhere in the process.
784     ///   Note that this isn't just copies of this `Engine` but it's any other
785     ///   `Engine` at all. This unloads global state that is used by all
786     ///   `Engine`s so this instance must be the last.
787     ///
788     /// * On Unix platforms no other signal handlers could have been installed
789     ///   for signals that Wasmtime catches. In this situation Wasmtime won't
790     ///   know how to restore signal handlers that Wasmtime possibly overwrote
791     ///   when Wasmtime was initially loaded. If possible initialize other
792     ///   libraries first and then initialize Wasmtime last (e.g. defer creating
793     ///   an `Engine`).
794     ///
795     /// * All existing threads which have used this DLL or copy of Wasmtime may
796     ///   no longer use this copy of Wasmtime. Per-thread state is not iterated
797     ///   and destroyed. Only future threads may use future instances of this
798     ///   Wasmtime itself.
799     ///
800     /// If other crashes are seen from using this method please feel free to
801     /// file an issue to update the documentation here with more preconditions
802     /// that must be met.
803     #[cfg(feature = "signals-based-traps")]
804     pub unsafe fn unload_process_handlers(self) {
805         assert_eq!(Arc::weak_count(&self.inner), 0);
806         assert_eq!(Arc::strong_count(&self.inner), 1);
807 
808         #[cfg(not(miri))]
809         crate::runtime::vm::deinit_traps();
810     }
811 }
812 
813 /// A weak reference to an [`Engine`].
814 #[derive(Clone)]
815 pub struct EngineWeak {
816     inner: alloc::sync::Weak<EngineInner>,
817 }
818 
819 impl EngineWeak {
820     /// Upgrade this weak reference into an [`Engine`]. Returns `None` if
821     /// strong references (the [`Engine`] type itself) no longer exist.
822     pub fn upgrade(&self) -> Option<Engine> {
823         alloc::sync::Weak::upgrade(&self.inner).map(|inner| Engine { inner })
824     }
825 }
826