use std::collections::hash_map::Entry as MapEntry;
use std::collections::HashMap;
use std::sync::{self, Arc};
use crate::binder::{FrozenState, FrozenStateChangeCallback};
use crate::error::StatusCode;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub(crate) enum FreezeCmd {
Request,
Clear,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub(crate) enum ClearDone {
Removed,
Rerequest,
Unexpected,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
enum Kernel {
Registered { last: Option<bool> },
PendingClear { rerequest: bool },
}
struct Entry<P> {
owner: usize,
proxy: P,
callback: sync::Weak<dyn FrozenStateChangeCallback>,
delivered: Option<bool>,
}
struct Slot<P> {
id: u64,
kernel: Kernel,
entries: Vec<Entry<P>>,
delivering: bool,
}
impl<P> Slot<P> {
fn after_removal(&mut self) -> Option<FreezeCmd> {
if !self.entries.is_empty() {
return None;
}
match &mut self.kernel {
Kernel::Registered { .. } => {
self.kernel = Kernel::PendingClear { rerequest: false };
Some(FreezeCmd::Clear)
}
Kernel::PendingClear { rerequest } => {
*rerequest = false;
None
}
}
}
}
pub(crate) type Batch = (u64, FrozenState, Vec<Arc<dyn FrozenStateChangeCallback>>);
pub(crate) struct FreezeRegistry<P> {
slots: HashMap<u32, Slot<P>>,
next_id: u64,
}
impl<P> Default for FreezeRegistry<P> {
fn default() -> Self {
Self {
slots: HashMap::new(),
next_id: 0,
}
}
}
impl<P: Clone> FreezeRegistry<P> {
pub(crate) fn add(
&mut self,
handle: u32,
owner: usize,
proxy: P,
callback: sync::Weak<dyn FrozenStateChangeCallback>,
) -> Option<FreezeCmd> {
let entry = Entry {
owner,
proxy,
callback,
delivered: None,
};
match self.slots.entry(handle) {
MapEntry::Vacant(vacant) => {
self.next_id += 1;
vacant.insert(Slot {
id: self.next_id,
kernel: Kernel::Registered { last: None },
entries: vec![entry],
delivering: false,
});
Some(FreezeCmd::Request)
}
MapEntry::Occupied(occupied) => {
let slot = occupied.into_mut();
if let Kernel::PendingClear { rerequest } = &mut slot.kernel {
*rerequest = true;
}
slot.entries.push(entry);
None
}
}
}
pub(crate) fn abort_request(&mut self, handle: u32) {
self.slots.remove(&handle);
}
pub(crate) fn remove(
&mut self,
handle: u32,
owner: usize,
callback: &sync::Weak<dyn FrozenStateChangeCallback>,
) -> Result<Option<FreezeCmd>, StatusCode> {
let slot = self
.slots
.get_mut(&handle)
.ok_or(StatusCode::NameNotFound)?;
let index = slot
.entries
.iter()
.position(|e| e.owner == owner && sync::Weak::ptr_eq(&e.callback, callback))
.ok_or(StatusCode::NameNotFound)?;
slot.entries.remove(index);
Ok(slot.after_removal())
}
pub(crate) fn drop_owner(&mut self, handle: u32, owner: usize) -> Option<FreezeCmd> {
let slot = self.slots.get_mut(&handle)?;
slot.entries.retain(|e| e.owner != owner);
slot.after_removal()
}
pub(crate) fn state_changed(&mut self, handle: u32, is_frozen: bool) -> bool {
match self.slots.get_mut(&handle).map(|slot| &mut slot.kernel) {
Some(Kernel::Registered { last }) if *last != Some(is_frozen) => {
*last = Some(is_frozen);
true
}
_ => false,
}
}
pub(crate) fn clear_done(&mut self, handle: u32) -> ClearDone {
let Some(slot) = self.slots.get_mut(&handle) else {
return ClearDone::Unexpected;
};
match slot.kernel {
Kernel::Registered { .. } => ClearDone::Unexpected,
Kernel::PendingClear { rerequest } if rerequest && !slot.entries.is_empty() => {
slot.kernel = Kernel::Registered { last: None };
slot.entries.iter_mut().for_each(|e| e.delivered = None);
ClearDone::Rerequest
}
Kernel::PendingClear { .. } => {
self.slots.remove(&handle);
ClearDone::Removed
}
}
}
pub(crate) fn pending_owners(&self, handle: u32) -> Vec<P> {
let Some(slot) = self.slots.get(&handle).filter(|slot| !slot.delivering) else {
return Vec::new();
};
let Kernel::Registered { last: Some(last) } = slot.kernel else {
return Vec::new();
};
let mut owners: Vec<(usize, P)> = Vec::new();
for e in slot.entries.iter().filter(|e| e.delivered != Some(last)) {
if !owners.iter().any(|(owner, _)| *owner == e.owner) {
owners.push((e.owner, e.proxy.clone()));
}
}
owners.into_iter().map(|(_, proxy)| proxy).collect()
}
pub(crate) fn begin_batch(&mut self, handle: u32, owner: usize) -> Option<Batch> {
let slot = self.slots.get_mut(&handle)?;
if slot.delivering {
return None;
}
let Kernel::Registered {
last: Some(is_frozen),
} = slot.kernel
else {
return None;
};
slot.entries.retain(|e| e.callback.strong_count() > 0);
let callbacks: Vec<_> = slot
.entries
.iter_mut()
.filter(|e| e.owner == owner && e.delivered != Some(is_frozen))
.filter_map(|e| {
let callback = e.callback.upgrade()?;
e.delivered = Some(is_frozen);
Some(callback)
})
.collect();
if callbacks.is_empty() {
return None;
}
slot.delivering = true;
Some((slot.id, FrozenState::from(is_frozen), callbacks))
}
pub(crate) fn end_batch(&mut self, handle: u32, token: u64) {
if let Some(slot) = self.slots.get_mut(&handle).filter(|slot| slot.id == token) {
slot.delivering = false;
}
}
#[cfg(test)]
fn kernel(&self, handle: u32) -> Option<Kernel> {
self.slots.get(&handle).map(|slot| slot.kernel)
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::binder::WIBinder;
struct Noop;
impl FrozenStateChangeCallback for Noop {
fn on_state_changed(&self, _: &WIBinder, _: FrozenState) {}
}
fn callback() -> Arc<dyn FrozenStateChangeCallback> {
Arc::new(Noop)
}
const H: u32 = 7;
const A: usize = 0x1000;
const B: usize = 0x2000;
#[test]
fn first_add_requests_and_later_adds_do_not() {
let mut reg = FreezeRegistry::default();
let (c1, c2) = (callback(), callback());
assert_eq!(
reg.add(H, A, A, Arc::downgrade(&c1)),
Some(FreezeCmd::Request)
);
assert_eq!(reg.add(H, A, A, Arc::downgrade(&c2)), None);
assert_eq!(reg.add(H, B, B, Arc::downgrade(&c1)), None);
assert_eq!(reg.kernel(H), Some(Kernel::Registered { last: None }));
}
#[test]
fn last_removal_clears_once() {
let mut reg = FreezeRegistry::default();
let (c1, c2) = (callback(), callback());
reg.add(H, A, A, Arc::downgrade(&c1));
reg.add(H, A, A, Arc::downgrade(&c2));
assert_eq!(reg.remove(H, A, &Arc::downgrade(&c1)), Ok(None));
assert_eq!(
reg.remove(H, A, &Arc::downgrade(&c2)),
Ok(Some(FreezeCmd::Clear))
);
assert_eq!(
reg.kernel(H),
Some(Kernel::PendingClear { rerequest: false })
);
assert_eq!(
reg.remove(H, A, &Arc::downgrade(&c2)),
Err(StatusCode::NameNotFound)
);
assert_eq!(reg.drop_owner(H, A), None);
}
#[test]
fn remove_matches_owner_and_first_entry_only() {
let mut reg = FreezeRegistry::default();
let c = callback();
reg.add(H, A, A, Arc::downgrade(&c));
reg.add(H, A, A, Arc::downgrade(&c));
assert_eq!(
reg.remove(H, B, &Arc::downgrade(&c)),
Err(StatusCode::NameNotFound)
);
assert_eq!(reg.remove(H, A, &Arc::downgrade(&c)), Ok(None));
assert_eq!(
reg.remove(H, A, &Arc::downgrade(&c)),
Ok(Some(FreezeCmd::Clear))
);
assert_eq!(
reg.remove(99, A, &Arc::downgrade(&c)),
Err(StatusCode::NameNotFound)
);
}
#[test]
fn dropping_old_proxy_keeps_new_proxys_registration() {
let mut reg = FreezeRegistry::default();
let (old, new) = (callback(), callback());
reg.add(H, A, A, Arc::downgrade(&old));
reg.add(H, B, B, Arc::downgrade(&new));
assert_eq!(reg.drop_owner(H, A), None);
assert_eq!(reg.drop_owner(H, B), Some(FreezeCmd::Clear));
}
#[test]
fn drop_owner_clears_a_registration_emptied_by_pruning() {
let mut reg = FreezeRegistry::default();
let c = callback();
reg.add(H, A, A, Arc::downgrade(&c));
drop(c);
assert!(reg.state_changed(H, true));
assert!(reg.begin_batch(H, A).is_none());
assert_eq!(reg.drop_owner(H, A), Some(FreezeCmd::Clear));
}
#[test]
fn add_during_clear_defers_the_request() {
let mut reg = FreezeRegistry::default();
let c = callback();
reg.add(H, A, A, Arc::downgrade(&c));
assert_eq!(reg.drop_owner(H, A), Some(FreezeCmd::Clear));
assert_eq!(reg.add(H, B, B, Arc::downgrade(&c)), None);
assert_eq!(reg.clear_done(H), ClearDone::Rerequest);
assert_eq!(reg.kernel(H), Some(Kernel::Registered { last: None }));
}
#[test]
fn add_then_remove_during_clear_does_not_rerequest() {
let mut reg = FreezeRegistry::default();
let c = callback();
reg.add(H, A, A, Arc::downgrade(&c));
reg.drop_owner(H, A);
reg.add(H, B, B, Arc::downgrade(&c));
assert_eq!(reg.remove(H, B, &Arc::downgrade(&c)), Ok(None));
assert_eq!(reg.clear_done(H), ClearDone::Removed);
assert_eq!(reg.kernel(H), None);
assert_eq!(
reg.add(H, B, B, Arc::downgrade(&c)),
Some(FreezeCmd::Request)
);
}
#[test]
fn unexpected_clear_done_changes_nothing() {
let mut reg = FreezeRegistry::default();
assert_eq!(reg.clear_done(H), ClearDone::Unexpected);
let c = callback();
reg.add(H, A, A, Arc::downgrade(&c));
assert_eq!(reg.clear_done(H), ClearDone::Unexpected);
assert_eq!(reg.kernel(H), Some(Kernel::Registered { last: None }));
}
#[test]
fn abort_request_forgets_the_slot() {
let mut reg = FreezeRegistry::default();
let c = callback();
assert_eq!(
reg.add(H, A, A, Arc::downgrade(&c)),
Some(FreezeCmd::Request)
);
reg.abort_request(H);
assert_eq!(reg.kernel(H), None);
assert_eq!(
reg.add(H, A, A, Arc::downgrade(&c)),
Some(FreezeCmd::Request)
);
}
#[test]
fn repeated_and_pending_clear_states_are_filtered() {
let mut reg = FreezeRegistry::default();
let c = callback();
assert!(!reg.state_changed(H, true), "no slot");
reg.add(H, A, A, Arc::downgrade(&c));
assert!(reg.state_changed(H, false));
assert!(
!reg.state_changed(H, false),
"C driver resend of the same state"
);
assert!(reg.state_changed(H, true));
reg.drop_owner(H, A);
assert!(!reg.state_changed(H, false), "clear in flight");
}
#[test]
fn batches_deliver_each_state_once_to_the_live_owner() {
let mut reg = FreezeRegistry::default();
let (mine, theirs) = (callback(), callback());
reg.add(H, A, A, Arc::downgrade(&mine));
reg.add(H, B, B, Arc::downgrade(&theirs));
assert!(reg.begin_batch(H, A).is_none(), "no state yet");
reg.state_changed(H, true);
let (token, state, callbacks) = reg.begin_batch(H, A).expect("batch");
assert_eq!(state, FrozenState::Frozen);
assert_eq!(callbacks.len(), 1);
assert!(Arc::ptr_eq(&callbacks[0], &mine));
reg.end_batch(H, token);
assert!(reg.begin_batch(H, A).is_none(), "already delivered");
let late = callback();
reg.add(H, A, A, Arc::downgrade(&late));
let (token, state, callbacks) = reg.begin_batch(H, A).expect("late batch");
assert_eq!(state, FrozenState::Frozen);
assert!(Arc::ptr_eq(&callbacks[0], &late));
reg.end_batch(H, token);
}
#[test]
fn pending_owners_lists_each_owner_with_an_undelivered_state() {
let mut reg = FreezeRegistry::default();
let (a1, a2, b) = (callback(), callback(), callback());
reg.add(H, A, A, Arc::downgrade(&a1));
reg.add(H, A, A, Arc::downgrade(&a2));
reg.add(H, B, B, Arc::downgrade(&b));
assert!(reg.pending_owners(H).is_empty(), "no state yet");
reg.state_changed(H, true);
assert_eq!(reg.pending_owners(H), [A, B]);
let (token, _, _) = reg.begin_batch(H, A).expect("A's batch");
assert!(reg.pending_owners(H).is_empty(), "another thread delivers");
reg.end_batch(H, token);
assert_eq!(reg.pending_owners(H), [B]);
reg.drop_owner(H, B);
assert!(reg.pending_owners(H).is_empty());
}
#[test]
fn a_second_deliverer_backs_off_until_the_first_ends() {
let mut reg = FreezeRegistry::default();
let c = callback();
reg.add(H, A, A, Arc::downgrade(&c));
reg.state_changed(H, true);
let (token, _, _) = reg.begin_batch(H, A).expect("first");
reg.state_changed(H, false);
assert!(reg.begin_batch(H, A).is_none(), "in progress");
reg.end_batch(H, token);
let (_, state, _) = reg.begin_batch(H, A).expect("re-check after the batch");
assert_eq!(state, FrozenState::Unfrozen);
}
#[test]
fn a_stale_batch_cannot_end_a_newer_slots_batch() {
let mut reg = FreezeRegistry::default();
let c = callback();
reg.add(H, A, A, Arc::downgrade(&c));
reg.state_changed(H, true);
let (stale, _, _) = reg.begin_batch(H, A).expect("old slot's batch");
assert_eq!(reg.drop_owner(H, A), Some(FreezeCmd::Clear));
assert_eq!(reg.clear_done(H), ClearDone::Removed);
assert_eq!(
reg.add(H, B, B, Arc::downgrade(&c)),
Some(FreezeCmd::Request)
);
reg.state_changed(H, false);
let (fresh, _, _) = reg.begin_batch(H, B).expect("new slot's batch");
assert_ne!(stale, fresh);
reg.end_batch(H, stale);
reg.state_changed(H, true);
assert!(
reg.begin_batch(H, B).is_none(),
"the new slot's batch is still in progress"
);
reg.end_batch(H, fresh);
assert!(reg.begin_batch(H, B).is_some());
}
#[test]
fn rerequest_redelivers_the_new_initial_state() {
let mut reg = FreezeRegistry::default();
let c = callback();
reg.add(H, A, A, Arc::downgrade(&c));
reg.state_changed(H, true);
let (token, _, _) = reg.begin_batch(H, A).expect("batch");
reg.end_batch(H, token);
reg.drop_owner(H, A);
reg.add(H, B, B, Arc::downgrade(&c));
assert_eq!(reg.clear_done(H), ClearDone::Rerequest);
assert!(reg.state_changed(H, true));
let (_, state, _) = reg.begin_batch(H, B).expect("re-delivered");
assert_eq!(state, FrozenState::Frozen);
}
}