Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Config type to initialize Auto Splitters #712

Merged
merged 1 commit into from
Aug 20, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion crates/livesplit-auto-splitting/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -238,7 +238,7 @@ mod settings;
mod timer;

pub use process::Process;
pub use runtime::{CreationError, InterruptHandle, Runtime};
pub use runtime::{Config, CreationError, InterruptHandle, Runtime};
pub use settings::{SettingValue, SettingsStore, UserSetting, UserSettingKind};
pub use time;
pub use timer::{Timer, TimerState};
2 changes: 1 addition & 1 deletion crates/livesplit-auto-splitting/src/process.rs
Original file line number Diff line number Diff line change
Expand Up @@ -181,7 +181,7 @@ impl Process {
}
}

fn build_path(original_path: &Path) -> Option<Box<str>> {
pub fn build_path(original_path: &Path) -> Option<Box<str>> {
let mut path = String::from("/mnt");
for component in original_path.components() {
if !path.ends_with('/') {
Expand Down
74 changes: 60 additions & 14 deletions crates/livesplit-auto-splitting/src/runtime.rs
Original file line number Diff line number Diff line change
@@ -1,23 +1,26 @@
#![allow(clippy::unnecessary_cast)]

use crate::{
process::Process, settings::UserSetting, timer::Timer, SettingValue, SettingsStore,
UserSettingKind,
process::{build_path, Process},
settings::UserSetting,
timer::Timer,
SettingValue, SettingsStore, UserSettingKind,
};

use anyhow::{ensure, format_err, Context as _, Result};
use slotmap::{Key, KeyData, SlotMap};
use snafu::Snafu;
use std::{
env::consts::{ARCH, OS},
path::PathBuf,
path::{Path, PathBuf},
str,
time::{Duration, Instant},
};
use sysinfo::{ProcessExt, ProcessRefreshKind, RefreshKind, System, SystemExt};
use wasi_common::{dir::DirCaps, file::FileCaps};
use wasmtime::{
Caller, Config, Engine, Extern, Linker, Memory, Module, OptLevel, Store, TypedFunc,
Caller, Engine, Extern, Linker, Memory, Module, OptLevel, Store, TypedFunc,
WasmBacktraceDetails,
};
use wasmtime_wasi::{ambient_authority, WasiCtx, WasiCtxBuilder};

Expand Down Expand Up @@ -153,6 +156,41 @@ impl ProcessList {
}
}

/// The configuration to use when creating a new [`Runtime`].
#[non_exhaustive]
pub struct Config<'a> {
/// The settings store that is used to store the settings of the auto
/// splitter. This contains all the settings that are currently modified by
/// the user. It may not contain all the settings that are registered as
/// user settings, because the user may not have modified them yet.
pub settings_store: Option<SettingsStore>,
/// The auto splitter itself may be a runtime that wants to load a script
/// from a file to interpret. This is the path to that script. It is
/// provided to the auto splitter as the `SCRIPT_PATH` environment variable.
/// **This is currently experimental and may change in the future.**
pub interpreter_script_path: Option<&'a Path>,
/// This enables debug information for the WebAssembly module. This is
/// useful for debugging purposes, but due to bugs in wasmtime might
/// currently crash the runtime. This is disabled by default. Relevant
/// issue: https://github.com/bytecodealliance/wasmtime/issues/3999
pub debug_info: bool,
/// This enables backtrace details for the WebAssembly module. If a trap
/// occurs more details are printed in the backtrace. This is enabled by
/// default.
pub backtrace_details: bool,
}

impl Default for Config<'_> {
fn default() -> Self {
Self {
settings_store: None,
interpreter_script_path: None,
debug_info: false,
backtrace_details: true,
}
}
}

/// An auto splitter runtime that allows using an auto splitter provided as a
/// WebAssembly module to control a timer.
pub struct Runtime<T: Timer> {
Expand All @@ -165,14 +203,16 @@ pub struct Runtime<T: Timer> {
impl<T: Timer> Runtime<T> {
/// Creates a new runtime with the given path to the WebAssembly module and
/// the timer that the module then controls.
pub fn new(
module: &[u8],
timer: T,
settings_store: SettingsStore,
) -> Result<Self, CreationError> {
pub fn new(module: &[u8], timer: T, config: Config<'_>) -> Result<Self, CreationError> {
let engine = Engine::new(
Config::new()
wasmtime::Config::new()
.cranelift_opt_level(OptLevel::Speed)
.debug_info(config.debug_info)
.wasm_backtrace_details(if config.backtrace_details {
WasmBacktraceDetails::Enable
} else {
WasmBacktraceDetails::Disable
})
.epoch_interruption(true),
)
.map_err(|source| CreationError::EngineCreation { source })?;
Expand All @@ -185,12 +225,12 @@ impl<T: Timer> Runtime<T> {
Context {
processes: SlotMap::with_key(),
user_settings: Vec::new(),
settings_store,
settings_store: config.settings_store.unwrap_or_default(),
tick_rate: Duration::new(0, 1_000_000_000 / 120),
timer,
memory: None,
process_list: ProcessList::new(),
wasi: build_wasi(),
wasi: build_wasi(config.interpreter_script_path),
},
);

Expand Down Expand Up @@ -299,8 +339,14 @@ impl<T: Timer> Runtime<T> {
}
}

fn build_wasi() -> WasiCtx {
let wasi = WasiCtxBuilder::new().build();
fn build_wasi(script_path: Option<&Path>) -> WasiCtx {
let mut wasi = WasiCtxBuilder::new().build();

if let Some(script_path) = script_path {
if let Some(path) = build_path(script_path) {
let _ = wasi.push_env("SCRIPT_PATH", &path);
}
}

#[cfg(windows)]
{
Expand Down
4 changes: 2 additions & 2 deletions crates/livesplit-auto-splitting/tests/sandboxing.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use livesplit_auto_splitting::{Runtime, SettingsStore, Timer, TimerState};
use livesplit_auto_splitting::{Config, Runtime, SettingsStore, Timer, TimerState};
use std::{
ffi::OsStr,
fmt, fs,
Expand Down Expand Up @@ -64,7 +64,7 @@ fn compile(crate_name: &str) -> anyhow::Result<Runtime<DummyTimer>> {
Ok(Runtime::new(
&std::fs::read(wasm_path).unwrap(),
DummyTimer,
SettingsStore::new(),
Config::default(),
)?)
}

Expand Down
12 changes: 4 additions & 8 deletions src/auto_splitting/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -194,8 +194,8 @@

use crate::timing::{SharedTimer, TimerPhase};
use livesplit_auto_splitting::{
CreationError, InterruptHandle, Runtime as ScriptRuntime, SettingsStore,
Timer as AutoSplitTimer, TimerState,
Config, CreationError, InterruptHandle, Runtime as ScriptRuntime, Timer as AutoSplitTimer,
TimerState,
};
use snafu::Snafu;
use std::{fmt, fs, io, path::PathBuf, thread, time::Duration};
Expand Down Expand Up @@ -399,7 +399,7 @@ async fn run(
let mut runtime = loop {
match receiver.recv().await {
Some(Request::LoadScript(script, ret)) => {
match ScriptRuntime::new(&script, Timer(timer.clone()), SettingsStore::new()) {
match ScriptRuntime::new(&script, Timer(timer.clone()), Config::default()) {
Ok(r) => {
ret.send(Ok(())).ok();
break r;
Expand Down Expand Up @@ -428,11 +428,7 @@ async fn run(
match timeout_at(next_step, receiver.recv()).await {
Ok(Some(request)) => match request {
Request::LoadScript(script, ret) => {
match ScriptRuntime::new(
&script,
Timer(timer.clone()),
SettingsStore::new(),
) {
match ScriptRuntime::new(&script, Timer(timer.clone()), Config::default()) {
Ok(r) => {
ret.send(Ok(())).ok();
runtime = r;
Expand Down
3 changes: 3 additions & 0 deletions src/platform/normal/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,9 @@
// defined as a clock measuring real time`, making it sound like a compliant
// implementation should measure the time the OS is suspended as well.
//
// Open issue:
// https://github.com/WebAssembly/wasi-clocks/issues/47
//
// # Web
//
// In the web we use `performance.now()` which they want to specify as being
Expand Down Expand Up @@ -130,12 +133,12 @@
// atomic isn't worth the slight performance penalty and
// once Rust bumps the minimal Linux version we may also
// just cfg out `CLOCK_MONOTONIC` here.
if unsafe { libc::clock_gettime(libc::CLOCK_BOOTTIME, &mut t) } == 0 {

Check warning on line 136 in src/platform/normal/mod.rs

View workflow job for this annotation

GitHub Actions / Check clippy lints

unsafe block missing a safety comment
return Self(Duration::new(t.tv_sec as _, t.tv_nsec as _));
}
}

if unsafe { libc::clock_gettime(libc::CLOCK_MONOTONIC, &mut t) } != 0 {

Check warning on line 141 in src/platform/normal/mod.rs

View workflow job for this annotation

GitHub Actions / Check clippy lints

unsafe block missing a safety comment
panic!("clock_gettime doesn't work.");
}

Expand Down
Loading