Skip to content

Commit d048f20

Browse files
authored
Seed tokio with the world rng (#245)
* Introduce `rt::Config` type In preparation for making tokio's rng seed configurable, introduce a new `rt::Config` struct that holds configuration for creating tokio runtimes and pass that around instead of the bare `enable_io` bool. * Seed tokio runtimes using the world rng If the `tokio_unstable` flag is set, seed host and client runtimes using the world rng. This makes the simulation more deterministic. In particular, the order in which tokio polls `select!` branches is determined by this seed.
1 parent a242a2a commit d048f20

File tree

2 files changed

+51
-20
lines changed

2 files changed

+51
-20
lines changed

src/rt.rs

Lines changed: 34 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ use std::mem;
33
use std::pin::Pin;
44
use std::sync::Arc;
55

6+
use rand::rngs::SmallRng;
67
use tokio::runtime::Runtime;
78
use tokio::task::JoinHandle;
89
use tokio::task::LocalSet;
@@ -28,6 +29,18 @@ pub enum Kind<'a> {
2829
NoSoftware,
2930
}
3031

32+
/// Runtime configuration.
33+
#[derive(Clone, Default)]
34+
pub struct Config {
35+
/// Whether io is enabled on the runtime.
36+
pub enable_io: bool,
37+
38+
/// Rng used to seed the tokio runtime.
39+
///
40+
/// Only used if the `tokio_unstable` cfg flag is set.
41+
pub rng: Option<SmallRng>,
42+
}
43+
3144
/// Per host simulated runtime.
3245
///
3346
/// The tokio runtime is paused (see [`Builder::start_paused`]), which gives us
@@ -50,16 +63,16 @@ pub struct Rt<'a> {
5063
/// consumed to check for error, which is propagated up to fail the simulation.
5164
handle: Option<JoinHandle<Result>>,
5265

53-
/// Whether io is enabled on this runtime.
54-
enable_io: bool,
66+
/// Runtime configuration.
67+
config: Config,
5568
}
5669

5770
impl<'a> Rt<'a> {
58-
pub(crate) fn client<F>(nodename: Arc<str>, client: F, enable_io: bool) -> Self
71+
pub(crate) fn client<F>(nodename: Arc<str>, client: F, mut config: Config) -> Self
5972
where
6073
F: Future<Output = Result> + 'static,
6174
{
62-
let (tokio, local) = init(enable_io);
75+
let (tokio, local) = init(&mut config);
6376

6477
let handle = with(&tokio, &local, || tokio::task::spawn_local(client));
6578

@@ -69,16 +82,16 @@ impl<'a> Rt<'a> {
6982
local,
7083
nodename,
7184
handle: Some(handle),
72-
enable_io,
85+
config,
7386
}
7487
}
7588

76-
pub(crate) fn host<F, Fut>(nodename: Arc<str>, software: F, enable_io: bool) -> Self
89+
pub(crate) fn host<F, Fut>(nodename: Arc<str>, software: F, mut config: Config) -> Self
7790
where
7891
F: Fn() -> Fut + 'a,
7992
Fut: Future<Output = Result> + 'static,
8093
{
81-
let (tokio, local) = init(enable_io);
94+
let (tokio, local) = init(&mut config);
8295

8396
let software: Software = Box::new(move || Box::pin(software()));
8497
let handle = with(&tokio, &local, || tokio::task::spawn_local(software()));
@@ -89,20 +102,21 @@ impl<'a> Rt<'a> {
89102
local,
90103
nodename,
91104
handle: Some(handle),
92-
enable_io,
105+
config,
93106
}
94107
}
95108

96109
pub(crate) fn no_software() -> Self {
97-
let (tokio, local) = init(false);
110+
let mut config = Config::default();
111+
let (tokio, local) = init(&mut config);
98112

99113
Self {
100114
kind: Kind::NoSoftware,
101115
tokio,
102116
local,
103117
nodename: String::new().into(),
104118
handle: None,
105-
enable_io: false,
119+
config,
106120
}
107121
}
108122

@@ -207,23 +221,30 @@ impl<'a> Rt<'a> {
207221
///
208222
/// Both the [`Runtime`] and [`LocalSet`] are replaced with new instances.
209223
fn cancel_tasks(&mut self) {
210-
let (tokio, local) = init(self.enable_io);
224+
let (tokio, local) = init(&mut self.config);
211225

212226
_ = mem::replace(&mut self.tokio, tokio);
213227
drop(mem::replace(&mut self.local, local));
214228
}
215229
}
216230

217-
fn init(enable_io: bool) -> (Runtime, LocalSet) {
231+
fn init(config: &mut Config) -> (Runtime, LocalSet) {
218232
let mut tokio_builder = tokio::runtime::Builder::new_current_thread();
219233

220234
#[cfg(tokio_unstable)]
221235
tokio_builder.unhandled_panic(tokio::runtime::UnhandledPanic::ShutdownRuntime);
222236

223-
if enable_io {
237+
if config.enable_io {
224238
tokio_builder.enable_io();
225239
}
226240

241+
#[cfg(tokio_unstable)]
242+
if let Some(rng) = &mut config.rng {
243+
let bytes: [u8; 32] = rand::Rng::gen(rng);
244+
let seed = tokio::runtime::RngSeed::from_bytes(&bytes);
245+
tokio_builder.rng_seed(seed);
246+
}
247+
227248
let tokio = tokio_builder
228249
.enable_time()
229250
.start_paused(true)

src/sim.rs

Lines changed: 17 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
use rand::seq::SliceRandom;
1+
use rand::{rngs::SmallRng, seq::SliceRandom, Rng, SeedableRng};
22
use std::cell::RefCell;
33
use std::future::Future;
44
use std::net::IpAddr;
@@ -91,9 +91,14 @@ impl<'a> Sim<'a> {
9191
world.register(addr, &nodename, HostTimer::new(self.elapsed), &self.config);
9292
}
9393

94-
let rt = World::enter(&self.world, || {
95-
Rt::client(nodename, client, self.config.enable_tokio_io)
96-
});
94+
let seed = self.world.borrow_mut().rng.gen();
95+
let rng = SmallRng::from_seed(seed);
96+
let config = rt::Config {
97+
enable_io: self.config.enable_tokio_io,
98+
rng: Some(rng),
99+
};
100+
101+
let rt = World::enter(&self.world, || Rt::client(nodename, client, config));
97102

98103
self.rts.insert(addr, rt);
99104
}
@@ -126,9 +131,14 @@ impl<'a> Sim<'a> {
126131
world.register(addr, &nodename, HostTimer::new(self.elapsed), &self.config);
127132
}
128133

129-
let rt = World::enter(&self.world, || {
130-
Rt::host(nodename, host, self.config.enable_tokio_io)
131-
});
134+
let seed = self.world.borrow_mut().rng.gen();
135+
let rng = SmallRng::from_seed(seed);
136+
let config = rt::Config {
137+
enable_io: self.config.enable_tokio_io,
138+
rng: Some(rng),
139+
};
140+
141+
let rt = World::enter(&self.world, || Rt::host(nodename, host, config));
132142

133143
self.rts.insert(addr, rt);
134144
}

0 commit comments

Comments
 (0)