2 releases
0.1.1 | Jan 23, 2022 |
---|---|
0.1.0 | Oct 31, 2021 |
#412 in Concurrency
10,286 downloads per month
Used in 54 crates
(6 directly)
23KB
215 lines
takecell
takecell
provides two new cell-like types, TakeCell
and TakeOwnCell
.
Both may store arbitrary non-Copy
types, can be read from at most once and
provide direct unique access to the stored contents. The core API looks
roughly like this:
impl<T> TakeCell<T> {
const fn new(v: T) -> Self { ... }
}
impl<T: ?Sized> TakeCell<T> {
fn take(&self) -> Option<&mut T> { ... }
}
impl<T> TakeOwnCell<T> {
const fn new(v: T) -> Self { ... }
fn take(&self) -> Option<T> { ... }
}
To use this crate add the following to your Cargo.toml
:
[dependencies]
takecell = "0.1"
lib.rs
:
This crate provides two new cell-like types, TakeCell
and TakeOwnCell
.
Both may store arbitrary non-Copy
types, can be read from at most once and
provide direct unique access to the stored contents. The core API looks
roughly like this (and there’s much more inside, read on!):
impl<T> TakeCell<T> {
const fn new(v: T) -> Self { ... }
}
impl<T: ?Sized> TakeCell<T> {
fn take(&self) -> Option<&mut T> { ... }
}
impl<T> TakeOwnCell<T> {
const fn new(v: T) -> Self { ... }
fn take(&self) -> Option<T> { ... }
}
Note that, like with RefCell
and Mutex
, the take
method requires only
a shared reference. Because of the single read restriction take
can
return a &mut T
or T
instead of RefMut<T>
or MutexGuard<T>
. In some
sense TakeCell
can be thought as a Mutex
without unlocking (or rather
with unlocking requiring unique access to the Mutex
, see heal
).
This crate is #![no_std]
and only requires little sychronization via 8-bit
atomic.
Usage examples
Singletons
TakeCell
is Sync
(when T: Sync
) and as such it may be used in
static
s. This can be used to create singletons:
use takecell::TakeCell;
#[non_exhaustive]
pub struct Peripherals {
pub something: Something,
}
pub static PEREPHERALS: TakeCell<Peripherals> = TakeCell::new(Peripherals {
something: Something,
});
let peripherals: &'static mut _ = PEREPHERALS.take().unwrap();
Doing work only once
use once_cell::sync::OnceCell;
use std::sync::{Arc, Condvar, Mutex};
use takecell::TakeCell;
#[derive(Clone)]
struct Job {
// Input can be a type which requires unique access to be used (e.g.: `dyn Read`)
input: Arc<TakeCell<Input>>,
output: Arc<OnceCell<Output>>,
wait: Arc<(Mutex<bool>, Condvar)>,
}
fn execute(job: Job) -> Output {
match job.input.take() {
Some(input) => {
// Nobody has started executing the job yet, so execute it
let output = input.process();
// Write the output
job.output.set(output);
// Notify other threads that the job is done
let (lock, cvar) = &*job.wait;
let mut done = lock.lock().unwrap();
*done = true;
}
None => {
// Wait for the other thread to do the job
let (lock, cvar) = &*job.wait;
let mut done = lock.lock().unwrap();
// As long as the value inside the `Mutex<bool>` is `false`, we wait
while !*done {
done = cvar.wait(done).unwrap();
}
}
}
// Read the output
job.output.get().unwrap().clone()
}
impl Input {
fn process(&mut self) -> Output {
// ...
}
}