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.
117 lines
3.1 KiB
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)
|
|
}
|
|
}
|