Class: Wasmtime::Engine

Inherits:
Object
  • Object
show all
Defined in:
ext/src/ruby_api/engine.rs

Overview

Represents a Wasmtime execution engine.

Examples:

Disabling parallel compilation

# Many Ruby servers use a pre-forking mechanism to allow parallel request
# processing. Unfortunately, this can causes processes to deadlock if you
# use parallel compilation to compile Wasm prior to calling
# `Process::fork`. To avoid this issue, any compilations that need to be
# done before forking need to disable the `parallel_compilation` option.

prefork_engine = Wasmtime::Engine.new(parallel_compilation: false)
wasm_module = Wasmtime::Module.new(prefork_engine, "(module)")

fork do
  # We can enable parallel compilation now that we've forked.
  engine = Wasmtime::Engine.new(parallel_compilation: true)
  store = Wasmtime::Store.new(engine)
  instance = Wasmtime::Instance.new(store, wasm_module)
  # ...
end

See Also:

Class Method Summary collapse

Instance Method Summary collapse

Class Method Details

.new(config = {}) ⇒ Object

Parameters:

  • config (Hash) (defaults to: {})

    The engine’s config. See the Config‘s Rust doc for detailed description of the different options and the defaults.

Options Hash (config):

  • :debug_info (Boolean)
  • :wasm_backtrace_details (Boolean)
  • :native_unwind_info (Boolean)
  • :consume_fuel (Boolean)
  • :epoch_interruption (Boolean)
  • :max_wasm_stack (Integer)
  • :wasm_threads (Boolean)
  • :wasm_multi_memory (Boolean)
  • :wasm_memory64 (Boolean)
  • :wasm_reference_types (Boolean)
  • :parallel_compilation (Boolean) — default: true

    Whether compile WASM using multiple threads

  • :generate_address_map (Boolean)

    Configures whether compiled artifacts will contain information to map native program addresses back to the original wasm module. This configuration option is ‘true` by default. Disabling this feature can result in considerably smaller serialized modules.

  • :cranelift_opt_level (Symbol)

    One of none, speed, speed_and_size.

  • :profiler (Symbol)

    One of none, jitdump, vtune.

  • :strategy (Symbol)

    One of auto, cranelift, winch

  • :target (String)

See Also:



89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
# File 'ext/src/ruby_api/engine.rs', line 89

pub fn new(args: &[Value]) -> Result<Self, Error> {
    let args = scan_args::scan_args::<(), (Option<Value>,), (), (), (), ()>(args)?;
    let (config,) = args.optional;
    let config = config.and_then(|v| if v.is_nil() { None } else { Some(v) });
    let inner = match config {
        Some(config) => {
            let config = RHash::try_convert(config).and_then(hash_to_config)?;

            EngineImpl::new(&config).map_err(|e| error!("{}", e))?
        }
        None => EngineImpl::new(&Config::default()).map_err(|e| error!("{}", e))?,
    };

    Ok(Self {
        inner,
        #[cfg(feature = "tokio")]
        timer_task: Default::default(),
    })
}

Instance Method Details

#increment_epochnil

Manually increment the engine’s epoch. Note: this cannot be used from a different thread while WebAssembly is running because the Global VM lock (GVL) is not released. Using #start_epoch_interval is recommended because it sidesteps the GVL.

Returns:

  • (nil)


154
155
156
# File 'ext/src/ruby_api/engine.rs', line 154

pub fn increment_epoch(&self) {
    self.inner.increment_epoch();
}

#precompile_compatibility_keyString

If two engines have a matching #precompile_compatibility_key, then serialized modules from one engine can be deserialized by the other.

Returns:

  • (String)

    The hex formatted string that can be used to check precompiled module compatibility.



184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
# File 'ext/src/ruby_api/engine.rs', line 184

pub fn precompile_compatibility_key(ruby: &Ruby, rb_self: Obj<Self>) -> Result<RString, Error> {
    static ID: LazyId = LazyId::new("precompile_compatibility_key");
    let ivar_id = LazyId::get_inner_with(&ID, ruby);

    if let Ok(cached) = rb_self.ivar_get::<_, RString>(ivar_id) {
        return Ok(cached);
    }

    let mut hasher = DefaultHasher::new();
    let engine = rb_self.inner.clone();
    engine.precompile_compatibility_hash().hash(&mut hasher);
    let hex_encoded = format!("{:x}", hasher.finish());
    let key = RString::new(&hex_encoded);
    key.freeze();

    rb_self.ivar_set(ivar_id, key)?;

    Ok(key)
}

#precompile_module(wat_or_wasm) ⇒ String

AoT compile a WebAssembly text or WebAssembly binary module for later use.

The compiled module can be instantiated using Module.deserialize.

Parameters:

  • wat_or_wasm (String)

    The String of WAT or Wasm.

Returns:

  • (String)

    Binary String of the compiled module.

See Also:



171
172
173
174
175
176
177
# File 'ext/src/ruby_api/engine.rs', line 171

pub fn precompile_module(&self, wat_or_wasm: RString) -> Result<RString, Error> {
    let (wat_or_wasm, _guard) = wat_or_wasm.as_locked_slice()?;

    nogvl(|| self.inner.precompile_module(wat_or_wasm))
        .map(|bytes| RString::from_slice(&bytes))
        .map_err(|e| error!("{}", e.to_string()))
}

#start_epoch_interval(milliseconds) ⇒ nil

Starts a timer that will increment the engine’s epoch every milliseconds. Waits milliseconds before incrementing for the first time.

If a prior timer was started, it will be stopped.

Parameters:

  • milliseconds (Integer)

Returns:

  • (nil)


118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
# File 'ext/src/ruby_api/engine.rs', line 118

pub fn start_epoch_interval(&self, milliseconds: u64) {
    self.stop_epoch_interval();
    let engine = self.inner.clone();

    let handle = TOKIO_RT.spawn(async move {
        let tick_every = tokio::time::Duration::from_millis(milliseconds);
        let mut interval = async_timer::Interval::platform_new(tick_every);

        loop {
            interval.wait().await;
            engine.increment_epoch();
        }
    });

    *self.timer_task.lock().unwrap() = Some(handle);
}

#stop_epoch_intervalnil

Stops a previously started timer with #start_epoch_interval. Does nothing if there is no running timer.

Returns:

  • (nil)


140
141
142
143
144
145
146
# File 'ext/src/ruby_api/engine.rs', line 140

pub fn stop_epoch_interval(&self) {
    let maybe_handle = self.timer_task.lock().unwrap().take();

    if let Some(handle) = maybe_handle {
        handle.abort();
    }
}