1use crate::device::kobject::Device;
9use crate::task::{CurrentTask, Kernel};
10use anyhow::Context as _;
11use fidl::endpoints::{
12 MemberOpener, Proxy as _, ServiceMarker, ServiceProxy as _, SynchronousProxy,
13};
14use fidl_fuchsia_io as fio;
15use fuchsia_async as fasync;
16use fuchsia_component::client as fclient;
17use fuchsia_fs::directory::{WatchEvent, WatchMessage, Watcher};
18use futures::{StreamExt as _, TryStreamExt as _};
19use starnix_logging::{log_info, log_warn};
20use starnix_sync::Mutex;
21use std::future::Future;
22use std::marker::PhantomData;
23use std::pin::Pin;
24use std::sync::atomic::{AtomicBool, Ordering};
25use std::sync::{Arc, Weak};
26use zx::Peered as _;
27
28type BoxFuture<'a, T> = Pin<Box<dyn Future<Output = T> + Send + 'a>>;
29
30trait MonitoredChannel: Send + Sync + 'static {
31 fn as_channel(&self) -> &zx::Channel;
32}
33
34impl<P: SynchronousProxy + Send + Sync + 'static> MonitoredChannel for P {
35 fn as_channel(&self) -> &zx::Channel {
36 SynchronousProxy::as_channel(self)
37 }
38}
39
40#[derive(Default)]
50pub struct ServiceBindingScope {
51 monitored_channel: Option<Arc<dyn MonitoredChannel>>,
52 teardown_hooks: Vec<Box<dyn FnOnce(&CurrentTask) + Send>>,
53}
54
55impl ServiceBindingScope {
56 pub fn monitor_channel<P: SynchronousProxy + Send + Sync + 'static>(&mut self, proxy: Arc<P>) {
60 self.monitored_channel = Some(proxy);
61 }
62
63 pub fn on_disconnect(&mut self, hook: impl FnOnce(&CurrentTask) + Send + 'static) {
68 self.teardown_hooks.push(Box::new(hook));
69 }
70
71 pub fn add_scoped_device(&mut self, device: Device) -> Device {
75 let device_to_remove = device.clone();
76 self.on_disconnect(move |current_task| {
77 current_task.kernel().device_registry.remove_device(current_task, device_to_remove);
78 });
79 device
80 }
81}
82
83impl std::fmt::Debug for ServiceBindingScope {
84 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
85 f.debug_struct("ServiceBindingScope")
86 .field("has_monitored_channel", &self.monitored_channel.is_some())
87 .field("teardown_hooks_len", &self.teardown_hooks.len())
88 .finish()
89 }
90}
91
92struct InstanceEntry<P> {
93 name: String,
94 proxy: Arc<P>,
95 active: Arc<AtomicBool>,
96}
97
98impl<P> Clone for InstanceEntry<P> {
99 fn clone(&self) -> Self {
100 Self { name: self.name.clone(), proxy: self.proxy.clone(), active: self.active.clone() }
101 }
102}
103
104struct ActiveBinding {
105 instance_active: Arc<AtomicBool>,
106 monitored_channel: Option<Arc<dyn MonitoredChannel>>,
107 _cancel_monitor: Option<futures::channel::oneshot::Sender<()>>,
108 teardown_hooks: Vec<Box<dyn FnOnce(&CurrentTask) + Send>>,
109}
110
111impl ActiveBinding {
112 fn is_stale(&self) -> bool {
113 !self.instance_active.load(Ordering::Acquire)
114 || self
115 .monitored_channel
116 .as_ref()
117 .is_some_and(|ch| ch.as_channel().is_closed().unwrap_or(true))
118 }
119
120 fn teardown(self, current_task: &CurrentTask) {
121 for hook in self.teardown_hooks.into_iter().rev() {
122 hook(current_task);
123 }
124 }
125}
126
127struct DriverState<Bind> {
128 bind: Bind,
129 active_bindings: Vec<ActiveBinding>,
130}
131
132trait ServiceDriver<S: ServiceMarker>: Send + Sync {
133 fn handle_instance<'a>(
134 &'a self,
135 state: &'a Arc<ServiceWatcherState<S>>,
136 weak_kernel: &'a Weak<Kernel>,
137 instance: InstanceEntry<S::Proxy>,
138 ) -> BoxFuture<'a, ()>;
139
140 fn cleanup_stale_bindings<'a>(
141 &'a self,
142 state: &'a Arc<ServiceWatcherState<S>>,
143 weak_kernel: &'a Weak<Kernel>,
144 ) -> BoxFuture<'a, ()>;
145}
146
147struct DriverEntry<S: ServiceMarker, P, Probe, ProbeFut, Bind> {
148 singleton: bool,
149 probe: Probe,
150 state: Mutex<DriverState<Bind>>,
158 _marker: PhantomData<fn(S, P, ProbeFut)>,
159}
160
161impl<S, P, Probe, ProbeFut, Bind> DriverEntry<S, P, Probe, ProbeFut, Bind>
162where
163 S: ServiceMarker<Proxy: Send + Sync + 'static>,
164 P: Send + 'static,
165 Probe: Fn(Arc<S::Proxy>) -> ProbeFut + Send + Sync + 'static,
166 ProbeFut: Future<Output = Option<P>> + Send + 'static,
167 Bind: FnMut(&CurrentTask, P, &mut ServiceBindingScope) + Send + 'static,
168{
169 fn new(singleton: bool, probe: Probe, bind: Bind) -> Arc<Self> {
170 Arc::new(Self {
171 singleton,
172 probe,
173 state: Mutex::new(DriverState { bind, active_bindings: Vec::new() }),
174 _marker: PhantomData,
175 })
176 }
177
178 fn cleanup_stale_bindings_locked(
179 state: &mut DriverState<Bind>,
180 current_task: &CurrentTask,
181 ) -> bool {
182 let mut removed_any = false;
183 let mut i = 0;
184 while i < state.active_bindings.len() {
185 if state.active_bindings[i].is_stale() {
186 let binding = state.active_bindings.swap_remove(i);
187 binding.instance_active.store(false, Ordering::Release);
188 binding.teardown(current_task);
189 removed_any = true;
190 } else {
191 i += 1;
192 }
193 }
194 removed_any
195 }
196
197 async fn cleanup_stale_bindings(
198 &self,
199 watcher_state: &Arc<ServiceWatcherState<S>>,
200 weak_kernel: &Weak<Kernel>,
201 ) {
202 let should_promote = {
203 let Some(kernel) = weak_kernel.upgrade() else {
204 return;
205 };
206 let system_task = kernel.kthreads.system_task();
207 let mut state = self.state.lock();
208 let removed = Self::cleanup_stale_bindings_locked(&mut state, system_task);
209 self.singleton && removed && state.active_bindings.is_empty()
210 };
211 watcher_state.prune_inactive_instances();
212 if should_promote {
213 self.promote_singleton_instance(watcher_state, weak_kernel).await;
214 }
215 }
216
217 async fn promote_singleton_instance(
218 &self,
219 watcher_state: &Arc<ServiceWatcherState<S>>,
220 weak_kernel: &Weak<Kernel>,
221 ) {
222 for candidate in watcher_state.active_instances() {
223 if !self.state.lock().active_bindings.is_empty() {
224 break;
225 }
226 self.probe_and_bind_instance(watcher_state, weak_kernel, candidate).await;
227 }
228 }
229
230 async fn probe_and_bind_instance(
231 &self,
232 watcher_state: &Arc<ServiceWatcherState<S>>,
233 weak_kernel: &Weak<Kernel>,
234 instance: InstanceEntry<S::Proxy>,
235 ) {
236 let Some(probed) = (self.probe)(instance.proxy.clone()).await else {
237 return;
238 };
239 if let Some(kernel) = weak_kernel.upgrade()
240 && instance.active.load(Ordering::Acquire)
241 {
242 self.bind_probed(kernel.kthreads.system_task(), watcher_state, &instance, probed);
243 }
244 }
245
246 fn bind_probed(
247 &self,
248 system_task: &CurrentTask,
249 watcher_state: &Arc<ServiceWatcherState<S>>,
250 instance: &InstanceEntry<S::Proxy>,
251 probed: P,
252 ) {
253 let monitored_channel = {
254 let mut state = self.state.lock();
255
256 Self::cleanup_stale_bindings_locked(&mut state, system_task);
260
261 if !instance.active.load(Ordering::Acquire) {
262 None
263 } else if state
264 .active_bindings
265 .iter()
266 .any(|binding| Arc::ptr_eq(&binding.instance_active, &instance.active))
267 {
268 None
269 } else if self.singleton && !state.active_bindings.is_empty() {
270 log_warn!(
271 "{}: ignoring additional service instance while singleton driver is bound",
272 S::SERVICE_NAME
273 );
274 None
275 } else {
276 let mut scope = ServiceBindingScope::default();
277 (state.bind)(system_task, probed, &mut scope);
278
279 let (cancel_tx, monitored) = match scope.monitored_channel.clone() {
280 Some(channel) => {
281 let (tx, rx) = futures::channel::oneshot::channel();
282 (Some(tx), Some((channel, rx)))
283 }
284 None => (None, None),
285 };
286 state.active_bindings.push(ActiveBinding {
287 instance_active: instance.active.clone(),
288 monitored_channel: scope.monitored_channel,
289 _cancel_monitor: cancel_tx,
290 teardown_hooks: scope.teardown_hooks,
291 });
292 monitored
293 }
294 };
295
296 watcher_state.prune_inactive_instances();
297
298 if let Some((monitored_channel, cancel_rx)) = monitored_channel {
299 let kernel = system_task.kernel();
300 let weak_kernel = kernel.weak_self.clone();
301 let watcher_state = watcher_state.clone();
302 let instance_active = instance.active.clone();
303 kernel.kthreads.spawn_future(
304 move || async move {
305 let peer_closed = {
306 let on_signals = std::pin::pin!(fasync::OnSignals::new(
307 monitored_channel.as_channel(),
308 zx::Signals::CHANNEL_PEER_CLOSED,
309 ));
310 matches!(
311 futures::future::select(on_signals, cancel_rx).await,
312 futures::future::Either::Left(_)
313 )
314 };
315 drop(monitored_channel);
316 if peer_closed {
317 let drivers = watcher_state.deactivate_instance(&instance_active);
318 futures::future::join_all(drivers.into_iter().map(|driver| {
319 let watcher_state = &watcher_state;
320 let weak_kernel = &weak_kernel;
321 async move {
322 driver.cleanup_stale_bindings(watcher_state, weak_kernel).await;
323 }
324 }))
325 .await;
326 }
327 },
328 "service_watcher_disconnect_monitor",
329 );
330 }
331 }
332}
333
334impl<S, P, Probe, ProbeFut, Bind> ServiceDriver<S> for DriverEntry<S, P, Probe, ProbeFut, Bind>
335where
336 S: ServiceMarker<Proxy: Send + Sync + 'static>,
337 P: Send + 'static,
338 Probe: Fn(Arc<S::Proxy>) -> ProbeFut + Send + Sync + 'static,
339 ProbeFut: Future<Output = Option<P>> + Send + 'static,
340 Bind: FnMut(&CurrentTask, P, &mut ServiceBindingScope) + Send + 'static,
341{
342 fn handle_instance<'a>(
343 &'a self,
344 state: &'a Arc<ServiceWatcherState<S>>,
345 weak_kernel: &'a Weak<Kernel>,
346 instance: InstanceEntry<S::Proxy>,
347 ) -> BoxFuture<'a, ()> {
348 Box::pin(async move {
349 self.probe_and_bind_instance(state, weak_kernel, instance).await;
350 DriverEntry::cleanup_stale_bindings(self, state, weak_kernel).await;
351 })
352 }
353
354 fn cleanup_stale_bindings<'a>(
355 &'a self,
356 state: &'a Arc<ServiceWatcherState<S>>,
357 weak_kernel: &'a Weak<Kernel>,
358 ) -> BoxFuture<'a, ()> {
359 Box::pin(DriverEntry::cleanup_stale_bindings(self, state, weak_kernel))
360 }
361}
362
363struct ServiceWatcherInner<S: ServiceMarker> {
364 instances: Vec<InstanceEntry<S::Proxy>>,
365 drivers: Vec<Arc<dyn ServiceDriver<S>>>,
366}
367
368impl<S: ServiceMarker> Default for ServiceWatcherInner<S> {
369 fn default() -> Self {
370 Self { instances: Vec::new(), drivers: Vec::new() }
371 }
372}
373
374struct ServiceWatcherState<S: ServiceMarker> {
375 watch_started: AtomicBool,
376 inner: Mutex<ServiceWatcherInner<S>>,
377}
378
379impl<S: ServiceMarker> Default for ServiceWatcherState<S> {
380 fn default() -> Self {
381 Self {
382 watch_started: AtomicBool::new(false),
383 inner: Mutex::new(ServiceWatcherInner::default()),
384 }
385 }
386}
387
388enum WatchDispatch<S: ServiceMarker> {
389 Bind(InstanceEntry<S::Proxy>, Vec<Arc<dyn ServiceDriver<S>>>),
390 Remove(Vec<Arc<dyn ServiceDriver<S>>>),
391}
392
393impl<S: ServiceMarker<Proxy: Send + Sync + 'static>> ServiceWatcherState<S> {
394 fn deactivate_instance(
395 &self,
396 instance_active: &Arc<AtomicBool>,
397 ) -> Vec<Arc<dyn ServiceDriver<S>>> {
398 instance_active.store(false, Ordering::Release);
399 let mut inner = self.inner.lock();
400 inner.instances.retain(|entry| entry.active.load(Ordering::Acquire));
401 inner.drivers.clone()
402 }
403
404 fn prune_inactive_instances(&self) {
405 self.inner.lock().instances.retain(|entry| entry.active.load(Ordering::Acquire));
406 }
407
408 fn active_instances(&self) -> Vec<InstanceEntry<S::Proxy>> {
409 let mut inner = self.inner.lock();
410 inner.instances.retain(|entry| entry.active.load(Ordering::Acquire));
411 inner.instances.clone()
412 }
413
414 fn record_instance(
415 &self,
416 instance_name: String,
417 proxy: Arc<S::Proxy>,
418 ) -> (InstanceEntry<S::Proxy>, Vec<Arc<dyn ServiceDriver<S>>>) {
419 let mut inner = self.inner.lock();
420 inner.instances.retain(|entry| {
421 if entry.name == instance_name {
422 entry.active.store(false, Ordering::Release);
423 false
424 } else {
425 entry.active.load(Ordering::Acquire)
426 }
427 });
428 let entry =
429 InstanceEntry { name: instance_name, proxy, active: Arc::new(AtomicBool::new(true)) };
430 inner.instances.push(entry.clone());
431 let drivers = inner.drivers.clone();
432 (entry, drivers)
433 }
434
435 fn remove_instance(&self, instance_name: &str) -> Vec<Arc<dyn ServiceDriver<S>>> {
436 let mut inner = self.inner.lock();
437 inner.instances.retain(|entry| {
438 if entry.name == instance_name {
439 entry.active.store(false, Ordering::Release);
440 false
441 } else {
442 entry.active.load(Ordering::Acquire)
443 }
444 });
445 inner.drivers.clone()
446 }
447
448 async fn watch_service_dir(
449 self: &Arc<Self>,
450 weak_kernel: &Weak<Kernel>,
451 service_dir: fio::DirectoryProxy,
452 ) -> Result<(), anyhow::Error> {
453 let watcher = Watcher::new(&service_dir)
454 .await
455 .with_context(|| format!("failed to watch {} service", S::SERVICE_NAME))?;
456 let sync_dir = Arc::new(fio::DirectorySynchronousProxy::new(
457 service_dir
458 .into_channel()
459 .map_err(|_| anyhow::anyhow!("service directory proxy still in use"))?
460 .into_zx_channel(),
461 ));
462 watcher
463 .take_while(|msg| {
464 let keep_going = weak_kernel.strong_count() > 0
465 && !matches!(msg, Ok(WatchMessage { event: WatchEvent::DELETED, .. }));
466 std::future::ready(keep_going)
467 })
468 .try_for_each_concurrent(None, |msg| {
469 let dispatch = msg.filename.to_str().filter(|&name| name != ".").and_then(
470 |filename| match msg.event {
471 WatchEvent::ADD_FILE | WatchEvent::EXISTING => {
472 let instance_name = filename.to_owned();
473 let proxy = Arc::new(S::Proxy::from_member_opener(Box::new(
474 SyncServiceInstanceOpener {
475 service_dir: sync_dir.clone(),
476 instance_name: instance_name.clone(),
477 },
478 )));
479 let (entry, drivers) = self.record_instance(instance_name, proxy);
480 Some(WatchDispatch::Bind(entry, drivers))
481 }
482 WatchEvent::REMOVE_FILE => {
483 Some(WatchDispatch::Remove(self.remove_instance(filename)))
484 }
485 _ => None,
486 },
487 );
488 async move {
489 match dispatch {
490 Some(WatchDispatch::Bind(entry, drivers)) => {
491 let futures = drivers.into_iter().map(|driver| {
492 let entry = entry.clone();
493 async move {
494 driver.handle_instance(self, weak_kernel, entry).await;
495 }
496 });
497 futures::future::join_all(futures).await;
498 }
499 Some(WatchDispatch::Remove(drivers)) => {
500 let futures = drivers.into_iter().map(|driver| async move {
501 driver.cleanup_stale_bindings(self, weak_kernel).await;
502 });
503 futures::future::join_all(futures).await;
504 }
505 None => {}
506 }
507 Ok(())
508 }
509 })
510 .await
511 .with_context(|| format!("error watching {} service", S::SERVICE_NAME))
512 }
513}
514
515#[derive(Debug)]
523pub struct ServiceWatcher<S: ServiceMarker>(PhantomData<S>);
524
525impl<S: ServiceMarker<Proxy: Send + Sync + 'static>> ServiceWatcher<S> {
526 pub fn register<Bind>(kernel: &Kernel, marker: S, bind: Bind)
529 where
530 Bind: FnMut(&CurrentTask, Arc<S::Proxy>, &mut ServiceBindingScope) + Send + 'static,
531 {
532 Self::register_driver(
533 kernel,
534 marker,
535 false,
536 async |proxy| Some(proxy),
537 bind,
538 );
539 }
540
541 pub fn register_singleton<Bind>(kernel: &Kernel, marker: S, bind: Bind)
545 where
546 Bind: FnMut(&CurrentTask, Arc<S::Proxy>, &mut ServiceBindingScope) + Send + 'static,
547 {
548 Self::register_driver(
549 kernel,
550 marker,
551 true,
552 async |proxy| Some(proxy),
553 bind,
554 );
555 }
556
557 pub fn register_with_probe<P, Probe, ProbeFut, Bind>(
568 kernel: &Kernel,
569 marker: S,
570 probe: Probe,
571 bind: Bind,
572 ) where
573 P: Send + 'static,
574 Probe: Fn(Arc<S::Proxy>) -> ProbeFut + Send + Sync + 'static,
575 ProbeFut: Future<Output = Option<P>> + Send + 'static,
576 Bind: FnMut(&CurrentTask, P, &mut ServiceBindingScope) + Send + 'static,
577 {
578 Self::register_driver(kernel, marker, false, probe, bind);
579 }
580
581 fn ensure_watcher_started(kernel: &Kernel) -> Arc<ServiceWatcherState<S>> {
582 let state = kernel.expando.get::<ServiceWatcherState<S>>();
583 if !state.watch_started.swap(true, Ordering::AcqRel) {
584 let weak_kernel = kernel.weak_self.clone();
585 let state_for_task = state.clone();
586 kernel.kthreads.spawn_future(
587 move || async move {
588 let result = match fclient::open_service::<S>() {
592 Ok(service_dir) => {
593 state_for_task.watch_service_dir(&weak_kernel, service_dir).await
594 }
595 Err(e) => Err(e.context("failed to open service directory")),
596 };
597 if let Err(e) = result {
598 log_info!("{}: service unavailable: {e:#}", S::SERVICE_NAME);
599 }
600 },
601 S::SERVICE_NAME,
602 );
603 }
604 state
605 }
606
607 fn register_driver<P, Probe, ProbeFut, Bind>(
608 kernel: &Kernel,
609 _marker: S,
610 singleton: bool,
611 probe: Probe,
612 bind: Bind,
613 ) where
614 P: Send + 'static,
615 Probe: Fn(Arc<S::Proxy>) -> ProbeFut + Send + Sync + 'static,
616 ProbeFut: Future<Output = Option<P>> + Send + 'static,
617 Bind: FnMut(&CurrentTask, P, &mut ServiceBindingScope) + Send + 'static,
618 {
619 let state = Self::ensure_watcher_started(kernel);
620 let driver: Arc<dyn ServiceDriver<S>> = DriverEntry::new(singleton, probe, bind);
621 let existing_instances = {
622 let mut inner = state.inner.lock();
623 inner.instances.retain(|entry| entry.active.load(Ordering::Acquire));
624 let instances = inner.instances.clone();
625 inner.drivers.push(driver.clone());
626 instances
627 };
628 if !existing_instances.is_empty() {
629 let weak_kernel = kernel.weak_self.clone();
630 kernel.kthreads.spawn_future(
631 move || async move {
632 futures::future::join_all(
633 existing_instances
634 .into_iter()
635 .map(|instance| driver.handle_instance(&state, &weak_kernel, instance)),
636 )
637 .await;
638 },
639 "service_watcher_replay",
640 );
641 }
642 }
643}
644
645struct SyncServiceInstanceOpener {
646 service_dir: Arc<fio::DirectorySynchronousProxy>,
647 instance_name: String,
648}
649
650impl MemberOpener for SyncServiceInstanceOpener {
651 fn open_member(&self, member: &str, server_end: zx::Channel) -> Result<(), fidl::Error> {
652 let path = format!("{}/{}", self.instance_name, member);
653 self.service_dir.open(
654 &path,
655 fio::Flags::PROTOCOL_SERVICE,
656 &fio::Options::default(),
657 server_end,
658 )
659 }
660
661 fn instance_name(&self) -> &str {
662 &self.instance_name
663 }
664}
665
666#[cfg(test)]
669pub(crate) fn watch_service_dir_for_testing<S>(kernel: &Kernel, service_dir: fio::DirectoryProxy)
670where
671 S: ServiceMarker<Proxy: Send + Sync + 'static>,
672{
673 let state = kernel.expando.get::<ServiceWatcherState<S>>();
674 state.watch_started.store(true, Ordering::Release);
675 let weak_kernel = kernel.weak_self.clone();
676 kernel.kthreads.spawn_future(
677 move || async move {
678 let _ = state.watch_service_dir(&weak_kernel, service_dir).await;
679 },
680 "service_watcher_test_dir",
681 );
682}
683
684#[cfg(test)]
685mod tests {
686 use super::*;
687 use crate::device::mem::DevNull;
688 use crate::device::{DeviceMode, simple_device_ops};
689 use crate::testing::spawn_kernel_and_run;
690 use fidl_fuchsia_hardware_power_stats as fpowerstats;
691 use futures::channel::{mpsc, oneshot};
692 use starnix_uapi::error;
693 use std::collections::HashSet;
694 use vfs::directory::helper::DirectlyMutable as _;
695 use vfs::directory::immutable::Simple;
696
697 fn start_test_service_dir(kernel: &Kernel) -> Arc<Simple> {
698 let dir = vfs::directory::immutable::simple();
699 let proxy = vfs::directory::serve_read_only(dir.clone(), vfs::ExecutionScope::new());
700 watch_service_dir_for_testing::<fpowerstats::ServiceMarker>(kernel, proxy);
701 dir
702 }
703
704 fn add_instance(
705 dir: &Arc<Simple>,
706 name: &str,
707 on_open: impl Fn(zx::Channel) + Send + Sync + 'static,
708 ) {
709 let instance_dir = vfs::pseudo_directory! {
710 "device" => vfs::service::endpoint(move |_scope, channel| {
711 on_open(channel.into_zx_channel());
712 }),
713 };
714 dir.add_entry_may_overwrite(name, instance_dir, true)
715 .expect("add service instance");
716 }
717
718 #[::fuchsia::test]
719 async fn watches_multiple_instances_concurrently() {
720 spawn_kernel_and_run(async |current_task| {
721 let kernel = current_task.kernel();
722 let dir = vfs::directory::immutable::simple();
723 add_instance(&dir, "first", |_| {});
724 add_instance(&dir, "second", |_| {});
725
726 let (second_tx, second_rx) = oneshot::channel();
727 let second_tx = Arc::new(Mutex::new(Some(second_tx)));
728 let second_rx = Arc::new(Mutex::new(Some(second_rx)));
729 let (done_tx, done_rx) = oneshot::channel();
730 let done_tx = Arc::new(Mutex::new(Some(done_tx)));
731 let seen = Arc::new(Mutex::new(HashSet::new()));
732
733 ServiceWatcher::register_with_probe(
734 kernel,
735 fpowerstats::ServiceMarker,
736 move |instance| {
737 let second_tx = second_tx.clone();
738 let second_rx = second_rx.clone();
739 async move {
740 let name = instance.instance_name().to_string();
741 if name == "first" {
742 let rx = second_rx.lock().take().expect("second_rx taken once");
745 let _ = rx.await;
746 } else if name == "second" {
747 if let Some(tx) = second_tx.lock().take() {
748 let _ = tx.send(());
749 }
750 }
751 Some(name)
752 }
753 },
754 move |_current_task, name, _scope| {
755 let mut seen = seen.lock();
756 seen.insert(name);
757 if seen.len() == 2
758 && let Some(tx) = done_tx.lock().take()
759 {
760 let _ = tx.send(std::mem::take(&mut *seen));
761 }
762 },
763 );
764
765 let proxy = vfs::directory::serve_read_only(dir, vfs::ExecutionScope::new());
766 watch_service_dir_for_testing::<fpowerstats::ServiceMarker>(kernel, proxy);
767
768 let seen = done_rx.await.expect("both instances discovered concurrently");
769 assert_eq!(seen, HashSet::from(["first".to_string(), "second".to_string()]));
770 })
771 .await;
772 }
773
774 #[::fuchsia::test]
775 async fn fans_out_to_multiple_drivers_and_replays_to_late_subscribers() {
776 spawn_kernel_and_run(async |current_task| {
777 let kernel = current_task.kernel();
778 let dir = start_test_service_dir(kernel);
779
780 let (early_tx, mut early_rx) = mpsc::unbounded();
781 ServiceWatcher::register(
782 kernel,
783 fpowerstats::ServiceMarker,
784 move |_current_task, instance, _scope| {
785 let _ = early_tx.unbounded_send(instance.instance_name().to_string());
786 },
787 );
788
789 add_instance(&dir, "first", |_| {});
790 assert_eq!(early_rx.next().await.as_deref(), Some("first"));
791
792 let (late_tx, mut late_rx) = mpsc::unbounded();
794 ServiceWatcher::register(
795 kernel,
796 fpowerstats::ServiceMarker,
797 move |_current_task, instance, _scope| {
798 let _ = late_tx.unbounded_send(instance.instance_name().to_string());
799 },
800 );
801 assert_eq!(late_rx.next().await.as_deref(), Some("first"));
802
803 add_instance(&dir, "second", |_| {});
805 assert_eq!(early_rx.next().await.as_deref(), Some("second"));
806 assert_eq!(late_rx.next().await.as_deref(), Some("second"));
807 })
808 .await;
809 }
810
811 #[::fuchsia::test]
812 async fn tears_down_scoped_devices_and_hooks_on_peer_close() {
813 spawn_kernel_and_run(async |current_task| {
814 let kernel = current_task.kernel();
815 let dir = start_test_service_dir(kernel);
816 let (event_tx, mut event_rx) = mpsc::unbounded();
817 let (all_seen_tx, mut all_seen_rx) = mpsc::unbounded();
818 let devts = Arc::new(Mutex::new(Vec::new()));
819 let devts_for_bind = devts.clone();
820
821 ServiceWatcher::register_singleton(
822 kernel,
823 fpowerstats::ServiceMarker,
824 move |current_task, instance, scope| {
825 let name = instance.instance_name().to_string();
826 let proxy =
827 Arc::new(instance.connect_to_device_sync().expect("connect to device"));
828 scope.monitor_channel(proxy);
829
830 let tx1 = event_tx.clone();
831 let name1 = name.clone();
832 scope.on_disconnect(move |_| {
833 let _ = tx1.unbounded_send(format!("hook1:{name1}"));
834 });
835 let tx2 = event_tx.clone();
836 let name2 = name.clone();
837 scope.on_disconnect(move |_| {
838 let _ = tx2.unbounded_send(format!("hook2:{name2}"));
839 });
840
841 let kernel = current_task.kernel();
842 let misc_dev = scope.add_scoped_device(
843 kernel
844 .device_registry
845 .add_misc_device(
846 kernel,
847 "watcher_misc".into(),
848 simple_device_ops::<DevNull>,
849 )
850 .expect("add misc device"),
851 );
852 devts_for_bind.lock().push(misc_dev.metadata().expect("metadata").devt);
853 let _ = event_tx.unbounded_send(format!("bind:{name}"));
854 },
855 );
856 ServiceWatcher::register(
857 kernel,
858 fpowerstats::ServiceMarker,
859 move |_current_task, instance, _scope| {
860 let _ = all_seen_tx.unbounded_send(instance.instance_name().to_string());
861 },
862 );
863
864 let (first_srv_tx, mut first_srv_rx) = mpsc::unbounded();
865 add_instance(&dir, "first", move |ch| {
866 let _ = first_srv_tx.unbounded_send(ch);
867 });
868 assert_eq!(event_rx.next().await.as_deref(), Some("bind:first"));
869 assert_eq!(all_seen_rx.next().await.as_deref(), Some("first"));
870 let first_server = first_srv_rx.next().await.expect("first server channel");
871
872 let registry = &kernel.device_registry;
873 let first_devt = devts.lock()[0];
874 assert!(
875 registry.objects.root.lookup("devices/virtual/misc/watcher_misc".into()).is_some()
876 );
877 assert!(registry.get_device(first_devt, DeviceMode::Char).is_ok());
878
879 let (second_srv_tx, mut second_srv_rx) = mpsc::unbounded();
881 add_instance(&dir, "second", move |ch| {
882 let _ = second_srv_tx.unbounded_send(ch);
883 });
884 assert_eq!(all_seen_rx.next().await.as_deref(), Some("second"));
885 assert!(event_rx.try_recv().is_err());
886
887 drop(first_server);
890 assert_eq!(event_rx.next().await.as_deref(), Some("hook2:first"));
891 assert_eq!(event_rx.next().await.as_deref(), Some("hook1:first"));
892 assert_eq!(event_rx.next().await.as_deref(), Some("bind:second"));
893 let second_server = second_srv_rx.next().await.expect("second server channel");
894 assert_eq!(devts.lock()[1], first_devt);
895
896 drop(second_server);
898 assert_eq!(event_rx.next().await.as_deref(), Some("hook2:second"));
899 assert_eq!(event_rx.next().await.as_deref(), Some("hook1:second"));
900 assert!(
901 registry.objects.root.lookup("devices/virtual/misc/watcher_misc".into()).is_none()
902 );
903 assert_eq!(
904 registry.get_device(first_devt, DeviceMode::Char).map(|_| ()),
905 error!(ENODEV)
906 );
907 })
908 .await;
909 }
910
911 #[::fuchsia::test]
912 async fn tears_down_binding_on_directory_remove_or_replace() {
913 spawn_kernel_and_run(async |current_task| {
914 let kernel = current_task.kernel();
915 let dir = start_test_service_dir(kernel);
916 let (event_tx, mut event_rx) = mpsc::unbounded();
917 let mut generation = 0u32;
918
919 ServiceWatcher::register(
920 kernel,
921 fpowerstats::ServiceMarker,
922 move |current_task, _instance, scope| {
923 generation += 1;
924 let current_gen = generation;
925 let tx_disconnect = event_tx.clone();
926 scope.on_disconnect(move |_| {
927 let _ = tx_disconnect.unbounded_send(format!("disconnect:{current_gen}"));
928 });
929 let kernel = current_task.kernel();
930 scope.add_scoped_device(
931 kernel
932 .device_registry
933 .add_misc_device(
934 kernel,
935 "watcher_replace_misc".into(),
936 simple_device_ops::<DevNull>,
937 )
938 .expect("add misc device"),
939 );
940 let _ = event_tx.unbounded_send(format!("bind:{current_gen}"));
941 },
942 );
943
944 add_instance(&dir, "first", |_| {});
945 assert_eq!(event_rx.next().await.as_deref(), Some("bind:1"));
946 let root = &kernel.device_registry.objects.root;
947 assert!(root.lookup("devices/virtual/misc/watcher_replace_misc".into()).is_some());
948
949 add_instance(&dir, "first", |_| {});
952 assert_eq!(event_rx.next().await.as_deref(), Some("disconnect:1"));
953 assert_eq!(event_rx.next().await.as_deref(), Some("bind:2"));
954 assert!(root.lookup("devices/virtual/misc/watcher_replace_misc".into()).is_some());
955
956 dir.remove_entry("first", false)
958 .expect("remove service instance");
959 assert_eq!(event_rx.next().await.as_deref(), Some("disconnect:2"));
960 assert!(root.lookup("devices/virtual/misc/watcher_replace_misc".into()).is_none());
961 })
962 .await;
963 }
964
965 #[::fuchsia::test]
966 async fn stops_watching_when_kernel_is_dropped() {
967 let dir = vfs::directory::immutable::simple();
968 add_instance(&dir, "first", |_| {});
969 let service_dir = vfs::directory::serve_read_only(dir, vfs::ExecutionScope::new());
970 let state = Arc::new(ServiceWatcherState::<fpowerstats::ServiceMarker>::default());
971 let called = Arc::new(AtomicBool::new(false));
972 let called_for_bind = called.clone();
973 state.inner.lock().drivers.push(DriverEntry::new(
974 false,
975 async |_| Some(()),
976 move |_, (), _| {
977 called_for_bind.store(true, Ordering::SeqCst);
978 },
979 ));
980
981 state
982 .watch_service_dir(&Weak::new(), service_dir)
983 .await
984 .expect("service watcher should exit cleanly when kernel is dropped");
985
986 assert!(!called.load(Ordering::SeqCst));
987 }
988}