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):

  • :async_stack_zeroing (Boolean)

    Configures whether or not stacks used for async futures are zeroed before (re)use.

  • :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:



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

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)


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

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.



210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
# File 'ext/src/ruby_api/engine.rs', line 210

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 = ruby.str_new(&hex_encoded);
    key.freeze();

    rb_self.ivar_set(ivar_id, key)?;

    Ok(key)
}

#precompile_component(wat_or_wasm) ⇒ String

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

The compiled component can be instantiated using Component::Component.deserialize.

Parameters:

  • wat_or_wasm (String)

    The String of WAT or Wasm component.

Returns:

  • (String)

    Binary String of the compiled component.

See Also:



193
194
195
196
197
198
199
200
201
202
203
# File 'ext/src/ruby_api/engine.rs', line 193

pub fn precompile_component(
    ruby: &Ruby,
    rb_self: Obj<Self>,
    wat_or_wasm: RString,
) -> Result<RString, Error> {
    let (wat_or_wasm, _guard) = wat_or_wasm.as_locked_slice()?;

    nogvl(|| rb_self.inner.precompile_component(wat_or_wasm))
        .map(|bytes| ruby.str_from_slice(&bytes))
        .map_err(|e| error!("{}", e.to_string()))
}

#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:



172
173
174
175
176
177
178
179
180
181
182
# File 'ext/src/ruby_api/engine.rs', line 172

pub fn precompile_module(
    ruby: &Ruby,
    rb_self: Obj<Self>,
    wat_or_wasm: RString,
) -> Result<RString, Error> {
    let (wat_or_wasm, _guard) = wat_or_wasm.as_locked_slice()?;

    nogvl(|| rb_self.inner.precompile_module(wat_or_wasm))
        .map(|bytes| ruby.str_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)


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

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)


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

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

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