From cbaaaba6be45133ebfc5e0200ed2124b5498b289 Mon Sep 17 00:00:00 2001 From: Sergii Tereshchenko <125313157+tereshch-aws@users.noreply.github.com> Date: Tue, 20 Feb 2024 08:39:51 -0800 Subject: [PATCH] Make sim_elapsed function safe to call outside of simulation (#170) If customer set custom timer for tracing subscriber that uses sim_elapsed to log simulation time, this function might be called before world or host set. E.g. with DEBUG level logging set this function panics on internal log lines in turmoil crate code before host is set. --- src/lib.rs | 14 ++++++++++---- src/sim.rs | 17 ++++++++++++++++- src/world.rs | 5 +++++ 3 files changed, 31 insertions(+), 5 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 77f2955..2eaecd7 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -182,11 +182,17 @@ pub fn elapsed() -> Duration { /// Returns how long the simulation has been executing for in virtual time. /// -/// Must be called from within a Turmoil simulation. Will return None if the -/// duration is not available, typically because there is no currently executing -/// host. +/// Will return None if the duration is not available, typically because +/// there is no currently executing host or world. pub fn sim_elapsed() -> Option { - World::try_current(|world| world.current_host().timer.sim_elapsed()).ok() + World::try_current(|world| { + world + .try_current_host() + .map(|host| host.timer.sim_elapsed()) + .ok() + }) + .ok() + .flatten() } /// Lookup an IP address by host name. diff --git a/src/sim.rs b/src/sim.rs index 1e4dd35..3178210 100644 --- a/src/sim.rs +++ b/src/sim.rs @@ -414,7 +414,7 @@ mod test { }; use crate::{ - elapsed, hold, + elapsed, hold, World, net::{TcpListener, TcpStream}, sim_elapsed, Builder, Result, }; @@ -560,6 +560,21 @@ mod test { Ok(()) } + /// This is a regression test to ensure it is safe to call sim_elapsed + /// if current world of host is not set. + #[test] + fn sim_elapsed_time() -> Result { + // Safe to call outside of simution while there + // is no current world set + assert!(sim_elapsed().is_none()); + + let sim = Builder::new().build(); + // Safe to call while there is no current host set + World::enter(&sim.world, || assert!(sim_elapsed().is_none())); + + Ok(()) + } + #[test] fn hold_release_peers() -> Result { let global = Duration::from_millis(2); diff --git a/src/world.rs b/src/world.rs index c23594c..2fd2698 100644 --- a/src/world.rs +++ b/src/world.rs @@ -101,6 +101,11 @@ impl World { self.hosts.get(&addr).expect("host missing") } + pub(crate) fn try_current_host(&self) -> TurmoilResult<&Host> { + let addr = self.current.ok_or("current host missing")?; + self.hosts.get(&addr).ok_or_else(|| "host missing".into()) + } + pub(crate) fn lookup(&mut self, host: impl ToIpAddr) -> IpAddr { self.dns.lookup(host) }