Skip to content

Commit 2b37a35

Browse files
committed
Initial commit
0 parents  commit 2b37a35

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

42 files changed

+3850
-0
lines changed

Diff for: .gitignore

+2
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
target
2+
Cargo.lock

Diff for: .travis.yml

+25
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
---
2+
language: rust
3+
rust:
4+
- stable
5+
- nightly
6+
7+
script:
8+
# Run tests with thread based runtime
9+
- cargo test
10+
11+
# Run tests with generator based runtime
12+
- cargo test --tests --features generator
13+
14+
# Run tests with fringe based runtime
15+
- |
16+
if [ "${TRAVIS_RUST_VERSION}" = "nightly" ];
17+
then cargo test --tests --features fringe;
18+
fi
19+
20+
# Check with serde based checkpoint feature
21+
- cargo check --features checkpoint
22+
23+
notifications:
24+
email:
25+
on_success: never

Diff for: CHANGELOG.md

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
# 0.1.0 (unreleased)
2+
3+
* Initial release

Diff for: Cargo.toml

+45
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
[package]
2+
name = "loom"
3+
# When releasing to crates.io:
4+
# - Update version number
5+
# - lib.rs: html_root_url.
6+
# - README.md
7+
# - Update CHANGELOG.md
8+
# - Update doc URL.
9+
# - Cargo.toml
10+
# - README.md
11+
# - Create git tag
12+
version = "0.1.0"
13+
license = "MIT"
14+
authors = ["Carl Lerche <[email protected]>"]
15+
description = "Model checker for concurrent code"
16+
documentation = "https://docs.rs/loom/0.1.0/loom"
17+
homepage = "https://github.com/carllerche/loom"
18+
repository = "https://github.com/carllerche/loom"
19+
readme = "README.md"
20+
keywords = ["atomic", "lock-free"]
21+
categories = ["concurrency", "data-structures"]
22+
23+
[features]
24+
default = []
25+
checkpoint = ["serde", "serde_derive", "serde_json"]
26+
27+
[dependencies]
28+
cfg-if = "0.1.6"
29+
libc = "0.2.44"
30+
scoped-tls = "0.1.2"
31+
scoped-mut-tls = { version = "0.1.0", git = "http://github.com/carllerche/scoped-mut-tls" }
32+
33+
# Provides a generator based runtime
34+
generator = { version = "0.6.10", optional = true }
35+
36+
# Provides a runtime based on libfringe. Requires nightly.
37+
fringe = { git = "https://github.com/carllerche/libfringe", branch = "track-nightly", optional = true }
38+
39+
# Optional futures support
40+
futures = { version = "", optional = true }
41+
42+
# Requires for "checkpoint" feature
43+
serde = { version = "1.0.80", optional = true }
44+
serde_derive = { version = "1.0.80", optional = true }
45+
serde_json = { version = "1.0.33", optional = true }

Diff for: LICENSE

+25
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
Copyright (c) 2018 Carl Lerche
2+
3+
Permission is hereby granted, free of charge, to any
4+
person obtaining a copy of this software and associated
5+
documentation files (the "Software"), to deal in the
6+
Software without restriction, including without
7+
limitation the rights to use, copy, modify, merge,
8+
publish, distribute, sublicense, and/or sell copies of
9+
the Software, and to permit persons to whom the Software
10+
is furnished to do so, subject to the following
11+
conditions:
12+
13+
The above copyright notice and this permission notice
14+
shall be included in all copies or substantial portions
15+
of the Software.
16+
17+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
18+
ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
19+
TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
20+
PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
21+
SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
22+
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
23+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
24+
IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
25+
DEALINGS IN THE SOFTWARE.

Diff for: README.md

+32
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
# Loom
2+
3+
Loom is a model checker for concurrent Rust code. It exhaustively explores the
4+
behaviors of code under the C11 memory model, which Rust inherits.
5+
6+
## Getting started
7+
8+
To use `loom`, first add this to your `Cargo.toml`:
9+
10+
```toml
11+
[dev-dependencies]
12+
loom = "0.1.0"
13+
```
14+
15+
Next, create a test file.
16+
17+
## Implementation
18+
19+
Loom is an implementation of techniques described in [CDSChecker: Checking
20+
Concurrent Data Structures Written with C/C++ Atomics][cdschecker].
21+
22+
[cdschecker]: http://demsky.eecs.uci.edu/publications/c11modelcheck.pdf
23+
24+
## License
25+
26+
This project is licensed under the [MIT license](LICENSE).
27+
28+
### Contribution
29+
30+
Unless you explicitly state otherwise, any contribution intentionally submitted
31+
for inclusion in `loom` by you, shall be licensed as MIT, without any additional
32+
terms or conditions.

Diff for: src/futures/atomic_task.rs

+35
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
use super::task;
2+
use std::cell::RefCell;
3+
4+
#[derive(Debug)]
5+
pub struct AtomicTask {
6+
task: RefCell<Option<task::Task>>,
7+
}
8+
9+
impl AtomicTask {
10+
pub fn new() -> AtomicTask {
11+
AtomicTask {
12+
task: RefCell::new(None),
13+
}
14+
}
15+
16+
pub fn register(&self) {
17+
self.register_task(task::current());
18+
}
19+
20+
pub fn register_task(&self, task: task::Task) {
21+
*self.task.borrow_mut() = Some(task);
22+
}
23+
24+
pub fn notify(&self) {
25+
if let Some(task) = self.task.borrow_mut().take() {
26+
task.notify();
27+
}
28+
}
29+
}
30+
31+
impl Default for AtomicTask {
32+
fn default() -> Self {
33+
AtomicTask::new()
34+
}
35+
}

Diff for: src/futures/mod.rs

+34
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
mod atomic_task;
2+
3+
pub use self::atomic_task::AtomicTask;
4+
5+
use rt;
6+
use _futures::Future;
7+
8+
pub fn spawn<F>(f: F)
9+
where
10+
F: Future<Item = (), Error = ()> + 'static,
11+
{
12+
rt::spawn(move || rt::wait_future(f));
13+
}
14+
15+
pub mod task {
16+
use rt;
17+
18+
#[derive(Debug)]
19+
pub struct Task {
20+
thread: rt::thread::Id,
21+
}
22+
23+
pub fn current() -> Task {
24+
Task {
25+
thread: rt::thread::Id::current(),
26+
}
27+
}
28+
29+
impl Task {
30+
pub fn notify(&self) {
31+
self.thread.future_notify();
32+
}
33+
}
34+
}

Diff for: src/fuzz.rs

+185
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,185 @@
1+
use rt::{self, Execution, Scheduler};
2+
3+
use std::path::PathBuf;
4+
use std::sync::Arc;
5+
6+
const DEFAULT_MAX_THREADS: usize = 4;
7+
8+
const DEFAULT_MAX_MEMORY: usize = 4096 << 14;
9+
10+
#[derive(Debug)]
11+
pub struct Builder {
12+
/// Max number of threads to check as part of the execution. This should be set as low as possible.
13+
pub max_threads: usize,
14+
15+
/// Maximum amount of memory that can be consumed by the associated metadata.
16+
pub max_memory: usize,
17+
18+
/// When doing an exhaustive fuzz, uses the file to store and load the fuzz
19+
/// progress
20+
pub checkpoint_file: Option<PathBuf>,
21+
22+
/// How often to write the checkpoint file
23+
pub checkpoint_interval: usize,
24+
25+
/// What runtime to use
26+
pub runtime: Runtime,
27+
28+
/// Log execution output to stdout.
29+
pub log: bool,
30+
}
31+
32+
#[derive(Debug)]
33+
pub enum Runtime {
34+
Thread,
35+
36+
#[cfg(feature = "generator")]
37+
Generator,
38+
39+
#[cfg(feature = "fringe")]
40+
Fringe,
41+
}
42+
43+
impl Builder {
44+
pub fn new() -> Builder {
45+
Builder {
46+
max_threads: DEFAULT_MAX_THREADS,
47+
max_memory: DEFAULT_MAX_MEMORY,
48+
checkpoint_file: None,
49+
checkpoint_interval: 100_000,
50+
51+
#[cfg(feature = "fringe")]
52+
runtime: Runtime::Fringe,
53+
54+
#[cfg(all(feature = "generator", not(feature = "fringe")))]
55+
runtime: Runtime::Generator,
56+
57+
#[cfg(all(not(feature = "generator"), not(feature = "fringe")))]
58+
runtime: Runtime::Thread,
59+
60+
log: false,
61+
}
62+
}
63+
64+
pub fn checkpoint_file(&mut self, file: &str) -> &mut Self {
65+
self.checkpoint_file = Some(file.into());
66+
self
67+
}
68+
69+
pub fn fuzz<F>(&self, f: F)
70+
where
71+
F: Fn() + Sync + Send + 'static,
72+
{
73+
let mut execution = Execution::new(self.max_threads, self.max_memory);
74+
let mut scheduler = match self.runtime {
75+
Runtime::Thread => Scheduler::new_thread(self.max_threads),
76+
77+
#[cfg(feature = "generator")]
78+
Runtime::Generator => Scheduler::new_generator(self.max_threads),
79+
80+
#[cfg(feature = "fringe")]
81+
Runtime::Fringe => Scheduler::new_fringe(self.max_threads),
82+
};
83+
84+
if let Some(ref path) = self.checkpoint_file {
85+
if path.exists() {
86+
execution.path = checkpoint::load_execution_path(path);
87+
}
88+
}
89+
90+
execution.log = self.log;
91+
92+
let f = Arc::new(f);
93+
94+
let mut i = 0;
95+
96+
loop {
97+
i += 1;
98+
99+
if i % self.checkpoint_interval == 0 {
100+
println!(" ===== iteration {} =====", i);
101+
102+
if let Some(ref path) = self.checkpoint_file {
103+
checkpoint::store_execution_path(&execution.path, path);
104+
}
105+
}
106+
107+
let f = f.clone();
108+
109+
scheduler.run(&mut execution, move || {
110+
f();
111+
rt::thread_done();
112+
});
113+
114+
if let Some(next) = execution.step() {
115+
execution = next;
116+
} else {
117+
return;
118+
}
119+
}
120+
}
121+
}
122+
123+
pub fn fuzz<F>(f: F)
124+
where
125+
F: Fn() + Sync + Send + 'static,
126+
{
127+
Builder::new().fuzz(f)
128+
}
129+
130+
if_futures! {
131+
use _futures::Future;
132+
133+
impl Builder {
134+
pub fn fuzz_future<F, R>(&self, f: F)
135+
where
136+
F: Fn() -> R + Sync + Send + 'static,
137+
R: Future<Item = (), Error = ()>,
138+
{
139+
self.fuzz(move || rt::wait_future(f()));
140+
}
141+
}
142+
143+
pub fn fuzz_future<F, R>(f: F)
144+
where
145+
F: Fn() -> R + Sync + Send + 'static,
146+
R: Future<Item = (), Error = ()>,
147+
{
148+
Builder::new().fuzz_future(f);
149+
}
150+
}
151+
152+
#[cfg(feature = "checkpoint")]
153+
mod checkpoint {
154+
use serde_json;
155+
use std::fs::File;
156+
use std::io::prelude::*;
157+
use std::path::Path;
158+
159+
pub(crate) fn load_execution_path(fs_path: &Path) -> ::rt::Path {
160+
let mut file = File::open(fs_path).unwrap();
161+
let mut contents = String::new();
162+
file.read_to_string(&mut contents).unwrap();
163+
serde_json::from_str(&contents).unwrap()
164+
}
165+
166+
pub(crate) fn store_execution_path(path: &::rt::Path, fs_path: &Path) {
167+
let serialized = serde_json::to_string(path).unwrap();
168+
169+
let mut file = File::create(fs_path).unwrap();
170+
file.write_all(serialized.as_bytes()).unwrap();
171+
}
172+
}
173+
174+
#[cfg(not(feature = "checkpoint"))]
175+
mod checkpoint {
176+
use std::path::Path;
177+
178+
pub(crate) fn load_execution_path(_fs_path: &Path) -> ::rt::Path {
179+
panic!("not compiled with `checkpoint` feature")
180+
}
181+
182+
pub(crate) fn store_execution_path(_path: &::rt::Path, _fs_path: &Path) {
183+
panic!("not compiled with `checkpoint` feature")
184+
}
185+
}

0 commit comments

Comments
 (0)