#![cfg_attr(feature = "tokio", doc = "[`CancellationToken::canceled`].")]
#![cfg_attr(
not(feature = "tokio"),
doc = "`CancellationToken::canceled` (`tokio` feature)."
)]
use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::{Arc, Mutex};
use crate::binder::{FromIBinder, Interface, Strong};
use crate::error::{Result, StatusCode};
use crate::status::BinderResult;
use crate::SIBinder;
include!(concat!(env!("OUT_DIR"), "/cancellation_signal.rs"));
pub use android::os::ICancellationSignal::{
BnCancellationSignal, BpCancellationSignal, ICancellationSignal, ICancellationSignalDefault,
ICancellationSignalDefaultRef,
};
#[cfg(feature = "async")]
pub use android::os::ICancellationSignal::{
ICancellationSignalAsync, ICancellationSignalAsyncService,
};
struct Inner {
canceled: AtomicBool,
on_cancel: Mutex<Option<Box<dyn FnOnce() + Send>>>,
#[cfg(feature = "tokio")]
notify: tokio::sync::Notify,
}
impl Inner {
fn new() -> Arc<Self> {
Arc::new(Inner {
canceled: AtomicBool::new(false),
on_cancel: Mutex::new(None),
#[cfg(feature = "tokio")]
notify: tokio::sync::Notify::new(),
})
}
fn cancel(self: &Arc<Self>) {
if self.canceled.swap(true, Ordering::SeqCst) {
return;
}
#[cfg(feature = "tokio")]
self.notify.notify_waiters();
let listener = self
.on_cancel
.lock()
.unwrap_or_else(|e| e.into_inner())
.take();
if let Some(listener) = listener {
listener();
}
}
fn set_on_cancel(self: &Arc<Self>, f: Box<dyn FnOnce() + Send>) {
if self.canceled.load(Ordering::SeqCst) {
f();
return;
}
let previous = {
let mut slot = self.on_cancel.lock().unwrap_or_else(|e| e.into_inner());
slot.replace(f)
};
drop(previous);
if self.canceled.load(Ordering::SeqCst) {
let late = self
.on_cancel
.lock()
.unwrap_or_else(|e| e.into_inner())
.take();
if let Some(late) = late {
late();
}
}
}
}
struct Transport(Arc<Inner>);
impl Interface for Transport {}
impl ICancellationSignal for Transport {
fn r#cancel(&self) -> BinderResult<()> {
self.0.cancel();
Ok(())
}
}
#[derive(Clone)]
pub struct CancellationSignal(Arc<Inner>);
impl Default for CancellationSignal {
fn default() -> Self {
Self::new()
}
}
impl CancellationSignal {
pub fn new() -> Self {
CancellationSignal(Inner::new())
}
pub fn create_transport(&self) -> Strong<dyn ICancellationSignal> {
BnCancellationSignal::new_binder(Transport(self.0.clone()))
}
pub fn cancel(&self) {
self.0.cancel();
}
pub fn is_canceled(&self) -> bool {
self.0.canceled.load(Ordering::SeqCst)
}
pub fn set_on_cancel(&self, f: impl FnOnce() + Send + 'static) {
self.0.set_on_cancel(Box::new(f));
}
pub fn token(&self) -> CancellationToken {
CancellationToken(self.0.clone())
}
}
impl std::fmt::Debug for CancellationSignal {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("CancellationSignal")
.field("canceled", &self.is_canceled())
.finish()
}
}
#[derive(Clone)]
pub struct CancellationToken(Arc<Inner>);
impl CancellationToken {
pub fn is_canceled(&self) -> bool {
self.0.canceled.load(Ordering::SeqCst)
}
#[cfg(feature = "tokio")]
pub async fn canceled(&self) {
let notified = self.0.notify.notified();
if self.is_canceled() {
return;
}
notified.await;
}
}
impl std::fmt::Debug for CancellationToken {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("CancellationToken")
.field("canceled", &self.is_canceled())
.finish()
}
}
pub fn cancel_remote(binder: &SIBinder) -> Result<()> {
let expected = <BpCancellationSignal as crate::Proxy>::descriptor();
let actual = binder.descriptor();
if actual.is_empty() {
#[cfg(feature = "rpc")]
if let Some(rp) = (**binder).as_any().downcast_ref::<crate::rpc::RpcProxy>() {
let data = rp.build_request(expected)?;
rp.transact(
android::os::ICancellationSignal::transactions::r#cancel,
&data,
crate::FLAG_ONEWAY | crate::FLAG_CLEAR_BUF | crate::FLAG_PRIVATE_LOCAL,
)?;
return Ok(());
}
log::error!("cancel_remote: a descriptor-less binder that is not an RPC proxy");
return Err(StatusCode::BadType);
}
if actual != expected {
log::error!("cancel_remote: not an {expected}: {actual}");
return Err(StatusCode::BadType);
}
let signal: Strong<dyn ICancellationSignal> =
FromIBinder::try_from(binder.clone()).map_err(|e| {
log::error!("cancel_remote: not an android.os.ICancellationSignal: {e:?}");
StatusCode::BadType
})?;
signal.r#cancel().map_err(StatusCode::from)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn a_listener_runs_once_and_late_registration_runs_now() {
let signal = CancellationSignal::new();
let runs = Arc::new(AtomicBool::new(false));
let flag = runs.clone();
signal.set_on_cancel(move || flag.store(true, Ordering::SeqCst));
assert!(!signal.is_canceled());
assert!(!runs.load(Ordering::SeqCst));
signal.cancel();
assert!(signal.is_canceled());
assert!(runs.load(Ordering::SeqCst), "the listener must have run");
runs.store(false, Ordering::SeqCst);
signal.cancel();
assert!(!runs.load(Ordering::SeqCst), "the listener ran twice");
let late = Arc::new(AtomicBool::new(false));
let flag = late.clone();
signal.set_on_cancel(move || flag.store(true, Ordering::SeqCst));
assert!(late.load(Ordering::SeqCst));
}
#[test]
fn the_transport_cancels_the_signal_it_came_from() {
let signal = CancellationSignal::new();
let token = signal.token();
let clone = signal.clone();
let transport = signal.create_transport();
assert!(!token.is_canceled());
transport.r#cancel().expect("local transport call");
assert!(signal.is_canceled());
assert!(clone.is_canceled());
assert!(token.is_canceled());
}
#[test]
fn a_dropped_signal_still_cancels_through_its_transport() {
let signal = CancellationSignal::new();
let token = signal.token();
let transport = signal.create_transport();
drop(signal);
transport.r#cancel().expect("local transport call");
assert!(token.is_canceled());
}
#[cfg(feature = "tokio")]
#[test]
fn an_async_waiter_wakes_on_cancel_and_returns_at_once_afterwards() {
use std::sync::mpsc;
use std::time::Duration;
let rt = tokio::runtime::Builder::new_multi_thread()
.worker_threads(2)
.build()
.expect("runtime");
let signal = CancellationSignal::new();
let token = signal.token();
let (tx, rx) = mpsc::channel();
rt.spawn({
let token = token.clone();
async move {
token.canceled().await;
let _ = tx.send(());
}
});
signal.cancel();
rx.recv_timeout(Duration::from_secs(5))
.expect("canceled() did not wake within 5s");
let (tx, rx) = mpsc::channel();
rt.spawn(async move {
token.canceled().await;
let _ = tx.send(());
});
rx.recv_timeout(Duration::from_secs(5))
.expect("canceled() on an already-cancelled token must return at once");
}
}