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