From 63db093d0c11b03aa1cca2fbcac527c79a0f9ec6 Mon Sep 17 00:00:00 2001 From: Wodann Date: Sun, 24 Jan 2021 19:25:21 -0600 Subject: [PATCH] feat(runtime): add runtime re-linking of dependencies --- crates/mun/src/lib.rs | 39 +++--- crates/mun/src/ops/start.rs | 11 +- crates/mun_compiler/src/driver.rs | 8 +- crates/mun_project/src/lib.rs | 1 + crates/mun_runtime/Cargo.toml | 1 + crates/mun_runtime/src/assembly.rs | 210 +++++++++++++++++++---------- crates/mun_runtime/src/lib.rs | 193 ++++++++++++++++++-------- crates/mun_runtime_capi/ffi | 2 +- crates/mun_runtime_capi/src/lib.rs | 21 +-- 9 files changed, 306 insertions(+), 180 deletions(-) diff --git a/crates/mun/src/lib.rs b/crates/mun/src/lib.rs index 7417976a2..35af25fae 100644 --- a/crates/mun/src/lib.rs +++ b/crates/mun/src/lib.rs @@ -40,14 +40,12 @@ where Arg::with_name("manifest-path") .long("manifest-path") .takes_value(true) - .help(&format!("Path to {}", MANIFEST_FILENAME)) - ) - .arg( - Arg::with_name("watch") - .long("watch") - .help("Run the compiler in watch mode.\ - Watch input files and trigger recompilation on changes.",) + .help(&format!("Path to {}", MANIFEST_FILENAME)), ) + .arg(Arg::with_name("watch").long("watch").help( + "Run the compiler in watch mode.\ + Watch input files and trigger recompilation on changes.", + )) .arg( Arg::with_name("opt-level") .short("O") @@ -71,7 +69,7 @@ where .arg( Arg::with_name("emit-ir") .long("emit-ir") - .help("emits IR instead of a *.munlib") + .help("emits IR instead of a *.munlib"), ) .about("Compiles a local Mun file into a module"), ) @@ -88,25 +86,24 @@ where .long("entry") .takes_value(true) .help("the function entry point to call on startup"), - ) - .arg( - Arg::with_name("delay") - .long("delay") - .takes_value(true) - .help("how much to delay received filesystem events (in ms). This allows bundling of identical events, e.g. when several writes to the same file are detected. A high delay will make hot reloading less responsive. (defaults to 10 ms)"), ), ) .subcommand( - SubCommand::with_name("new") - .arg(Arg::with_name("path").help("the path to create a new project").required(true).index(1)) - ) - .subcommand( - SubCommand::with_name("init") - .arg(Arg::with_name("path").help("the path to create a new project [default: .]").index(1)) + SubCommand::with_name("new").arg( + Arg::with_name("path") + .help("the path to create a new project") + .required(true) + .index(1), + ), ) .subcommand( - SubCommand::with_name("language-server") + SubCommand::with_name("init").arg( + Arg::with_name("path") + .help("the path to create a new project [default: .]") + .index(1), + ), ) + .subcommand(SubCommand::with_name("language-server")) .get_matches_from_safe(args); match matches { diff --git a/crates/mun/src/ops/start.rs b/crates/mun/src/ops/start.rs index 60f35f061..f2ef28d73 100644 --- a/crates/mun/src/ops/start.rs +++ b/crates/mun/src/ops/start.rs @@ -1,6 +1,4 @@ -use std::cell::RefCell; -use std::rc::Rc; -use std::time::Duration; +use std::{cell::RefCell, rc::Rc}; use anyhow::anyhow; use clap::ArgMatches; @@ -57,12 +55,5 @@ fn runtime(matches: &ArgMatches) -> Result>, anyhow::Error> matches.value_of("LIBRARY").unwrap(), // Safe because its a required arg ); - let builder = if let Some(delay) = matches.value_of("delay") { - let delay: u64 = delay.parse()?; - builder.set_delay(Duration::from_millis(delay)) - } else { - builder - }; - builder.spawn() } diff --git a/crates/mun_compiler/src/driver.rs b/crates/mun_compiler/src/driver.rs index e6c4b2aca..e7b70ef00 100644 --- a/crates/mun_compiler/src/driver.rs +++ b/crates/mun_compiler/src/driver.rs @@ -21,15 +21,11 @@ pub use self::config::Config; pub use self::display_color::DisplayColor; use crate::diagnostics_snippets::{emit_hir_diagnostic, emit_syntax_error}; -use mun_project::Package; -use std::collections::HashMap; -use std::convert::TryInto; -use std::path::Path; -use std::time::Duration; +use mun_project::{Package, LOCKFILE_NAME}; +use std::{collections::HashMap, convert::TryInto, path::Path, time::Duration}; use walkdir::WalkDir; pub const WORKSPACE: SourceRootId = SourceRootId(0); -pub const LOCKFILE_NAME: &str = ".munlock"; pub struct Driver { db: CompilerDatabase, diff --git a/crates/mun_project/src/lib.rs b/crates/mun_project/src/lib.rs index 8b92dc667..640fe3014 100644 --- a/crates/mun_project/src/lib.rs +++ b/crates/mun_project/src/lib.rs @@ -7,3 +7,4 @@ mod package; mod project_manifest; pub const MANIFEST_FILENAME: &str = "mun.toml"; +pub const LOCKFILE_NAME: &str = ".munlock"; diff --git a/crates/mun_runtime/Cargo.toml b/crates/mun_runtime/Cargo.toml index 1936fe432..c4f8dd133 100644 --- a/crates/mun_runtime/Cargo.toml +++ b/crates/mun_runtime/Cargo.toml @@ -19,6 +19,7 @@ libloader = { version = "=0.1.0", path = "../mun_libloader", package = "mun_libl log = "0.4" md5 = "0.7.0" memory = { version = "=0.1.0", path = "../mun_memory", package = "mun_memory" } +mun_project = { version = "=0.1.0", path = "../mun_project" } notify = "4.0.12" once_cell = "1.4.0" parking_lot = "0.10" diff --git a/crates/mun_runtime/src/assembly.rs b/crates/mun_runtime/src/assembly.rs index ef15e85f0..dfc54acb1 100644 --- a/crates/mun_runtime/src/assembly.rs +++ b/crates/mun_runtime/src/assembly.rs @@ -2,12 +2,14 @@ use crate::{ garbage_collector::{GarbageCollector, UnsafeTypeInfo}, DispatchTable, }; -use abi::AssemblyInfo; +use abi::{AssemblyInfo, FunctionPrototype}; use anyhow::anyhow; use libloader::{MunLibrary, TempLibrary}; use log::error; use memory::mapping::{Mapping, MemoryMapper}; use std::{ + collections::HashMap, + ffi::c_void, path::{Path, PathBuf}, ptr::NonNull, sync::Arc, @@ -52,31 +54,12 @@ impl Assembly { Ok(assembly) } - /// Tries to link the `assemblies`, resulting in a new [`DispatchTable`] on success. This leaves - /// the original `dispatch_table` intact, in case of linking errors. - pub(super) fn link_all<'a>( - assemblies: impl Iterator, - dispatch_table: &DispatchTable, - ) -> Result { - let assemblies: Vec<&'a mut _> = assemblies.collect(); - - // Clone the dispatch table, such that we can roll back if linking fails - let mut dispatch_table = dispatch_table.clone(); - - // Insert all assemblies' functions into the dispatch table - for assembly in assemblies.iter() { - for function in assembly.info().symbols.functions() { - dispatch_table.insert_fn(function.prototype.name(), function.clone()); - } - } - - let mut to_link: Vec<_> = assemblies - .into_iter() - .flat_map(|asm| asm.info.dispatch_table.iter_mut()) - // Only take signatures into account that do *not* yet have a function pointer assigned - // by the compiler. - .filter(|(ptr, _)| ptr.is_null()) - .collect(); + /// Private implementation of runtime linking + fn link_all_impl<'a>( + dispatch_table: &mut DispatchTable, + to_link: impl Iterator, + ) -> anyhow::Result<()> { + let mut to_link: Vec<_> = to_link.collect(); let mut retry = true; while retry { @@ -114,65 +97,154 @@ impl Assembly { return Err(anyhow!("Failed to link due to missing dependencies.")); } + Ok(()) + } + + /// Tries to link the `assemblies`, resulting in a new [`DispatchTable`] on success. This leaves + /// the original `dispatch_table` intact, in case of linking errors. + pub(super) fn link_all<'a>( + assemblies: impl Iterator, + dispatch_table: &DispatchTable, + ) -> anyhow::Result { + let assemblies: Vec<&'a mut _> = assemblies.collect(); + + // Clone the dispatch table, such that we can roll back if linking fails + let mut dispatch_table = dispatch_table.clone(); + + // Insert all assemblies' functions into the dispatch table + for assembly in assemblies.iter() { + for function in assembly.info().symbols.functions() { + dispatch_table.insert_fn(function.prototype.name(), function.clone()); + } + } + + let to_link: Vec<_> = assemblies + .into_iter() + .flat_map(|asm| asm.info.dispatch_table.iter_mut()) + // Only take signatures into account that do *not* yet have a function pointer assigned + // by the compiler. + .filter(|(ptr, _)| ptr.is_null()) + .collect(); + + Assembly::link_all_impl(&mut dispatch_table, to_link.into_iter())?; + Ok(dispatch_table) } - /// Swaps the assembly's shared library and its information for the library at `library_path`. - pub fn swap( - &mut self, - library_path: &Path, - runtime_dispatch_table: &mut DispatchTable, - ) -> Result<(), anyhow::Error> { - let mut new_assembly = Assembly::load(library_path, self.allocator.clone())?; - - let old_types: Vec = self - .info - .symbols - .types() - .iter() - .map(|ty| { - // Safety: `ty` is a shared reference, so is guaranteed to not be `ptr::null()`. - UnsafeTypeInfo::new(unsafe { - NonNull::new_unchecked(*ty as *const abi::TypeInfo as *mut _) - }) + /// Tries to link the `assemblies`, resulting in a new [`DispatchTable`] on success. This leaves + /// the original `dispatch_table` intact, in case of linking errors. + pub(super) fn relink_all<'a, 'b>( + unlinked_assemblies: &mut HashMap, + linked_assemblies: &mut HashMap, + dispatch_table: &DispatchTable, + ) -> anyhow::Result { + let mut assemblies = unlinked_assemblies + .iter_mut() + .map(|(old_path, asm)| { + let old_assembly = linked_assemblies.get(old_path); + + (asm, old_assembly) }) + .collect::>(); + + // Clone the dispatch table, such that we can roll back if linking fails + let mut dispatch_table = dispatch_table.clone(); + + // Remove the old assemblies' functions from the dispatch table + for (_, old_assembly) in assemblies.iter() { + if let Some(assembly) = old_assembly { + for function in assembly.info.symbols.functions() { + dispatch_table.remove_fn(function.prototype.name()); + } + } + } + + // Insert all assemblies' functions into the dispatch table + for (new_assembly, _) in assemblies.iter() { + for function in new_assembly.info().symbols.functions() { + dispatch_table.insert_fn(function.prototype.name(), function.clone()); + } + } + + let to_link = assemblies + .iter_mut() + .flat_map(|(asm, _)| asm.info.dispatch_table.iter_mut()) + // Only take signatures into account that do *not* yet have a function pointer assigned + // by the compiler. + .filter(|(ptr, _)| ptr.is_null()); + + Assembly::link_all_impl(&mut dispatch_table, to_link)?; + + let assemblies_to_map: Vec<_> = assemblies + .into_iter() + .filter_map(|(new_asm, old_asm)| old_asm.map(|old_asm| (old_asm, new_asm))) .collect(); - let new_types: Vec = new_assembly - .info - .symbols - .types() - .iter() - .map(|ty| { - // Safety: `ty` is a shared reference, so is guaranteed to not be `ptr::null()`. - UnsafeTypeInfo::new(unsafe { - NonNull::new_unchecked(*ty as *const abi::TypeInfo as *mut _) + let mut assemblies_to_keep = HashMap::new(); + for (old_assembly, new_assembly) in assemblies_to_map.iter() { + let old_types: Vec = old_assembly + .info + .symbols + .types() + .iter() + .map(|ty| { + // Safety: `ty` is a shared reference, so is guaranteed to not be `ptr::null()`. + UnsafeTypeInfo::new(unsafe { + NonNull::new_unchecked(*ty as *const abi::TypeInfo as *mut _) + }) }) - }) - .collect(); + .collect(); - let mapping = Mapping::new(&old_types, &new_types); - let deleted_objects = self.allocator.map_memory(mapping); + let new_types: Vec = new_assembly + .info + .symbols + .types() + .iter() + .map(|ty| { + // Safety: `ty` is a shared reference, so is guaranteed to not be `ptr::null()`. + UnsafeTypeInfo::new(unsafe { + NonNull::new_unchecked(*ty as *const abi::TypeInfo as *mut _) + }) + }) + .collect(); - // Remove the old assembly's functions - for function in self.info.symbols.functions() { - runtime_dispatch_table.remove_fn(function.prototype.name()); + let mapping = Mapping::new(&old_types, &new_types); + let deleted_objects = old_assembly.allocator.map_memory(mapping); + + if !deleted_objects.is_empty() { + // Retain the previous assembly + assemblies_to_keep.insert( + old_assembly.library_path().to_path_buf(), + new_assembly.library_path().to_path_buf(), + ); + } } - new_assembly.link(runtime_dispatch_table); + let mut newly_linked = HashMap::new(); + std::mem::swap(unlinked_assemblies, &mut newly_linked); + + for (old_path, mut new_assembly) in newly_linked.into_iter() { + let mut old_assembly = linked_assemblies + .remove(&old_path) + .expect("Assembly must exist."); + + let new_path = if let Some(new_path) = assemblies_to_keep.remove(&old_path) { + // Retain all existing legacy libs + new_assembly + .legacy_libs + .append(&mut old_assembly.legacy_libs); - // Retain all existing legacy libs - new_assembly.legacy_libs.append(&mut self.legacy_libs); + new_assembly.legacy_libs.push(old_assembly.into_library()); - std::mem::swap(self, &mut new_assembly); - let old_assembly = new_assembly; + new_path + } else { + new_assembly.library_path().to_path_buf() + }; - if !deleted_objects.is_empty() { - // Retain the previous assembly - self.legacy_libs.push(old_assembly.into_library()); + linked_assemblies.insert(new_path, new_assembly); } - Ok(()) + Ok(dispatch_table) } /// Returns the assembly's information. diff --git a/crates/mun_runtime/src/lib.rs b/crates/mun_runtime/src/lib.rs index fac5a382f..8ceb81be0 100644 --- a/crates/mun_runtime/src/lib.rs +++ b/crates/mun_runtime/src/lib.rs @@ -13,9 +13,13 @@ mod adt; mod marshal; mod reflection; +use anyhow::Result; +use ffi::OsString; use garbage_collector::GarbageCollector; +use log::{debug, error, info}; use memory::gc::{self, GcRuntime}; -use notify::{DebouncedEvent, RecommendedWatcher, RecursiveMode, Watcher}; +use mun_project::LOCKFILE_NAME; +use notify::{RawEvent, RecommendedWatcher, RecursiveMode, Watcher}; use rustc_hash::FxHashMap; use std::{ cell::RefCell, @@ -29,7 +33,6 @@ use std::{ mpsc::{channel, Receiver}, Arc, }, - time::Duration, }; pub use crate::{ @@ -45,12 +48,38 @@ pub use abi::IntoFunctionDefinition; pub struct RuntimeOptions { /// Path to the entry point library pub library_path: PathBuf, - /// Delay during which filesystem events are collected, deduplicated, and after which emitted. - pub delay: Duration, /// Custom user injected functions pub user_functions: Vec<(abi::FunctionDefinition, abi::FunctionDefinitionStorage)>, } +/// Retrieve the allocator using the provided handle. +/// +/// # Safety +/// +/// The allocator must have been set using the `set_allocator_handle` call - exposed by the Mun +/// library. +unsafe fn get_allocator(alloc_handle: *mut ffi::c_void) -> Arc { + Arc::from_raw(alloc_handle as *const GarbageCollector) +} + +extern "C" fn new( + type_info: *const abi::TypeInfo, + alloc_handle: *mut ffi::c_void, +) -> *const *mut ffi::c_void { + // Safety: `new` is only called from within Mun assemblies' core logic, so we are guaranteed + // that the `Runtime` and its `GarbageCollector` still exist if this function is called, and + // will continue to do so for the duration of this function. + let allocator = unsafe { get_allocator(alloc_handle) }; + // Safety: the Mun Compiler guarantees that `new` is never called with `ptr::null()`. + let type_info = UnsafeTypeInfo::new(unsafe { NonNull::new_unchecked(type_info as *mut _) }); + let handle = allocator.alloc(type_info); + + // Prevent destruction of the allocator + mem::forget(allocator); + + handle.into() +} + /// A builder for the [`Runtime`]. pub struct RuntimeBuilder { options: RuntimeOptions, @@ -62,18 +91,11 @@ impl RuntimeBuilder { Self { options: RuntimeOptions { library_path: library_path.into(), - delay: Duration::from_millis(10), user_functions: Default::default(), }, } } - /// Sets the `delay`. - pub fn set_delay(mut self, delay: Duration) -> Self { - self.options.delay = delay; - self - } - /// Adds a custom user function to the dispatch table. pub fn insert_fn, F: abi::IntoFunctionDefinition>( mut self, @@ -170,41 +192,16 @@ impl DispatchTable { /// [log-impl]: https://docs.rs/log/0.4.13/log/#available-logging-implementations pub struct Runtime { assemblies: HashMap, + /// Assemblies that have changed and thus need to be relinked. Maps the old to the (potentially) new path. + assemblies_to_relink: VecDeque<(PathBuf, PathBuf)>, dispatch_table: DispatchTable, watcher: RecommendedWatcher, - watcher_rx: Receiver, + watcher_rx: Receiver, + renamed_files: HashMap, gc: Arc, _user_functions: Vec, } -/// Retrieve the allocator using the provided handle. -/// -/// # Safety -/// -/// The allocator must have been set using the `set_allocator_handle` call - exposed by the Mun -/// library. -unsafe fn get_allocator(alloc_handle: *mut ffi::c_void) -> Arc { - Arc::from_raw(alloc_handle as *const GarbageCollector) -} - -extern "C" fn new( - type_info: *const abi::TypeInfo, - alloc_handle: *mut ffi::c_void, -) -> *const *mut ffi::c_void { - // Safety: `new` is only called from within Mun assemblies' core logic, so we are guaranteed - // that the `Runtime` and its `GarbageCollector` still exist if this function is called, and - // will continue to do so for the duration of this function. - let allocator = unsafe { get_allocator(alloc_handle) }; - // Safety: the Mun Compiler guarantees that `new` is never called with `ptr::null()`. - let type_info = UnsafeTypeInfo::new(unsafe { NonNull::new_unchecked(type_info as *mut _) }); - let handle = allocator.alloc(type_info); - - // Prevent destruction of the allocator - mem::forget(allocator); - - handle.into() -} - impl Runtime { /// Constructs a new `Runtime` that loads the library at `library_path` and its /// dependencies. The `Runtime` contains a file watcher that is triggered with an interval @@ -226,12 +223,14 @@ impl Runtime { storages.push(storage) } - let watcher: RecommendedWatcher = Watcher::new(tx, options.delay)?; + let watcher: RecommendedWatcher = Watcher::new_raw(tx)?; let mut runtime = Runtime { assemblies: HashMap::new(), + assemblies_to_relink: VecDeque::new(), dispatch_table, watcher, watcher_rx: rx, + renamed_files: HashMap::new(), gc: Arc::new(self::garbage_collector::GarbageCollector::default()), _user_functions: storages, }; @@ -257,6 +256,11 @@ impl Runtime { // Load all assemblies and their dependencies while let Some(library_path) = to_load.pop_front() { + // A dependency can be added by multiple dependants, so check that we didn't load it yet + if loaded.contains_key(&library_path) { + continue; + } + let assembly = Assembly::load(&library_path, self.gc.clone())?; let parent = library_path.parent().expect("Invalid library path"); @@ -312,29 +316,104 @@ impl Runtime { /// Updates the state of the runtime. This includes checking for file changes, and reloading /// compiled assemblies. pub fn update(&mut self) -> bool { + fn is_lockfile(path: &Path) -> bool { + path.file_name().expect("Invalid file path.") == OsString::from(LOCKFILE_NAME) + } + + fn relink_assemblies(runtime: &mut Runtime) -> anyhow::Result { + let mut loaded = HashMap::new(); + let to_load = &mut runtime.assemblies_to_relink; + + info!("Relinking assemblies:"); + for (old_path, new_path) in to_load.iter() { + info!( + "{} -> {}", + old_path.to_string_lossy(), + new_path.to_string_lossy() + ); + } + + // Load all assemblies and their dependencies + while let Some((old_path, new_path)) = to_load.pop_front() { + // A dependency can be added by multiple dependants, so check that we didn't load it yet + if loaded.contains_key(&old_path) { + continue; + } + + let assembly = Assembly::load(&new_path, runtime.gc.clone())?; + + let parent = new_path.parent().expect("Invalid library path"); + let extension = new_path.extension(); + + let dependencies: Vec = + assembly.info().dependencies().map(From::from).collect(); + loaded.insert(old_path.clone(), assembly); + + for dependency in dependencies { + let mut library_path = PathBuf::from(parent.join(dependency)); + if let Some(extension) = extension { + library_path = library_path.with_extension(extension); + } + + if !loaded.contains_key(&library_path) + && !runtime.assemblies.contains_key(&library_path) + { + to_load.push_back((old_path.clone(), library_path)); + } + } + } + + Assembly::relink_all( + &mut loaded, + &mut runtime.assemblies, + &runtime.dispatch_table, + ) + } + while let Ok(event) = self.watcher_rx.try_recv() { - use notify::DebouncedEvent::*; - match event { - Write(ref path) | Rename(_, ref path) | Create(ref path) => { - if let Some(assembly) = self.assemblies.get_mut(path) { - if let Err(e) = assembly.swap(path, &mut self.dispatch_table) { - println!( - "An error occured while reloading assembly '{}': {:?}", - path.to_string_lossy(), - e - ); + if let Some(path) = event.path { + let op = event.op.expect("Invalid event."); + + if is_lockfile(&path) { + if op.contains(notify::op::CREATE) { + debug!("Lockfile created"); + } else if op.contains(notify::op::REMOVE) { + debug!("Lockfile deleted"); + + match relink_assemblies(self) { + Ok(table) => { + info!("Succesfully reloaded assemblies."); + + self.dispatch_table = table; + self.assemblies_to_relink.clear(); + + return true; + } + Err(e) => error!("Failed to relink assemblies, due to {}.", e), + } + } + } else { + let path = path.canonicalize().expect(&format!( + "Failed to canonicalize path: {}.", + path.to_string_lossy() + )); + + if op.contains(notify::op::RENAME) { + let cookie = event.cookie.expect("Invalid RENAME event."); + if let Some(old_path) = self.renamed_files.remove(&cookie) { + self.assemblies_to_relink.push_back((old_path, path)); + // on_file_changed(self, &old_path, &path); } else { - println!( - "Succesfully reloaded assembly: '{}'", - path.to_string_lossy() - ); - return true; + self.renamed_files.insert(cookie, path); } + } else if op.contains(notify::op::WRITE) { + // TODO: don't overwrite existing + self.assemblies_to_relink.push_back((path.clone(), path)); } } - _ => {} } } + false } diff --git a/crates/mun_runtime_capi/ffi b/crates/mun_runtime_capi/ffi index 966514e82..02b0e9681 160000 --- a/crates/mun_runtime_capi/ffi +++ b/crates/mun_runtime_capi/ffi @@ -1 +1 @@ -Subproject commit 966514e820aaa7bbf9ad79faa5d3377f3a32d09a +Subproject commit 02b0e96812c570fb1541ccf29ae475e32d5f08b7 diff --git a/crates/mun_runtime_capi/src/lib.rs b/crates/mun_runtime_capi/src/lib.rs index 71114f037..d026b47d4 100644 --- a/crates/mun_runtime_capi/src/lib.rs +++ b/crates/mun_runtime_capi/src/lib.rs @@ -11,11 +11,12 @@ pub mod hub; #[cfg(test)] mod tests; -use std::ffi::{c_void, CStr, CString}; -use std::{os::raw::c_char, time::Duration}; +use std::{ + ffi::{c_void, CStr, CString}, + os::raw::c_char, +}; -use crate::error::ErrorHandle; -use crate::hub::HUB; +use crate::{error::ErrorHandle, hub::HUB}; use anyhow::anyhow; use runtime::Runtime; @@ -45,10 +46,6 @@ pub struct RuntimeHandle(*mut c_void); #[repr(C)] #[derive(Clone, Copy)] pub struct RuntimeOptions { - /// The interval at which changes to the disk are detected. `0` will initialize this value to - /// default. - pub delay_ms: u32, - /// Function definitions that should be inserted in the runtime before a mun library is loaded. /// This is useful to initialize `extern` functions used in a mun library. /// @@ -63,7 +60,6 @@ pub struct RuntimeOptions { impl Default for RuntimeOptions { fn default() -> Self { RuntimeOptions { - delay_ms: 0, functions: std::ptr::null(), num_functions: 0, } @@ -118,12 +114,6 @@ pub unsafe extern "C" fn mun_runtime_create( } }; - let delay_ms = if options.delay_ms > 0 { - options.delay_ms - } else { - 10 - }; - let user_functions = std::slice::from_raw_parts(options.functions, options.num_functions as usize) .iter() @@ -139,7 +129,6 @@ pub unsafe extern "C" fn mun_runtime_create( let runtime_options = runtime::RuntimeOptions { library_path: library_path.into(), - delay: Duration::from_millis(delay_ms.into()), user_functions, };