binder_tokio: only use threadpool for new transactions
The binder kernel driver will detect whether a binder transaction is
started from a thread that is already handling a transaction, and if-so,
use some special logic to avoid deadlocks. This CL makes the async Rust
wrapper check whether we are handling a transaction before offloading
transactions to the `spawn_blocking` thread pool. This avoids breaking
the deadlock prevention in the kernel.
Test: m
Change-Id: If2707c464a4043c80bd893314113b65ef4633baf
diff --git a/libs/binder/rust/binder_tokio/lib.rs b/libs/binder/rust/binder_tokio/lib.rs
index 64833b6..91047be 100644
--- a/libs/binder/rust/binder_tokio/lib.rs
+++ b/libs/binder/rust/binder_tokio/lib.rs
@@ -35,6 +35,11 @@
/// Retrieve an existing service for a particular interface, sleeping for a few
/// seconds if it doesn't yet exist.
pub async fn get_interface<T: FromIBinder + ?Sized + 'static>(name: &str) -> Result<Strong<T>, StatusCode> {
+ if binder::is_handling_transaction() {
+ // See comment in the BinderAsyncPool impl.
+ return binder::public_api::get_interface::<T>(name);
+ }
+
let name = name.to_string();
let res = tokio::task::spawn_blocking(move || {
binder::public_api::get_interface::<T>(&name)
@@ -54,6 +59,11 @@
/// Retrieve an existing service for a particular interface, or start it if it
/// is configured as a dynamic service and isn't yet started.
pub async fn wait_for_interface<T: FromIBinder + ?Sized + 'static>(name: &str) -> Result<Strong<T>, StatusCode> {
+ if binder::is_handling_transaction() {
+ // See comment in the BinderAsyncPool impl.
+ return binder::public_api::wait_for_interface::<T>(name);
+ }
+
let name = name.to_string();
let res = tokio::task::spawn_blocking(move || {
binder::public_api::wait_for_interface::<T>(&name)
@@ -86,18 +96,27 @@
B: Send + 'a,
E: From<crate::StatusCode>,
{
- let handle = tokio::task::spawn_blocking(spawn_me);
- Box::pin(async move {
- // The `is_panic` branch is not actually reachable in Android as we compile
- // with `panic = abort`.
- match handle.await {
- Ok(res) => after_spawn(res).await,
- Err(e) if e.is_panic() => std::panic::resume_unwind(e.into_panic()),
- Err(e) if e.is_cancelled() => Err(StatusCode::FAILED_TRANSACTION.into()),
- Err(_) => Err(StatusCode::UNKNOWN_ERROR.into()),
- }
- })
+ if binder::is_handling_transaction() {
+ // We are currently on the thread pool for a binder server, so we should execute the
+ // transaction on the current thread so that the binder kernel driver is able to apply
+ // its deadlock prevention strategy to the sub-call.
+ //
+ // This shouldn't cause issues with blocking the thread as only one task will run in a
+ // call to `block_on`, so there aren't other tasks to block.
+ let result = spawn_me();
+ Box::pin(after_spawn(result))
+ } else {
+ let handle = tokio::task::spawn_blocking(spawn_me);
+ Box::pin(async move {
+ // The `is_panic` branch is not actually reachable in Android as we compile
+ // with `panic = abort`.
+ match handle.await {
+ Ok(res) => after_spawn(res).await,
+ Err(e) if e.is_panic() => std::panic::resume_unwind(e.into_panic()),
+ Err(e) if e.is_cancelled() => Err(StatusCode::FAILED_TRANSACTION.into()),
+ Err(_) => Err(StatusCode::UNKNOWN_ERROR.into()),
+ }
+ })
+ }
}
}
-
-
diff --git a/libs/binder/rust/src/lib.rs b/libs/binder/rust/src/lib.rs
index cce55c0..b94dfa1 100644
--- a/libs/binder/rust/src/lib.rs
+++ b/libs/binder/rust/src/lib.rs
@@ -114,7 +114,7 @@
};
pub use crate::binder_async::{BoxFuture, BinderAsyncPool};
pub use error::{status_t, ExceptionCode, Result, Status, StatusCode};
-pub use native::{add_service, force_lazy_services_persist, register_lazy_service, Binder};
+pub use native::{add_service, force_lazy_services_persist, is_handling_transaction, register_lazy_service, Binder};
pub use parcel::{BorrowedParcel, Parcel};
pub use proxy::{get_interface, get_service, wait_for_interface, wait_for_service};
pub use proxy::{AssociateClass, DeathRecipient, Proxy, SpIBinder, WpIBinder};
diff --git a/libs/binder/rust/src/native.rs b/libs/binder/rust/src/native.rs
index e183ea3..b7c7ae4 100644
--- a/libs/binder/rust/src/native.rs
+++ b/libs/binder/rust/src/native.rs
@@ -517,3 +517,12 @@
}
impl Interface for () {}
+
+/// Determine whether the current thread is currently executing an incoming
+/// transaction.
+pub fn is_handling_transaction() -> bool {
+ unsafe {
+ // Safety: This method is always safe to call.
+ sys::AIBinder_isHandlingTransaction()
+ }
+}