use std::cmp::Reverse;
use std::collections::{BinaryHeap, HashMap};
use std::os::fd::OwnedFd;
use std::sync::{self, Arc};
use crate::binder::{IBinder, SIBinder};
use super::address::{AddressSpace, RpcAddress};
use super::wire::WireTransaction;
struct AsyncTodo {
async_number: u64,
transaction: WireTransaction,
in_fds: Vec<OwnedFd>,
}
impl PartialEq for AsyncTodo {
fn eq(&self, other: &Self) -> bool {
self.async_number == other.async_number
}
}
impl Eq for AsyncTodo {}
impl PartialOrd for AsyncTodo {
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
Some(self.cmp(other))
}
}
impl Ord for AsyncTodo {
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
self.async_number.cmp(&other.async_number)
}
}
#[derive(Debug, Clone, Copy)]
pub enum DropReason {
UnknownAddress,
StaleAsyncNumber,
}
const ASYNC_TODO_TERMINATE_LEVEL: usize = 10000;
const ASYNC_TODO_WARN_PER: usize = 1000;
#[derive(Debug)]
pub enum AsyncDecision {
Dispatch(WireTransaction, Vec<OwnedFd>),
Enqueued,
Drop(DropReason),
Terminate(usize),
}
struct LocalNode {
binder: SIBinder,
ptr: usize,
strong: i64,
next_async_number: u64,
async_todo: BinaryHeap<Reverse<AsyncTodo>>,
}
pub struct RpcState {
local_nodes: HashMap<RpcAddress, LocalNode>,
local_by_ptr: HashMap<usize, RpcAddress>,
remote_proxies: HashMap<RpcAddress, sync::Weak<dyn IBinder>>,
remote_send_async_counters: HashMap<RpcAddress, u64>,
addr_counter: u64,
space: AddressSpace,
}
fn binder_ptr(b: &SIBinder) -> usize {
Arc::as_ptr(b.as_arc()) as *const () as usize
}
impl RpcState {
pub fn new(space: AddressSpace) -> Self {
RpcState {
local_nodes: HashMap::new(),
local_by_ptr: HashMap::new(),
remote_proxies: HashMap::new(),
remote_send_async_counters: HashMap::new(),
addr_counter: 0,
space,
}
}
pub fn on_binder_leaving(&mut self, binder: &SIBinder) -> crate::Result<RpcAddress> {
let ptr = binder_ptr(binder);
if let Some(&addr) = self.local_by_ptr.get(&ptr) {
if let Some(node) = self.local_nodes.get_mut(&addr) {
node.strong += 1;
}
return Ok(addr);
}
if self.addr_counter >= u32::MAX as u64 {
log::error!(
"RPC: local address counter exhausted (>= u32::MAX) on one session; \
refusing to mint an aliasing address"
);
return Err(crate::StatusCode::FailedTransaction);
}
let addr = RpcAddress::unique(&mut self.addr_counter, self.space);
self.local_nodes.insert(
addr,
LocalNode {
binder: binder.clone(),
ptr,
strong: 1,
next_async_number: 0,
async_todo: BinaryHeap::new(),
},
);
self.local_by_ptr.insert(ptr, addr);
Ok(addr)
}
pub fn lookup_local(&self, addr: &RpcAddress) -> Option<SIBinder> {
self.local_nodes.get(addr).map(|n| n.binder.clone())
}
#[must_use = "drop the returned SIBinder outside the RpcState lock"]
pub fn cancel_binder_leaving(&mut self, addr: &RpcAddress) -> Option<SIBinder> {
if let Some(node) = self.local_nodes.get_mut(addr) {
node.strong -= 1;
if node.strong <= 0 {
return self.remove_local(addr);
}
}
None
}
fn remove_local(&mut self, addr: &RpcAddress) -> Option<SIBinder> {
let node = self.local_nodes.remove(addr)?;
self.local_by_ptr.remove(&node.ptr);
Some(node.binder)
}
#[must_use = "drop the returned SIBinder outside the RpcState lock"]
pub fn dec_strong_local(&mut self, addr: &RpcAddress, amount: u32) -> Option<SIBinder> {
if let Some(node) = self.local_nodes.get_mut(addr) {
node.strong -= amount as i64;
if node.strong <= 0 {
return self.remove_local(addr);
}
}
None
}
pub fn clear_local(&mut self) -> Vec<SIBinder> {
self.local_by_ptr.clear();
self.local_nodes.drain().map(|(_, n)| n.binder).collect()
}
pub fn remote_proxy<F>(&mut self, addr: RpcAddress, make: F) -> crate::Result<(SIBinder, bool)>
where
F: FnOnce() -> SIBinder,
{
if let Some(weak) = self.remote_proxies.get(&addr) {
if let Some(arc) = weak.upgrade() {
return Ok((SIBinder::from_arc(arc), true));
}
}
if addr.space_tag() == self.space.tag() {
log::error!("RPC: peer sent an unknown address from our own subspace: {addr:?}");
return Err(crate::StatusCode::BadValue);
}
let sib = make();
self.remote_proxies
.insert(addr, Arc::downgrade(sib.as_arc()));
Ok((sib, false))
}
pub fn forget_remote_if(&mut self, addr: &RpcAddress, who: *const ()) {
if let Some(weak) = self.remote_proxies.get(addr) {
if weak.as_ptr() as *const () == who {
self.remote_proxies.remove(addr);
self.remote_send_async_counters.remove(addr);
}
}
}
pub fn local_node_count(&self) -> usize {
self.local_nodes.len()
}
pub(crate) fn remote_proxy_snapshot(&self) -> Vec<sync::Arc<dyn IBinder>> {
self.remote_proxies
.values()
.filter_map(sync::Weak::upgrade)
.collect()
}
pub fn next_send_async_number(&mut self, addr: RpcAddress) -> u64 {
let counter = self.remote_send_async_counters.entry(addr).or_insert(0);
let n = *counter;
*counter = n.wrapping_add(1);
if *counter == 0 {
warn_async_wrap(&addr);
}
n
}
pub fn cancel_send_async_number(&mut self, addr: RpcAddress, consumed: u64) {
if let Some(counter) = self.remote_send_async_counters.get_mut(&addr) {
if *counter == consumed.wrapping_add(1) {
*counter = consumed;
}
}
}
pub fn dispatch_async_or_enqueue(
&mut self,
addr: RpcAddress,
wire_async: u64,
txn: WireTransaction,
in_fds: Vec<OwnedFd>,
) -> AsyncDecision {
let Some(node) = self.local_nodes.get_mut(&addr) else {
return AsyncDecision::Drop(DropReason::UnknownAddress);
};
if wire_async == node.next_async_number {
AsyncDecision::Dispatch(txn, in_fds)
} else if wire_async > node.next_async_number {
node.async_todo.push(Reverse(AsyncTodo {
async_number: wire_async,
transaction: txn,
in_fds,
}));
let num_pending = node.async_todo.len();
if num_pending >= ASYNC_TODO_TERMINATE_LEVEL {
node.async_todo.clear();
return AsyncDecision::Terminate(num_pending);
}
if num_pending % ASYNC_TODO_WARN_PER == 0 {
log::warn!(
"RPC: {num_pending} pending out-of-order oneway transactions on {addr:?}"
);
}
AsyncDecision::Enqueued
} else {
AsyncDecision::Drop(DropReason::StaleAsyncNumber)
}
}
pub fn advance_and_pop_async(
&mut self,
addr: RpcAddress,
) -> Option<(WireTransaction, Vec<OwnedFd>)> {
let node = self.local_nodes.get_mut(&addr)?;
node.next_async_number = node.next_async_number.wrapping_add(1);
if node.next_async_number == 0 {
warn_async_wrap(&addr);
}
while let Some(Reverse(top)) = node.async_todo.peek() {
if top.async_number >= node.next_async_number {
break;
}
node.async_todo.pop();
}
if let Some(Reverse(top)) = node.async_todo.peek() {
if top.async_number == node.next_async_number {
let Reverse(todo) = node.async_todo.pop().expect("peek-pop");
return Some((todo.transaction, todo.in_fds));
}
}
None
}
#[cfg(test)]
pub(crate) fn async_todo_len(&self, addr: &RpcAddress) -> usize {
self.local_nodes
.get(addr)
.map(|n| n.async_todo.len())
.unwrap_or(0)
}
#[cfg(test)]
pub(crate) fn next_async_number(&self, addr: &RpcAddress) -> u64 {
self.local_nodes
.get(addr)
.map(|n| n.next_async_number)
.unwrap_or(0)
}
}
fn warn_async_wrap(addr: &RpcAddress) {
log::warn!(
"RPC: per-address async_number wrapped at u64::MAX for {addr:?} — \
AOSP-divergent (AOSP terminates the session)."
);
}
#[cfg(test)]
mod tests {
use super::*;
use crate::binder::*;
use std::mem::ManuallyDrop;
use std::sync::Arc;
struct Dummy;
impl IBinder for Dummy {
fn link_to_death(&self, _: sync::Weak<dyn DeathRecipient>) -> crate::Result<()> {
Err(crate::StatusCode::InvalidOperation)
}
fn unlink_to_death(&self, _: sync::Weak<dyn DeathRecipient>) -> crate::Result<()> {
Err(crate::StatusCode::InvalidOperation)
}
fn ping_binder(&self) -> crate::Result<()> {
Ok(())
}
fn as_any(&self) -> &dyn std::any::Any {
self
}
fn as_transactable(&self) -> Option<&dyn Transactable> {
None
}
fn descriptor(&self) -> &str {
"rsbinder.test.Dummy"
}
fn is_remote(&self) -> bool {
false
}
fn inc_strong(&self, _: &SIBinder) -> crate::Result<()> {
Ok(())
}
fn attempt_inc_strong(&self) -> bool {
true
}
fn dec_strong(&self, _: Option<ManuallyDrop<SIBinder>>) -> crate::Result<()> {
Ok(())
}
fn inc_weak(&self, _: &WIBinder) -> crate::Result<()> {
Ok(())
}
fn dec_weak(&self) -> crate::Result<()> {
Ok(())
}
}
#[test]
fn leaving_is_idempotent_by_identity() {
let mut st = RpcState::new(AddressSpace::Acceptor);
let b = SIBinder::new(Arc::new(Dummy)).unwrap();
let a1 = st.on_binder_leaving(&b).unwrap();
let a2 = st.on_binder_leaving(&b).unwrap();
assert_eq!(a1, a2, "same object → same address");
assert_eq!(st.local_node_count(), 1);
assert!(st.lookup_local(&a1).is_some());
}
#[test]
fn dec_strong_releases_node() {
let mut st = RpcState::new(AddressSpace::Acceptor);
let b = SIBinder::new(Arc::new(Dummy)).unwrap();
let a = st.on_binder_leaving(&b).unwrap();
assert_eq!(st.local_node_count(), 1);
assert!(
st.dec_strong_local(&a, 1).is_some(),
"node removed at strong 0"
);
assert_eq!(st.local_node_count(), 0, "no leak");
assert!(st.lookup_local(&a).is_none());
assert!(st.dec_strong_local(&a, 1).is_none());
}
#[test]
fn dec_strong_honors_batched_amount() {
let mut st = RpcState::new(AddressSpace::Acceptor);
let b = SIBinder::new(Arc::new(Dummy)).unwrap();
let a = st.on_binder_leaving(&b).unwrap(); st.on_binder_leaving(&b).unwrap(); st.on_binder_leaving(&b).unwrap(); assert_eq!(st.local_node_count(), 1);
assert!(
st.dec_strong_local(&a, 3).is_some(),
"one batched DEC of amount 3 frees a node sent 3×"
);
assert_eq!(st.local_node_count(), 0, "no leak on batched drop");
}
#[test]
fn cancel_binder_leaving_rolls_back_one_bump() {
let mut st = RpcState::new(AddressSpace::Acceptor);
let b = SIBinder::new(Arc::new(Dummy)).unwrap();
let a = st.on_binder_leaving(&b).unwrap(); let a2 = st.on_binder_leaving(&b).unwrap(); assert_eq!(a, a2);
assert_eq!(st.local_node_count(), 1);
let _ = st.cancel_binder_leaving(&a);
assert_eq!(
st.local_node_count(),
1,
"still referenced by the other send"
);
assert!(st.lookup_local(&a).is_some());
let _ = st.cancel_binder_leaving(&a);
assert_eq!(st.local_node_count(), 0, "last bump cancelled → node freed");
assert!(st.lookup_local(&a).is_none());
let _ = st.cancel_binder_leaving(&a);
}
#[test]
fn cancel_send_async_number_only_when_last_consumer() {
let mut st = RpcState::new(AddressSpace::Acceptor);
let b = SIBinder::new(Arc::new(Dummy)).unwrap();
let a = st.on_binder_leaving(&b).unwrap();
assert_eq!(st.next_send_async_number(a), 0);
let consumed = st.next_send_async_number(a); assert_eq!(consumed, 1);
st.cancel_send_async_number(a, consumed);
assert_eq!(st.next_send_async_number(a), 1, "rolled back");
let consumed2 = st.next_send_async_number(a); let _other = st.next_send_async_number(a); st.cancel_send_async_number(a, consumed2);
assert_eq!(
st.next_send_async_number(a),
4,
"no rollback when not the last consumer"
);
}
#[test]
fn address_counter_exhaustion_is_rejected_not_aliased() {
let mut st = RpcState::new(AddressSpace::Acceptor);
st.addr_counter = (u32::MAX as u64) - 1;
let b1 = SIBinder::new(Arc::new(Dummy)).unwrap();
assert!(st.on_binder_leaving(&b1).is_ok());
let b2 = SIBinder::new(Arc::new(Dummy)).unwrap();
assert!(st.on_binder_leaving(&b2).is_err());
assert!(st.on_binder_leaving(&b1).is_ok());
}
#[test]
fn two_states_are_isolated() {
let mut s1 = RpcState::new(AddressSpace::Acceptor);
let s2 = RpcState::new(AddressSpace::Acceptor); let b1 = SIBinder::new(Arc::new(Dummy)).unwrap();
let a1 = s1.on_binder_leaving(&b1).unwrap();
assert!(
s2.lookup_local(&a1).is_none(),
"independent tables: a fresh session knows no foreign address"
);
assert_eq!(s1.local_node_count(), 1);
assert_eq!(s2.local_node_count(), 0);
let _ = s1.dec_strong_local(&a1, 1);
assert_eq!(s1.local_node_count(), 0);
assert_eq!(s2.local_node_count(), 0);
}
#[test]
fn stale_drop_does_not_split_remote_dedup() {
let mut st = RpcState::new(AddressSpace::Acceptor);
let addr = RpcAddress::from_wire_bytes([7u8; 32]);
let sib1 = SIBinder::new(Arc::new(Dummy)).unwrap();
let p1 = Arc::as_ptr(sib1.as_arc()) as *const ();
let (got1, ex1) = st
.remote_proxy(addr, || sib1.clone())
.expect("remote_proxy");
assert!(!ex1, "first receipt mints a proxy — not an excess");
drop(got1);
drop(sib1);
let sib2 = SIBinder::new(Arc::new(Dummy)).unwrap();
let (got2, ex2) = st
.remote_proxy(addr, || sib2.clone())
.expect("remote_proxy");
assert!(!ex2, "dead-Weak ⇒ re-mint, not an excess receipt");
let p2 = Arc::as_ptr(got2.as_arc()) as *const ();
st.forget_remote_if(&addr, p1);
let (again, ex_again) = st
.remote_proxy(addr, || panic!("must dedup to P2, not re-make"))
.expect("remote_proxy");
assert!(
Arc::ptr_eq(again.as_arc(), got2.as_arc()),
"stale P1 Drop must not split the per-address dedup (AC-2.5/P5)"
);
assert!(
ex_again,
"reusing the live P2 is a duplicate receipt (excess)"
);
drop(again);
drop(got2);
drop(sib2);
st.forget_remote_if(&addr, p2);
let sib3 = SIBinder::new(Arc::new(Dummy)).unwrap();
let mut remade = false;
let (_p3, ex3) = st
.remote_proxy(addr, || {
remade = true;
sib3.clone()
})
.expect("remote_proxy");
assert!(
remade,
"after identity-checked forget, the address re-mints"
);
assert!(!ex3, "re-mint after forget is a fresh proxy — not excess");
}
#[test]
fn phase_c_forget_remote_if_gcs_send_counter() {
let mut st = RpcState::new(AddressSpace::Acceptor);
let addr = RpcAddress::from_wire_bytes([3u8; 32]);
let sib1 = SIBinder::new(Arc::new(Dummy)).unwrap();
let (got1, _) = st
.remote_proxy(addr, || sib1.clone())
.expect("remote_proxy");
let p1 = Arc::as_ptr(got1.as_arc()) as *const ();
assert_eq!(st.next_send_async_number(addr), 0);
assert_eq!(st.next_send_async_number(addr), 1);
drop(got1);
drop(sib1);
let sib2 = SIBinder::new(Arc::new(Dummy)).unwrap();
let (got2, _) = st
.remote_proxy(addr, || sib2.clone())
.expect("remote_proxy");
let p2 = Arc::as_ptr(got2.as_arc()) as *const ();
st.forget_remote_if(&addr, p1);
assert_eq!(
st.next_send_async_number(addr),
2,
"stale forget must not evict the live counter"
);
drop(got2);
drop(sib2);
st.forget_remote_if(&addr, p2);
assert_eq!(
st.next_send_async_number(addr),
0,
"post-forget counter restarts from 0 (peer's BinderNode \
reaches timesSent=0 in lockstep with the matching DEC)"
);
}
#[test]
fn f7_timessent_balance_no_leak() {
let mut srv = RpcState::new(AddressSpace::Acceptor);
let b = SIBinder::new(Arc::new(Dummy)).unwrap();
let a = srv.on_binder_leaving(&b).unwrap();
let a2 = srv.on_binder_leaving(&b).unwrap();
let a3 = srv.on_binder_leaving(&b).unwrap();
assert_eq!((a, a), (a2, a3), "identity ⇒ same address on re-send");
assert_eq!(srv.local_node_count(), 1, "one node, strong = timesSent");
let mut peer = RpcState::new(AddressSpace::Initiator);
let pb = SIBinder::new(Arc::new(Dummy)).unwrap();
let (_p, e1) = peer.remote_proxy(a, || pb.clone()).expect("remote_proxy");
let (_p2, e2) = peer.remote_proxy(a, || pb.clone()).expect("remote_proxy");
let (_p3, e3) = peer.remote_proxy(a, || pb.clone()).expect("remote_proxy");
assert_eq!(
(e1, e2, e3),
(false, true, true),
"1st mints; 2nd/3rd are excess receipts (owe a flush DEC)"
);
assert!(srv.dec_strong_local(&a, 1).is_none());
assert!(srv.dec_strong_local(&a, 1).is_none());
assert!(
srv.dec_strong_local(&a, 1).is_some(),
"3rd DEC frees the node"
);
assert_eq!(srv.local_node_count(), 0, "no leak (AC-2.5)");
let mut s = RpcState::new(AddressSpace::Acceptor);
let o = SIBinder::new(Arc::new(Dummy)).unwrap();
let x = s.on_binder_leaving(&o).unwrap(); let _ = s.on_binder_leaving(&o).unwrap(); assert!(
s.dec_strong_local(&x, 1).is_none(),
"conn #1 proxy drop must NOT free a node conn #2 still holds (F7)"
);
assert!(s.lookup_local(&x).is_some(), "sibling still reachable");
assert!(
s.dec_strong_local(&x, 1).is_some(),
"conn #2 proxy drop frees it"
);
assert_eq!(s.local_node_count(), 0, "no leak");
}
fn mk_txn(addr: RpcAddress, async_n: u64) -> WireTransaction {
WireTransaction {
address: addr,
code: 1,
flags: crate::binder::FLAG_ONEWAY,
async_number: async_n,
data: vec![],
object_positions: vec![],
}
}
#[test]
fn phase_c_send_async_number_is_per_address_monotonic() {
let mut st = RpcState::new(AddressSpace::Initiator);
let a = RpcAddress::from_wire_bytes([1u8; 32]);
let b = RpcAddress::from_wire_bytes([2u8; 32]);
assert_eq!(st.next_send_async_number(a), 0);
assert_eq!(st.next_send_async_number(a), 1);
assert_eq!(
st.next_send_async_number(b),
0,
"per-address — b starts at 0"
);
assert_eq!(st.next_send_async_number(a), 2);
assert_eq!(st.next_send_async_number(b), 1);
}
#[test]
fn phase_c_in_order_dispatches_and_advances_counter() {
let mut st = RpcState::new(AddressSpace::Acceptor);
let b = SIBinder::new(Arc::new(Dummy)).unwrap();
let a = st.on_binder_leaving(&b).unwrap();
assert_eq!(st.next_async_number(&a), 0);
for i in 0..5u64 {
let txn = mk_txn(a, i);
match st.dispatch_async_or_enqueue(a, i, txn, vec![]) {
AsyncDecision::Dispatch(t, _) => assert_eq!(t.async_number, i),
other => panic!("in-order async_number {i} must dispatch, got {other:?}"),
}
assert_eq!(st.async_todo_len(&a), 0, "in-order ⇒ never enqueued");
assert!(
st.advance_and_pop_async(a).is_none(),
"queue empty ⇒ drain returns None"
);
assert_eq!(st.next_async_number(&a), i + 1);
}
}
#[test]
fn phase_c_out_of_order_enqueues_then_drains_in_priority_order() {
let mut st = RpcState::new(AddressSpace::Acceptor);
let b = SIBinder::new(Arc::new(Dummy)).unwrap();
let a = st.on_binder_leaving(&b).unwrap();
for arrival_async in [2u64, 4, 1, 3, 0] {
let txn = mk_txn(a, arrival_async);
let decision = st.dispatch_async_or_enqueue(a, arrival_async, txn, vec![]);
if arrival_async == 0 {
match decision {
AsyncDecision::Dispatch(t, _) => assert_eq!(t.async_number, 0),
_ => panic!("arrival 0 must dispatch"),
}
break;
} else {
assert!(
matches!(decision, AsyncDecision::Enqueued),
"out-of-order arrival {arrival_async} must enqueue (expected was 0)"
);
}
}
assert_eq!(st.async_todo_len(&a), 4, "1, 2, 3, 4 parked");
let mut dispatched = vec![0u64];
while let Some((t, _)) = st.advance_and_pop_async(a) {
dispatched.push(t.async_number);
}
assert_eq!(
dispatched,
vec![0, 1, 2, 3, 4],
"per-node monotonic dispatch despite wire reorder"
);
assert_eq!(st.async_todo_len(&a), 0, "drained");
assert_eq!(st.next_async_number(&a), 5);
}
#[test]
fn phase_c_async_todo_terminate_caps_and_flushes_backlog() {
let mut st = RpcState::new(AddressSpace::Acceptor);
let b = SIBinder::new(Arc::new(Dummy)).unwrap();
let a = st.on_binder_leaving(&b).unwrap();
for n in 1..ASYNC_TODO_TERMINATE_LEVEL as u64 {
let txn = mk_txn(a, n);
assert!(
matches!(
st.dispatch_async_or_enqueue(a, n, txn, vec![]),
AsyncDecision::Enqueued
),
"async_number {n} below the watermark must park"
);
}
assert_eq!(st.async_todo_len(&a), ASYNC_TODO_TERMINATE_LEVEL - 1);
let n = ASYNC_TODO_TERMINATE_LEVEL as u64;
let txn = mk_txn(a, n);
match st.dispatch_async_or_enqueue(a, n, txn, vec![]) {
AsyncDecision::Terminate(pending) => {
assert_eq!(pending, ASYNC_TODO_TERMINATE_LEVEL)
}
other => panic!("watermark must terminate, got {other:?}"),
}
assert_eq!(st.async_todo_len(&a), 0, "backlog flushed on terminate");
}
#[test]
fn phase_c_unknown_address_drops_not_enqueues() {
let mut st = RpcState::new(AddressSpace::Acceptor);
let unknown = RpcAddress::from_wire_bytes([9u8; 32]);
let txn = mk_txn(unknown, 0);
assert!(matches!(
st.dispatch_async_or_enqueue(unknown, 0, txn, vec![]),
AsyncDecision::Drop(DropReason::UnknownAddress)
));
assert!(st.advance_and_pop_async(unknown).is_none());
}
#[test]
fn phase_c_stale_arrival_drops_and_heap_drains_below_expected() {
let mut st = RpcState::new(AddressSpace::Acceptor);
let b = SIBinder::new(Arc::new(Dummy)).unwrap();
let a = st.on_binder_leaving(&b).unwrap();
for i in 0..3u64 {
let txn = mk_txn(a, i);
assert!(matches!(
st.dispatch_async_or_enqueue(a, i, txn, vec![]),
AsyncDecision::Dispatch(_, _)
));
let _ = st.advance_and_pop_async(a);
}
assert_eq!(st.next_async_number(&a), 3);
let txn = mk_txn(a, 1);
assert!(matches!(
st.dispatch_async_or_enqueue(a, 1, txn, vec![]),
AsyncDecision::Drop(DropReason::StaleAsyncNumber)
));
assert_eq!(st.async_todo_len(&a), 0);
let txn5 = mk_txn(a, 5);
let _ = st.dispatch_async_or_enqueue(a, 5, txn5, vec![]);
assert_eq!(st.async_todo_len(&a), 1);
let _ = st.dispatch_async_or_enqueue(a, 3, mk_txn(a, 3), vec![]);
let _ = st.advance_and_pop_async(a); let _ = st.dispatch_async_or_enqueue(a, 4, mk_txn(a, 4), vec![]);
let _ = st.advance_and_pop_async(a); assert_eq!(st.async_todo_len(&a), 0);
assert_eq!(st.next_async_number(&a), 5);
}
#[test]
fn phase_c_per_node_independence() {
let mut st = RpcState::new(AddressSpace::Acceptor);
let b1 = SIBinder::new(Arc::new(Dummy)).unwrap();
let b2 = SIBinder::new(Arc::new(Dummy)).unwrap();
let a1 = st.on_binder_leaving(&b1).unwrap();
let a2 = st.on_binder_leaving(&b2).unwrap();
let txn = mk_txn(a1, 1);
assert!(matches!(
st.dispatch_async_or_enqueue(a1, 1, txn, vec![]),
AsyncDecision::Enqueued
));
assert_eq!(st.async_todo_len(&a1), 1);
let txn = mk_txn(a2, 0);
match st.dispatch_async_or_enqueue(a2, 0, txn, vec![]) {
AsyncDecision::Dispatch(t, _) => assert_eq!(t.async_number, 0),
_ => panic!("a2 must dispatch independently of a1's stalled queue"),
}
assert_eq!(st.async_todo_len(&a2), 0);
assert_eq!(st.next_async_number(&a1), 0, "a1 not yet advanced");
assert!(st.advance_and_pop_async(a2).is_none());
assert_eq!(st.next_async_number(&a2), 1);
}
}