You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
yuurei/src/state/local/work.rs

117 lines
3.1 KiB

//! Worker that mutates `Kokoro`.
use super::*;
use tokio::{
sync::{
watch,
oneshot,
},
};
/// Handles working on `Karada` instances.
#[derive(Debug)]
pub struct Kokoro
{
scape: Arc<RwLock<MessageSpan>>,
deltas: Arc<RwLock<Vec<Delta>>>,
update_body: watch::Sender<String>,
body_recv: Option<watch::Receiver<String>>,
}
impl Kokoro
{
/// Create a new instance. This instance can spawn a `Karada` instance.
pub fn new() -> Self
{
let (tx, rx) = watch::channel(String::new());
Self {
scape: Arc::new(RwLock::new(MessageSpan::new())),
deltas: Arc::new(RwLock::new(Vec::new())),
update_body: tx,
body_recv: Some(rx),
}
}
/// Create a new worker instance from a suspension of `Karada`.
pub fn from_suspended(susp: Suspension) -> Self
{
let span = susp.scape.chars().collect();
let (tx, rx) = watch::channel(susp.scape);
Self {
scape: Arc::new(RwLock::new(span)),
deltas: Arc::new(RwLock::new(susp.deltas)),
update_body: tx,
body_recv: Some(rx),
}
}
/// Consume this instance into a suspension
///
/// This will only acquire locks if needed, but since they might be needed, it must be awaited in case of `Kokoro` instances potentially owning the data.
pub async fn into_suspended(self) -> Suspension
{
let scape: String = {
let scape = self.scape;
match Arc::try_unwrap(scape) { //try to unwrap if possible, to avoid acquiring useless lock
Ok(scape) => scape.into_inner().into_iter().collect(),
Err(scape) => scape.read().await.iter().collect(),
}
};
let deltas: Vec<Delta> = {
let deltas = self.deltas;
match Arc::try_unwrap(deltas) {
Ok(deltas) => deltas.into_inner(),
Err(deltas) => deltas.read().await.clone(),
}
};
Suspension{scape,deltas}
}
/// Spawn one `Karada` instance. If this has already been called, it returns `None`.
pub fn spawn(&mut self) -> Option<Karada>
{
self.body_recv.take()
.map(|current_body| {
Karada {
scape: Arc::clone(&self.scape),
deltas: Arc::clone(&self.deltas),
current_body,
}
})
}
/// Spawn a clone `Karada` into a new instance. This function can be called many times to yield `Karada` instances that are all identical and controlled by this instance.
///
/// # Panics
/// If `spawn` was previously called.
pub fn spawn_clone(&self) -> Karada
{
Karada {
scape: Arc::clone(&self.scape),
deltas: Arc::clone(&self.deltas),
current_body: self.body_recv.as_ref().unwrap().clone(),
}
}
/// Apply a delta to this instance.
pub async fn apply<I: IntoIterator<Item= Delta>>(&mut self, delta: I) -> Result<(), error::Error>
{
let (mut scape, mut deltas) = tokio::join!{
self.scape.write(),
self.deltas.write(),
};
for delta in delta.into_iter() {
// Only start mutating now that both locks are writable. Is this needed, or can we do the mutation concurrently?
delta.insert(scape.as_mut());
deltas.push(delta);
}
self.update_body.broadcast(scape.iter().collect()).map_err(|_| error::Error::BroadcastUpdate)
}
}