use std::io;
use std::net::SocketAddrV4;
use std::os::fd::OwnedFd;
use std::path::PathBuf;
use crate::error::Result;
#[derive(Clone, Debug)]
pub enum AccessorSockAddr {
Unix(PathBuf),
Vsock { cid: u32, port: u32 },
Inet(SocketAddrV4),
}
impl AccessorSockAddr {
pub fn family_str(&self) -> &'static str {
match self {
AccessorSockAddr::Unix(_) => "AF_UNIX",
AccessorSockAddr::Vsock { .. } => "AF_VSOCK",
AccessorSockAddr::Inet(_) => "AF_INET",
}
}
pub fn connect_owned_fd(&self) -> std::result::Result<OwnedFd, AccessorConnectError> {
match self {
AccessorSockAddr::Unix(path) => connect_unix_owned_fd(path),
AccessorSockAddr::Vsock { cid, port } => connect_vsock_owned_fd(*cid, *port),
AccessorSockAddr::Inet(addr) => connect_inet_owned_fd(*addr),
}
}
}
#[derive(Debug)]
pub enum AccessorConnectError {
UnsupportedFamily,
CreateSocketFailed(io::Error),
ConnectFailedEacces,
ConnectFailed(io::Error),
}
impl std::fmt::Display for AccessorConnectError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
AccessorConnectError::UnsupportedFamily => {
write!(f, "unsupported socket family for IAccessor::addConnection")
}
AccessorConnectError::CreateSocketFailed(e) => {
write!(f, "socket(2) failed: {e}")
}
AccessorConnectError::ConnectFailedEacces => {
write!(f, "connect(2) refused with EACCES")
}
AccessorConnectError::ConnectFailed(e) => {
write!(f, "connect(2) failed: {e}")
}
}
}
}
impl std::error::Error for AccessorConnectError {}
fn connect_unix_owned_fd(path: &PathBuf) -> std::result::Result<OwnedFd, AccessorConnectError> {
use std::os::unix::net::UnixStream;
match UnixStream::connect(path) {
Ok(stream) => Ok(OwnedFd::from(stream)),
Err(e) if e.kind() == std::io::ErrorKind::PermissionDenied => {
Err(AccessorConnectError::ConnectFailedEacces)
}
Err(e) => Err(AccessorConnectError::ConnectFailed(e)),
}
}
#[cfg(feature = "rpc-vsock")]
fn connect_vsock_owned_fd(
cid: u32,
port: u32,
) -> std::result::Result<OwnedFd, AccessorConnectError> {
use std::os::fd::IntoRawFd;
use vsock::{VsockAddr, VsockStream};
match VsockStream::connect(&VsockAddr::new(cid, port)) {
Ok(stream) => {
let raw = stream.into_raw_fd();
Ok(unsafe { OwnedFd::from_raw_fd(raw) })
}
Err(e) if e.kind() == std::io::ErrorKind::PermissionDenied => {
Err(AccessorConnectError::ConnectFailedEacces)
}
Err(e) => Err(AccessorConnectError::ConnectFailed(e)),
}
}
#[cfg(not(feature = "rpc-vsock"))]
fn connect_vsock_owned_fd(
_cid: u32,
_port: u32,
) -> std::result::Result<OwnedFd, AccessorConnectError> {
Err(AccessorConnectError::UnsupportedFamily)
}
#[cfg(feature = "rpc-tcp-debug")]
fn connect_inet_owned_fd(addr: SocketAddrV4) -> std::result::Result<OwnedFd, AccessorConnectError> {
use std::net::TcpStream;
match TcpStream::connect(addr) {
Ok(stream) => Ok(OwnedFd::from(stream)),
Err(e) if e.kind() == std::io::ErrorKind::PermissionDenied => {
Err(AccessorConnectError::ConnectFailedEacces)
}
Err(e) => Err(AccessorConnectError::ConnectFailed(e)),
}
}
#[cfg(not(feature = "rpc-tcp-debug"))]
fn connect_inet_owned_fd(
_addr: SocketAddrV4,
) -> std::result::Result<OwnedFd, AccessorConnectError> {
Err(AccessorConnectError::UnsupportedFamily)
}
#[cfg(feature = "rpc-vsock")]
use std::os::fd::FromRawFd;
use super::accessor_16::{
BnAccessor, IAccessor, ERROR_CONNECTION_INFO_NOT_FOUND, ERROR_FAILED_TO_CONNECT_EACCES,
ERROR_FAILED_TO_CONNECT_TO_SOCKET, ERROR_FAILED_TO_CREATE_SOCKET,
ERROR_UNSUPPORTED_SOCKET_FAMILY,
};
use crate::binder::{Interface, Strong};
use crate::file_descriptor::ParcelFileDescriptor;
use crate::status::Status;
pub struct LocalAccessor {
instance: String,
addr_provider: AccessorAddrProvider,
}
impl LocalAccessor {
pub fn new_binder(
instance: impl Into<String>,
addr_provider: AccessorAddrProvider,
) -> Strong<dyn IAccessor> {
BnAccessor::new_binder(LocalAccessor {
instance: instance.into(),
addr_provider,
})
}
}
impl Interface for LocalAccessor {}
impl IAccessor for LocalAccessor {
fn addConnection(&self) -> crate::status::Result<ParcelFileDescriptor> {
let addr = match (self.addr_provider)(&self.instance) {
Ok(a) => a,
Err(e) => {
let msg = format!("AccessorAddrProvider({:?}) failed: {e}", self.instance);
log::warn!("{msg}");
return Err(Status::new_service_specific_error(
ERROR_CONNECTION_INFO_NOT_FOUND,
Some(msg),
));
}
};
match addr.connect_owned_fd() {
Ok(fd) => Ok(ParcelFileDescriptor::new(fd)),
Err(err) => {
let (code, label) = accessor_error_code_for(&err);
let msg = format!(
"LocalAccessor({:?}) {} ({}): {err}",
self.instance,
addr.family_str(),
label,
);
log::warn!("{msg}");
Err(Status::new_service_specific_error(code, Some(msg)))
}
}
}
fn getInstanceName(&self) -> crate::status::Result<String> {
Ok(self.instance.clone())
}
}
fn accessor_error_code_for(err: &AccessorConnectError) -> (i32, &'static str) {
match err {
AccessorConnectError::UnsupportedFamily => (
ERROR_UNSUPPORTED_SOCKET_FAMILY,
"ERROR_UNSUPPORTED_SOCKET_FAMILY",
),
AccessorConnectError::CreateSocketFailed(_) => (
ERROR_FAILED_TO_CREATE_SOCKET,
"ERROR_FAILED_TO_CREATE_SOCKET",
),
AccessorConnectError::ConnectFailedEacces => (
ERROR_FAILED_TO_CONNECT_EACCES,
"ERROR_FAILED_TO_CONNECT_EACCES",
),
AccessorConnectError::ConnectFailed(_) => (
ERROR_FAILED_TO_CONNECT_TO_SOCKET,
"ERROR_FAILED_TO_CONNECT_TO_SOCKET",
),
}
}
pub type AccessorAddrProvider = Box<dyn Fn(&str) -> Result<AccessorSockAddr> + Send + Sync>;
use std::collections::HashSet;
use std::sync::{Arc, OnceLock, Weak};
pub type AccessorProviderFn = Box<dyn Fn(&str) -> Option<crate::binder::SIBinder> + Send + Sync>;
struct AccessorProviderEntry {
instances: HashSet<String>,
provider: Arc<AccessorProviderFn>,
}
static REGISTRY: OnceLock<std::sync::Mutex<Vec<AccessorProviderEntry>>> = OnceLock::new();
fn registry() -> &'static std::sync::Mutex<Vec<AccessorProviderEntry>> {
REGISTRY.get_or_init(|| std::sync::Mutex::new(Vec::new()))
}
fn is_instance_provided_locked(entries: &[AccessorProviderEntry], instance: &str) -> bool {
entries.iter().any(|e| e.instances.contains(instance))
}
pub fn add_accessor_provider(
instances: HashSet<String>,
provider: AccessorProviderFn,
) -> std::result::Result<AccessorProviderHandle, crate::error::StatusCode> {
if instances.is_empty() {
log::warn!("add_accessor_provider: empty instance set rejected");
return Err(crate::error::StatusCode::BadValue);
}
let mut reg = registry().lock().expect("accessor registry poisoned");
for instance in &instances {
if is_instance_provided_locked(®, instance) {
log::warn!("add_accessor_provider: instance {instance:?} already provided — rejecting");
return Err(crate::error::StatusCode::AlreadyExists);
}
}
let arc_provider = Arc::new(provider);
let handle = AccessorProviderHandle {
provider: Arc::downgrade(&arc_provider),
};
reg.push(AccessorProviderEntry {
instances,
provider: arc_provider,
});
Ok(handle)
}
pub fn remove_accessor_provider(
handle: AccessorProviderHandle,
) -> std::result::Result<(), crate::error::StatusCode> {
let Some(target) = handle.provider.upgrade() else {
return Err(crate::error::StatusCode::BadValue);
};
let mut reg = registry().lock().expect("accessor registry poisoned");
let before = reg.len();
reg.retain(|e| !Arc::ptr_eq(&e.provider, &target));
if reg.len() == before {
return Err(crate::error::StatusCode::NameNotFound);
}
Ok(())
}
pub struct AccessorProviderHandle {
provider: Weak<AccessorProviderFn>,
}
impl std::fmt::Debug for AccessorProviderHandle {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("AccessorProviderHandle")
.field("live", &(self.provider.strong_count() > 0))
.finish()
}
}
impl AccessorProviderHandle {
#[cfg(test)]
fn is_live(&self) -> bool {
self.provider.strong_count() > 0
}
}
impl Drop for AccessorProviderHandle {
fn drop(&mut self) {
let Some(target) = self.provider.upgrade() else {
return;
};
if let Ok(mut reg) = registry().lock() {
reg.retain(|e| !Arc::ptr_eq(&e.provider, &target));
}
}
}
pub(crate) fn lookup_accessor_provider(name: &str) -> Option<crate::binder::SIBinder> {
let snapshot: Vec<Arc<AccessorProviderFn>> = {
let reg = registry().lock().expect("accessor registry poisoned");
reg.iter()
.filter(|e| e.instances.contains(name))
.map(|e| Arc::clone(&e.provider))
.collect()
};
for provider in snapshot {
if let Some(binder) = provider(name) {
return Some(binder);
}
}
None
}
pub fn resolve_via_process_local(
name: &str,
) -> Option<super::servicemanager_16::android::os::ServiceWithMetadata::ServiceWithMetadata> {
let accessor = lookup_accessor_provider(name)?;
super::accessor_16::resolve_accessor(name, accessor)
}
pub fn create_accessor(
instance: impl Into<String>,
addr_provider: AccessorAddrProvider,
) -> crate::binder::SIBinder {
LocalAccessor::new_binder(instance, addr_provider).as_binder()
}
#[cfg(test)]
mod tests {
use super::*;
use std::os::fd::AsRawFd;
use std::os::unix::net::{UnixListener, UnixStream};
#[test]
fn family_str_covers_all_variants() {
let unix = AccessorSockAddr::Unix(PathBuf::from("/tmp/rsbinder-test-2-14.sock"));
let vsock = AccessorSockAddr::Vsock { cid: 2, port: 5555 };
let inet = AccessorSockAddr::Inet("127.0.0.1:5555".parse().expect("addr"));
assert_eq!(unix.family_str(), "AF_UNIX");
assert_eq!(vsock.family_str(), "AF_VSOCK");
assert_eq!(inet.family_str(), "AF_INET");
}
#[test]
fn clone_and_debug_roundtrip() {
let a = AccessorSockAddr::Unix(PathBuf::from("/tmp/x"));
let b = a.clone();
let dbg = format!("{b:?}");
assert!(dbg.contains("Unix"));
assert!(dbg.contains("/tmp/x"));
}
#[test]
fn provider_closure_compiles() {
let path = PathBuf::from("/tmp/rsbinder-test-provider.sock");
let provider: AccessorAddrProvider =
Box::new(move |_name: &str| Ok(AccessorSockAddr::Unix(path.clone())));
let got = provider("instance-x").expect("provider ok");
match got {
AccessorSockAddr::Unix(p) => {
assert_eq!(p, PathBuf::from("/tmp/rsbinder-test-provider.sock"))
}
other => panic!("expected Unix, got {other:?}"),
}
}
fn unique_unix_listener(tag: &str) -> (UnixListener, PathBuf) {
let mut p = std::env::temp_dir();
p.push(format!("rsb_{}_{}.sock", tag, std::process::id()));
let _ = std::fs::remove_file(&p);
let l = UnixListener::bind(&p).expect("bind");
(l, p)
}
#[test]
fn connect_owned_fd_unix_roundtrips() {
use std::io::{Read, Write};
let (listener, path) = unique_unix_listener("a02-rt");
let server_handle = std::thread::spawn(move || {
let (mut s, _) = listener.accept().expect("accept");
let mut buf = [0u8; 1];
s.read_exact(&mut buf).expect("read");
s.write_all(&buf).expect("write");
});
let addr = AccessorSockAddr::Unix(path.clone());
let fd = addr.connect_owned_fd().expect("connect");
let mut stream = UnixStream::from(fd);
stream.write_all(&[0x42]).expect("client write");
let mut got = [0u8; 1];
stream.read_exact(&mut got).expect("client read");
assert_eq!(got, [0x42]);
drop(stream);
server_handle.join().expect("server join");
let _ = std::fs::remove_file(&path);
}
#[test]
fn connect_owned_fd_unix_missing_path_is_connect_failed() {
let path = PathBuf::from(format!("/tmp/rsb_2_14_nope_{}.sock", std::process::id()));
let addr = AccessorSockAddr::Unix(path);
let err = addr.connect_owned_fd().expect_err("must fail");
assert!(
matches!(err, AccessorConnectError::ConnectFailed(_)),
"expected ConnectFailed, got {err:?}"
);
}
#[cfg(not(feature = "rpc-vsock"))]
#[test]
fn connect_owned_fd_vsock_disabled_is_unsupported() {
let addr = AccessorSockAddr::Vsock { cid: 2, port: 5555 };
let err = addr.connect_owned_fd().expect_err("vsock disabled");
assert!(
matches!(err, AccessorConnectError::UnsupportedFamily),
"expected UnsupportedFamily, got {err:?}"
);
}
#[cfg(not(feature = "rpc-tcp-debug"))]
#[test]
fn connect_owned_fd_inet_disabled_is_unsupported() {
let addr = AccessorSockAddr::Inet("127.0.0.1:5555".parse().expect("addr"));
let err = addr.connect_owned_fd().expect_err("inet disabled");
assert!(
matches!(err, AccessorConnectError::UnsupportedFamily),
"expected UnsupportedFamily, got {err:?}"
);
}
#[test]
fn connect_error_display_includes_inner_cause() {
let e = AccessorConnectError::ConnectFailed(io::Error::new(
io::ErrorKind::ConnectionRefused,
"test cause",
));
let s = format!("{e}");
assert!(s.contains("test cause"), "Display missing inner: {s}");
assert!(s.contains("connect(2)"), "Display missing op label: {s}");
}
#[test]
fn connect_owned_fd_returns_real_fd() {
let (listener, path) = unique_unix_listener("a02-fd");
let server_handle = std::thread::spawn(move || {
let _ = listener.accept();
});
let addr = AccessorSockAddr::Unix(path.clone());
let fd = addr.connect_owned_fd().expect("connect");
assert!(
fd.as_raw_fd() >= 3,
"expected real fd, got {}",
fd.as_raw_fd()
);
drop(fd);
server_handle.join().expect("server join");
let _ = std::fs::remove_file(&path);
}
use crate::FromIBinder;
#[test]
fn local_accessor_inprocess_roundtrip() {
use std::io::{Read, Write};
use std::os::unix::net::UnixStream;
let (listener, path) = unique_unix_listener("a03-rt");
let server_handle = std::thread::spawn(move || {
let (mut s, _) = listener.accept().expect("accept");
let mut buf = [0u8; 1];
s.read_exact(&mut buf).expect("read");
s.write_all(&buf).expect("write");
});
let provider_path = path.clone();
let provider: AccessorAddrProvider =
Box::new(move |_name: &str| Ok(AccessorSockAddr::Unix(provider_path.clone())));
let strong = LocalAccessor::new_binder("rsbinder.test.a03", provider);
let sib: crate::binder::SIBinder = strong.as_binder();
let bp = <dyn IAccessor as FromIBinder>::try_from(sib).expect("FromIBinder");
assert_eq!(
bp.getInstanceName().expect("getInstanceName"),
"rsbinder.test.a03"
);
let pfd = bp.addConnection().expect("addConnection");
let fd: std::os::fd::OwnedFd = pfd.into();
let mut stream = UnixStream::from(fd);
stream.write_all(&[0xA5]).expect("client write");
let mut got = [0u8; 1];
stream.read_exact(&mut got).expect("client read");
assert_eq!(got, [0xA5]);
drop(stream);
server_handle.join().expect("server join");
let _ = std::fs::remove_file(&path);
}
#[test]
fn local_accessor_provider_error_maps_to_connection_info_not_found() {
let provider: AccessorAddrProvider =
Box::new(|_name: &str| Err(crate::error::StatusCode::BadValue));
let strong = LocalAccessor::new_binder("rsbinder.test.a03.provider-err", provider);
let sib = strong.as_binder();
let bp = <dyn IAccessor as FromIBinder>::try_from(sib).expect("FromIBinder");
assert_eq!(
bp.getInstanceName().unwrap(),
"rsbinder.test.a03.provider-err"
);
let status = bp.addConnection().expect_err("must fail");
assert_eq!(
status.service_specific_error(),
ERROR_CONNECTION_INFO_NOT_FOUND,
"expected ERROR_CONNECTION_INFO_NOT_FOUND, got {status:?}"
);
}
#[test]
fn local_accessor_connect_failure_maps_to_connect_to_socket() {
let path = PathBuf::from(format!(
"/tmp/rsb_2_14_a03_nope_{}.sock",
std::process::id()
));
let provider: AccessorAddrProvider =
Box::new(move |_name: &str| Ok(AccessorSockAddr::Unix(path.clone())));
let strong = LocalAccessor::new_binder("rsbinder.test.a03.no-listener", provider);
let sib = strong.as_binder();
let bp = <dyn IAccessor as FromIBinder>::try_from(sib).expect("FromIBinder");
let status = bp.addConnection().expect_err("must fail (no listener)");
assert_eq!(
status.service_specific_error(),
ERROR_FAILED_TO_CONNECT_TO_SOCKET,
"expected ERROR_FAILED_TO_CONNECT_TO_SOCKET, got {status:?}"
);
}
#[cfg(not(feature = "rpc-vsock"))]
#[test]
fn local_accessor_unsupported_family_maps_to_unsupported() {
let provider: AccessorAddrProvider =
Box::new(|_name: &str| Ok(AccessorSockAddr::Vsock { cid: 2, port: 5555 }));
let strong = LocalAccessor::new_binder("rsbinder.test.a03.vsock-disabled", provider);
let sib = strong.as_binder();
let bp = <dyn IAccessor as FromIBinder>::try_from(sib).expect("FromIBinder");
let status = bp.addConnection().expect_err("must fail (vsock disabled)");
assert_eq!(
status.service_specific_error(),
ERROR_UNSUPPORTED_SOCKET_FAMILY,
"expected ERROR_UNSUPPORTED_SOCKET_FAMILY, got {status:?}"
);
}
fn unique_instance(tag: &str) -> String {
format!(
"rsb.test.a4.{}.{}.{}",
tag,
std::process::id(),
line!() )
}
#[test]
fn add_two_providers_disjoint_instances() {
let instance_a = unique_instance("disjoint-a");
let instance_b = unique_instance("disjoint-b");
let path_a = PathBuf::from(format!("/tmp/rsb_a4_disj_a_{}.sock", std::process::id()));
let path_b = PathBuf::from(format!("/tmp/rsb_a4_disj_b_{}.sock", std::process::id()));
let cap_a = path_a.clone();
let cap_b = path_b.clone();
let provider_a: AccessorProviderFn = {
let want = instance_a.clone();
Box::new(move |name: &str| {
if name == want {
Some(create_accessor(
name,
Box::new({
let p = cap_a.clone();
move |_| Ok(AccessorSockAddr::Unix(p.clone()))
}),
))
} else {
None
}
})
};
let provider_b: AccessorProviderFn = {
let want = instance_b.clone();
Box::new(move |name: &str| {
if name == want {
Some(create_accessor(
name,
Box::new({
let p = cap_b.clone();
move |_| Ok(AccessorSockAddr::Unix(p.clone()))
}),
))
} else {
None
}
})
};
let h_a =
add_accessor_provider(HashSet::from([instance_a.clone()]), provider_a).expect("add a");
let h_b =
add_accessor_provider(HashSet::from([instance_b.clone()]), provider_b).expect("add b");
assert!(h_a.is_live());
assert!(h_b.is_live());
let bp_a = <dyn IAccessor as FromIBinder>::try_from(
lookup_accessor_provider(&instance_a).expect("lookup a"),
)
.expect("cast a");
assert_eq!(bp_a.getInstanceName().unwrap(), instance_a);
let bp_b = <dyn IAccessor as FromIBinder>::try_from(
lookup_accessor_provider(&instance_b).expect("lookup b"),
)
.expect("cast b");
assert_eq!(bp_b.getInstanceName().unwrap(), instance_b);
assert!(lookup_accessor_provider("rsb.test.a4.absent.unknown").is_none());
drop(h_a);
drop(h_b);
assert!(lookup_accessor_provider(&instance_a).is_none());
assert!(lookup_accessor_provider(&instance_b).is_none());
}
#[test]
fn add_accessor_provider_rejects_duplicate_instance() {
let instance = unique_instance("dup");
let first: AccessorProviderFn = Box::new({
let want = instance.clone();
move |name: &str| {
if name == want {
Some(create_accessor(
name,
Box::new(|_| {
Ok(AccessorSockAddr::Unix(PathBuf::from(
"/tmp/rsb_a4_dup_1.sock",
)))
}),
))
} else {
None
}
}
});
let h1 = add_accessor_provider(HashSet::from([instance.clone()]), first).expect("add 1");
assert!(h1.is_live());
let second: AccessorProviderFn = Box::new(|_name| {
Some(create_accessor(
"ignored",
Box::new(|_| Ok(AccessorSockAddr::Unix(PathBuf::from("/tmp/x")))),
))
});
let err = add_accessor_provider(HashSet::from([instance.clone()]), second)
.expect_err("dup must reject");
assert_eq!(
err,
crate::error::StatusCode::AlreadyExists,
"dup-instance must surface AlreadyExists, got {err:?}"
);
let bp = <dyn IAccessor as FromIBinder>::try_from(
lookup_accessor_provider(&instance).expect("lookup hits first"),
)
.expect("cast");
assert_eq!(bp.getInstanceName().unwrap(), instance);
drop(h1);
assert!(lookup_accessor_provider(&instance).is_none());
}
#[test]
fn add_accessor_provider_rejects_empty_instances() {
let provider: AccessorProviderFn = Box::new(|_| None);
let err =
add_accessor_provider(HashSet::new(), provider).expect_err("empty set must reject");
assert_eq!(err, crate::error::StatusCode::BadValue);
}
#[test]
fn handle_drop_unregisters() {
let instance = unique_instance("raii");
let provider: AccessorProviderFn = {
let want = instance.clone();
Box::new(move |name: &str| {
if name == want {
Some(create_accessor(
name,
Box::new(|_| {
Ok(AccessorSockAddr::Unix(PathBuf::from(
"/tmp/rsb_a4_raii.sock",
)))
}),
))
} else {
None
}
})
};
let h = add_accessor_provider(HashSet::from([instance.clone()]), provider).expect("add");
assert!(lookup_accessor_provider(&instance).is_some());
drop(h);
assert!(
lookup_accessor_provider(&instance).is_none(),
"handle drop must unregister"
);
}
#[test]
fn explicit_remove_succeeds() {
let instance = unique_instance("explicit");
let provider: AccessorProviderFn = {
let want = instance.clone();
Box::new(move |name: &str| {
if name == want {
Some(create_accessor(
name,
Box::new(|_| {
Ok(AccessorSockAddr::Unix(PathBuf::from(
"/tmp/rsb_a4_explicit.sock",
)))
}),
))
} else {
None
}
})
};
let h = add_accessor_provider(HashSet::from([instance.clone()]), provider).expect("add");
remove_accessor_provider(h).expect("explicit remove");
assert!(
lookup_accessor_provider(&instance).is_none(),
"after explicit remove, lookup must be None"
);
}
#[test]
fn concurrent_register_and_lookup_p6() {
const N: usize = 8;
let mut handles = Vec::new();
let started = Arc::new(std::sync::Barrier::new(N));
for i in 0..N {
let bar = Arc::clone(&started);
handles.push(std::thread::spawn(move || {
bar.wait();
let instance = format!(
"rsb.test.a4.p6.{}.{:?}.{}",
std::process::id(),
std::thread::current().id(),
i,
);
let cap = instance.clone();
let provider: AccessorProviderFn = Box::new(move |name: &str| {
if name == cap {
Some(create_accessor(
name,
Box::new(|_| {
Ok(AccessorSockAddr::Unix(PathBuf::from("/tmp/rsb_a4_p6.sock")))
}),
))
} else {
None
}
});
let h = add_accessor_provider(HashSet::from([instance.clone()]), provider)
.expect("add");
let bp = <dyn IAccessor as FromIBinder>::try_from(
lookup_accessor_provider(&instance).expect("self lookup"),
)
.expect("cast");
assert_eq!(bp.getInstanceName().unwrap(), instance);
assert!(lookup_accessor_provider("rsb.test.a4.p6.never").is_none());
drop(h);
}));
}
for h in handles {
h.join().expect("thread join");
}
}
}