tokio-rs / loom

Concurrency permutation testing tool for Rust.
MIT License
2.14k stars 111 forks source link

loom model appears to be executed only once when using `tokio` #256

Closed CBenoit closed 2 years ago

CBenoit commented 2 years ago

Hi,

I'm unable to run my loom model for some reason. (Permalink to my attempt)

My test harness is:

#[test]
fn jmux_proxy() {
    loom::model(|| {
        let decorator = slog_term::PlainDecorator::new(slog_term::TestStdoutWriter);
        let drain = slog_term::CompactFormat::new(decorator).build().fuse();
        let async_drain = slog_async::Async::new(drain).build().fuse();
        let logger = slog::Logger::root(async_drain, slog::o!());

        let client_targets = &[("232.192.0.94", 42), ("3.199.201.72", 666), ("232.192.0.94", 42)];
        let echo_server_addrs = &[("232.192.0.94", 42), ("3.199.201.72", 666)];
        let jmux_server_addr = ("3.141.0.207", 893);

        let rt = Builder::new_multi_thread().build().unwrap();

        rt.block_on(async {
            let mut to_await = Vec::new();
            let mut to_abort = Vec::new();

            for (index, addr) in echo_server_addrs.into_iter().enumerate() {
                to_abort.push(tokio::spawn(echo_server(
                    *addr,
                    logger.new(slog::o!("echo server" => index)),
                )));
            }

            let (api_request_tx, api_request_rx) = mpsc::channel(3);

            to_abort.push(tokio::spawn(server_side_jmux(jmux_server_addr, logger.clone())));

            to_abort.push(tokio::spawn(client_side_jmux(
                api_request_rx,
                jmux_server_addr,
                logger.clone(),
            )));

            for (index, target) in client_targets.into_iter().enumerate() {
                to_await.push(tokio::spawn(
                    client(api_request_tx.clone(), *target)
                        .map(move |res| res.with_context(|| format!("client {}", index))),
                ));
            }

            for handle in to_await {
                handle.await.unwrap().unwrap();
            }

            for handle in to_abort {
                handle.abort();
                assert!(handle.await.unwrap_err().is_cancelled());
            }
        });
    })
}

I'm running using this command:

$ RUSTFLAGS="--cfg loom" cargo test --test loom --release -- --nocapture

Maybe because of the new_multi_thread, I get the panic even though I'm indeed running from loom::model:

thread 'tokio-runtime-worker' panicked at 'cannot access Loom execution state from outside a Loom model. are you accessing a Loom synchronization primitive from outside a Loom test (a call to `model` or `check`)?'

When I replace new_multi_thread by new_current_thread, the test ends successfully, but adding a println! statement reveal the model is executed only once.

I believe tokio is instrumented to use loom when --cfg loom is used, and I updated my own code to use loom primitives instead of std when appropriate. As such, I'm expecting more than one permutation to be executed.

taiki-e commented 2 years ago

I believe tokio is instrumented to use loom when --cfg loom is used

AFAIK, tokio's cfg(loom) is for testing tokio itself and is not exposed to the users.

https://github.com/tokio-rs/tokio/blob/702d6dccc948b6a460caa52da4e4c03b252c5c3b/tokio/Cargo.toml#L145-L146

[target.'cfg(loom)'.dev-dependencies] loom = { version = "0.5.2", features = ["futures", "checkpoint"] }

CBenoit commented 2 years ago

Oh, I understand. So I can't use tokio to mock a thread pool, and obviously there is a single permutation when there is no parallelism at all (new_current_thread). Makes sense. Thank you for the answer!