Fix review findings across compositor and shell
This commit is contained in:
1 parent
5d9673aad3
commit
f7e5efb245
30 files changed
+1222
-181
No files matched your search
@@ -1,19 +1,22 @@
|
||||
//! Bounded serialization and delivery outside the compositor event loop.
|
||||
use ferese_ipc::{Response, activities::Snapshot};
|
||||
use std::sync::{
|
||||
Arc,
|
||||
Arc, RwLock,
|
||||
mpsc::{self, SyncSender},
|
||||
};
|
||||
|
||||
type Recipient = (u64, SyncSender<Response>);
|
||||
struct Job {
|
||||
epoch: u64,
|
||||
snapshot: Arc<Snapshot>,
|
||||
recipients: Vec<Recipient>,
|
||||
}
|
||||
pub(super) struct Publisher(SyncSender<Job>);
|
||||
pub(super) struct Publisher(SyncSender<Job>, Arc<RwLock<u64>>);
|
||||
impl Publisher {
|
||||
pub fn new() -> std::io::Result<Self> {
|
||||
let (send, receive) = mpsc::sync_channel::<Job>(8);
|
||||
let epoch = Arc::new(RwLock::new(0));
|
||||
let worker_epoch = epoch.clone();
|
||||
std::thread::Builder::new()
|
||||
.name("ferese-activity-snapshots".into())
|
||||
.spawn(move || {
|
||||
@@ -26,23 +29,47 @@ impl Publisher {
|
||||
cached = Some((job.snapshot.clone(), serde_json::json!(job.snapshot.as_ref())));
|
||||
}
|
||||
let value = &cached.as_ref().unwrap().1;
|
||||
for (id, sender) in job.recipients {
|
||||
// Client queues are bounded too. A slow/disconnected client
|
||||
// cannot block serialization or another subscriber.
|
||||
let _ = sender.try_send(Response::success(id, value.clone()));
|
||||
}
|
||||
// Lock acquisition revokes queued/in-flight serialization.
|
||||
// Hold the guard through delivery to order sends before revocation.
|
||||
deliver(job.epoch, &worker_epoch, job.recipients, value);
|
||||
}
|
||||
})?;
|
||||
Ok(Self(send))
|
||||
Ok(Self(send, epoch))
|
||||
}
|
||||
|
||||
pub fn revoke(&self) {
|
||||
let mut epoch = self.1.write().expect("activity publication epoch");
|
||||
*epoch = epoch.wrapping_add(1);
|
||||
}
|
||||
|
||||
pub fn send(&self, snapshot: Arc<Snapshot>, recipients: Vec<Recipient>) {
|
||||
if let Err(error) = self.0.try_send(Job { snapshot, recipients }) {
|
||||
let epoch = *self.1.read().expect("activity publication epoch");
|
||||
if let Err(error) = self.0.try_send(Job {
|
||||
epoch,
|
||||
snapshot,
|
||||
recipients,
|
||||
}) {
|
||||
let (mpsc::TrySendError::Full(job) | mpsc::TrySendError::Disconnected(job)) = error;
|
||||
unavailable(job.recipients);
|
||||
}
|
||||
}
|
||||
}
|
||||
fn deliver(epoch: u64, current: &RwLock<u64>, recipients: Vec<Recipient>, value: &serde_json::Value) {
|
||||
let current = current.read().expect("activity publication epoch");
|
||||
if epoch != *current {
|
||||
locked(recipients);
|
||||
return;
|
||||
}
|
||||
for (id, sender) in recipients {
|
||||
// A slow/disconnected client cannot block another subscriber.
|
||||
let _ = sender.try_send(Response::success(id, value.clone()));
|
||||
}
|
||||
}
|
||||
pub(super) fn locked(recipients: Vec<Recipient>) {
|
||||
for (id, sender) in recipients {
|
||||
let _ = sender.try_send(Response::error(id, "session_locked", "Session is locked"));
|
||||
}
|
||||
}
|
||||
pub(super) fn unavailable(recipients: Vec<Recipient>) {
|
||||
for (id, sender) in recipients {
|
||||
let _ = sender.try_send(Response::error(
|
||||
@@ -58,10 +85,29 @@ mod tests {
|
||||
use super::*;
|
||||
use std::time::Duration;
|
||||
|
||||
#[test]
|
||||
fn lock_revokes_serialized_jobs_even_after_unlock() {
|
||||
let (send, _receive) = mpsc::sync_channel(1);
|
||||
let publisher = Publisher(send, Arc::new(RwLock::new(0)));
|
||||
let job_epoch = *publisher.1.read().unwrap();
|
||||
publisher.revoke();
|
||||
let (reply, receive) = mpsc::sync_channel(1);
|
||||
deliver(
|
||||
job_epoch,
|
||||
&publisher.1,
|
||||
vec![(9, reply)],
|
||||
&serde_json::json!({"title": "private"}),
|
||||
);
|
||||
let response = receive.recv().unwrap();
|
||||
assert_eq!(response.id, 9);
|
||||
assert!(response.result.is_none());
|
||||
assert_eq!(response.error.unwrap().code, "session_locked");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn saturated_queue_rejects_without_blocking_and_slow_clients_do_not_block_others() {
|
||||
let (send, _receive) = mpsc::sync_channel(0);
|
||||
let publisher = Publisher(send);
|
||||
let publisher = Publisher(send, Arc::new(RwLock::new(0)));
|
||||
let (reply, receive) = mpsc::sync_channel(1);
|
||||
publisher.send(Arc::new(Snapshot::default()), vec![(7, reply)]);
|
||||
let response = receive.recv_timeout(Duration::from_secs(1)).unwrap();
|
||||
|
||||
Reference in new issue
Block a user