197 lines
4.2 KiB
Rust
197 lines
4.2 KiB
Rust
#[macro_use]
|
|
mod location;
|
|
pub(crate) use self::location::Location;
|
|
|
|
mod access;
|
|
use self::access::Access;
|
|
|
|
mod alloc;
|
|
pub(crate) use self::alloc::{alloc, dealloc, Allocation};
|
|
|
|
mod arc;
|
|
pub(crate) use self::arc::Arc;
|
|
|
|
mod atomic;
|
|
pub(crate) use self::atomic::{fence, Atomic};
|
|
|
|
pub(crate) mod cell;
|
|
pub(crate) use self::cell::Cell;
|
|
|
|
mod condvar;
|
|
pub(crate) use self::condvar::Condvar;
|
|
|
|
mod execution;
|
|
pub(crate) use self::execution::Execution;
|
|
|
|
mod notify;
|
|
pub(crate) use self::notify::Notify;
|
|
|
|
mod num;
|
|
pub(crate) use self::num::Numeric;
|
|
|
|
#[macro_use]
|
|
pub(crate) mod object;
|
|
|
|
mod mpsc;
|
|
pub(crate) use self::mpsc::Channel;
|
|
|
|
mod mutex;
|
|
pub(crate) use self::mutex::Mutex;
|
|
|
|
mod path;
|
|
pub(crate) use self::path::Path;
|
|
|
|
mod rwlock;
|
|
pub(crate) use self::rwlock::RwLock;
|
|
|
|
mod scheduler;
|
|
pub(crate) use self::scheduler::Scheduler;
|
|
|
|
mod synchronize;
|
|
pub(crate) use self::synchronize::Synchronize;
|
|
|
|
pub(crate) mod lazy_static;
|
|
pub(crate) mod thread;
|
|
|
|
mod vv;
|
|
pub(crate) use self::vv::VersionVec;
|
|
|
|
use tracing::trace;
|
|
|
|
/// Maximum number of threads that can be included in a model.
|
|
pub const MAX_THREADS: usize = 4;
|
|
|
|
/// Maximum number of atomic store history to track per-cell.
|
|
pub(crate) const MAX_ATOMIC_HISTORY: usize = 7;
|
|
|
|
pub(crate) fn spawn<F>(f: F) -> crate::rt::thread::Id
|
|
where
|
|
F: FnOnce() + 'static,
|
|
{
|
|
let id = execution(|execution| execution.new_thread());
|
|
|
|
trace!(thread = ?id, "spawn");
|
|
|
|
Scheduler::spawn(Box::new(move || {
|
|
f();
|
|
thread_done();
|
|
}));
|
|
|
|
id
|
|
}
|
|
|
|
/// Marks the current thread as blocked
|
|
pub(crate) fn park(location: Location) {
|
|
let switch = execution(|execution| {
|
|
use thread::State;
|
|
let thread = execution.threads.active_id();
|
|
let active = execution.threads.active_mut();
|
|
|
|
trace!(?thread, ?active.state, "park");
|
|
|
|
match active.state {
|
|
// The thread was previously unparked while it was active. Instead
|
|
// of parking, consume the unpark.
|
|
State::Runnable { unparked: true } => {
|
|
active.set_runnable();
|
|
return false;
|
|
}
|
|
// The thread doesn't have a saved unpark; set its state to blocked.
|
|
_ => active.set_blocked(location),
|
|
};
|
|
|
|
execution.threads.active_mut().set_blocked(location);
|
|
execution.threads.active_mut().operation = None;
|
|
execution.schedule()
|
|
});
|
|
|
|
if switch {
|
|
Scheduler::switch();
|
|
}
|
|
}
|
|
|
|
/// Add an execution branch point.
|
|
fn branch<F, R>(f: F) -> R
|
|
where
|
|
F: FnOnce(&mut Execution) -> R,
|
|
{
|
|
let (ret, switch) = execution(|execution| {
|
|
let ret = f(execution);
|
|
let switch = execution.schedule();
|
|
|
|
trace!(?switch, "branch");
|
|
|
|
(ret, switch)
|
|
});
|
|
|
|
if switch {
|
|
Scheduler::switch();
|
|
}
|
|
|
|
ret
|
|
}
|
|
|
|
fn synchronize<F, R>(f: F) -> R
|
|
where
|
|
F: FnOnce(&mut Execution) -> R,
|
|
{
|
|
execution(|execution| {
|
|
execution.threads.active_causality_inc();
|
|
trace!("synchronize");
|
|
f(execution)
|
|
})
|
|
}
|
|
|
|
/// Yield the thread.
|
|
///
|
|
/// This enables concurrent algorithms that require other threads to make
|
|
/// progress.
|
|
pub fn yield_now() {
|
|
let switch = execution(|execution| {
|
|
let thread = execution.threads.active_id();
|
|
|
|
execution.threads.active_mut().set_yield();
|
|
execution.threads.active_mut().operation = None;
|
|
let switch = execution.schedule();
|
|
|
|
trace!(?thread, ?switch, "yield_now");
|
|
|
|
switch
|
|
});
|
|
|
|
if switch {
|
|
Scheduler::switch();
|
|
}
|
|
}
|
|
|
|
pub(crate) fn execution<F, R>(f: F) -> R
|
|
where
|
|
F: FnOnce(&mut Execution) -> R,
|
|
{
|
|
Scheduler::with_execution(f)
|
|
}
|
|
|
|
pub fn thread_done() {
|
|
let locals = execution(|execution| {
|
|
let thread = execution.threads.active_id();
|
|
|
|
trace!(?thread, "thread_done: drop locals");
|
|
|
|
execution.threads.active_mut().drop_locals()
|
|
});
|
|
|
|
// Drop outside of the execution context
|
|
drop(locals);
|
|
|
|
execution(|execution| {
|
|
let thread = execution.threads.active_id();
|
|
|
|
execution.threads.active_mut().operation = None;
|
|
execution.threads.active_mut().set_terminated();
|
|
let switch = execution.schedule();
|
|
|
|
trace!(?thread, ?switch, "thread_done: terminate");
|
|
|
|
switch
|
|
});
|
|
}
|