tokio-rs / loom

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

Consider using a feature rather than cfg flag #235

Closed KamilaBorowska closed 3 years ago

KamilaBorowska commented 3 years ago

Currently Loom recommends using target specific dependencies.

[target.'cfg(loom)'.dependencies]
loom = "0.5"

This works, however it has certain drawbacks:

All those issues would be solvable if loom did use a feature rather than a config flag. To clarify what I mean: the user could use loom like this in Cargo.toml.

[dependencies]
loom = "0.6"
[dev-dependencies]
loom = { version = "0.6", features = ["model"] }

Then import whatever they want by simply using use, without having to bother with #[cfg] or cfg_if!.

use loom::sync::atomic::AtomicUsize;

That would work because Loom crate would provide a basic AtomicUsize implementation that directly uses methods from std when model feature is not enabled. This means no performance cost when not using model feature.

if_cfg! {
    if #[cfg(feature = "model")] {
        pub struct AtomicUsize(loom::Atomic<usize>);
        impl AtomicUsize {
            #[track_caller]
            pub fn with_mut<R>(&mut self, f: impl FnOnce(&mut usize) -> R) -> R {
                self.0.with_mut(f)
            }

            // ...
        }
    } else {
        pub struct AtomicUsize(std::sync::atomic::AtomicUsize);
        impl AtomicUsize {
            pub fn with_mut<R>(&mut self, f: impl FnOnce(&mut usize) -> R) -> R {
                f(self.get_mut())
            }

            // ...
        }
    }
}

Some possible concerns:

taiki-e commented 3 years ago

cargo feature needs to be additive. see also https://github.com/crossbeam-rs/crossbeam/pull/638

KamilaBorowska commented 3 years ago

This is additive I believe unless I'm missing something? A model feature would add loom::model function to an API, and that's it. It also would modify a lot of things to interact with model, but the user-visible API wouldn't change, and enabling model wouldn't make changes in other functions visible when not using model function (other than making them slower).

KamilaBorowska commented 3 years ago

Oh, I see, Loom functions expect loom::model to be used first? Yeah, that is an issue. I wonder if it's possible to make them behave like normal functions when not in loom::model scope.

taiki-e commented 3 years ago

This is additive I believe unless I'm missing something? A model feature would add loom::model function to an API, and that's it. It also would modify a lot of things to interact with model, but the user-visible API wouldn't change, and enabling model wouldn't make changes in other functions visible when not using model function (other than making them slower).

Some of the features do not work outside of the loom::model function. For example, removing this function's loom::model will cause a panic with "cannot access a scoped thread local variable without calling set first".

https://github.com/tokio-rs/loom/blob/0d7f0d5a00d80e1c7a78ac6bfa1083b20a0448e1/tests/atomic.rs#L32-L42

A feature that can break most of the existing tests with panic should not be called "additive", even if it does not seem to break the API.

taiki-e commented 3 years ago
  • cargo vendor vendors dependencies of loom.
  • Dependencies of loom are included in Cargo.lock when using a crate that depends on loom.
  • cargo audit includes dependencies of loom when using a crate that depends on loom.

The workaround for these problems is to make loom an optional dependency: https://github.com/crossbeam-rs/crossbeam/pull/666