1use crate::token_registry::TokenRegistry;
20
21use fuchsia_async::{JoinHandle, Scope, ScopeHandle, SpawnableFuture};
22use fuchsia_sync::{MappedMutexGuard, Mutex, MutexGuard};
23use futures::Future;
24use futures::task::{self, Poll};
25use std::future::poll_fn;
26use std::pin::Pin;
27use std::sync::{Arc, Weak};
28use std::task::Context;
29
30#[cfg(target_os = "fuchsia")]
31use fuchsia_async::EHandle;
32
33pub use fuchsia_async::scope::ScopeActiveGuard as ActiveGuard;
34
35pub type SpawnError = task::SpawnError;
36
37#[derive(Clone)]
48pub struct ExecutionScope {
49 executor: Arc<Executor>,
50
51 #[cfg(feature = "fdomain")]
54 client: Arc<flex_client::Client>,
55}
56
57struct Executor {
58 token_registry: TokenRegistry,
59 scope: Mutex<Option<Scope>>,
60}
61
62impl ExecutionScope {
63 pub fn new(#[cfg(feature = "fdomain")] client: Arc<flex_client::Client>) -> Self {
66 Self::build().new(
67 #[cfg(feature = "fdomain")]
68 client,
69 )
70 }
71
72 #[cfg(feature = "fdomain")]
74 pub fn domain(&self) -> Arc<flex_client::Client> {
75 Arc::clone(&self.client)
76 }
77
78 #[cfg(not(feature = "fdomain"))]
80 pub fn domain(&self) -> fidl::endpoints::ZirconClient {
81 fidl::endpoints::ZirconClient
82 }
83
84 pub fn build() -> ExecutionScopeParams {
88 ExecutionScopeParams::default()
89 }
90
91 pub fn as_weak(&self) -> WeakExecutionScope {
92 WeakExecutionScope {
93 executor: Arc::downgrade(&self.executor),
94 #[cfg(feature = "fdomain")]
95 client: Arc::downgrade(&self.client),
96 }
97 }
98
99 pub fn spawn(&self, task: impl Future<Output = ()> + Send + 'static) -> JoinHandle<()> {
111 self.executor.scope().spawn(task)
112 }
113
114 pub fn spawn_local(&self, task: impl Future<Output = ()> + 'static) -> JoinHandle<()> {
121 self.executor.scope().spawn_local(task)
122 }
123
124 pub fn new_task(self, task: impl Future<Output = ()> + Send + 'static) -> Task {
126 Task(self.executor, SpawnableFuture::new(task))
127 }
128
129 pub fn token_registry(&self) -> &TokenRegistry {
130 &self.executor.token_registry
131 }
132
133 pub fn shutdown(&self) {
134 self.executor.shutdown();
135 }
136
137 pub fn force_shutdown(&self) {
139 let _ = self.executor.scope().clone().abort();
140 }
141
142 pub fn resurrect(&self) {
145 *self.executor.scope.lock() = None;
148 }
149
150 pub async fn wait(&self) {
152 let scope = self.executor.scope().clone();
153 scope.on_no_tasks_and_guards().await;
154 }
155
156 pub fn try_active_guard(&self) -> Option<ActiveGuard> {
159 self.executor.scope().active_guard()
160 }
161}
162
163impl PartialEq for ExecutionScope {
164 fn eq(&self, other: &Self) -> bool {
165 Arc::as_ptr(&self.executor) == Arc::as_ptr(&other.executor)
166 }
167}
168
169impl Eq for ExecutionScope {}
170
171impl std::fmt::Debug for ExecutionScope {
172 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
173 f.write_fmt(format_args!("ExecutionScope {:?}", Arc::as_ptr(&self.executor)))
174 }
175}
176
177#[derive(Default)]
178pub struct ExecutionScopeParams {
179 #[cfg(target_os = "fuchsia")]
180 async_executor: Option<EHandle>,
181}
182
183impl ExecutionScopeParams {
184 #[cfg(target_os = "fuchsia")]
185 pub fn executor(mut self, value: EHandle) -> Self {
186 assert!(self.async_executor.is_none(), "`executor` is already set");
187 self.async_executor = Some(value);
188 self
189 }
190
191 pub fn new(
192 self,
193 #[cfg(feature = "fdomain")] client: Arc<flex_client::Client>,
194 ) -> ExecutionScope {
195 ExecutionScope {
196 executor: Arc::new(Executor {
197 token_registry: TokenRegistry::new(),
198 #[cfg(target_os = "fuchsia")]
199 scope: self.async_executor.map_or_else(
200 || Mutex::new(None),
201 |e| Mutex::new(Some(e.global_scope().new_child())),
202 ),
203 #[cfg(not(target_os = "fuchsia"))]
204 scope: Mutex::new(None),
205 }),
206 #[cfg(feature = "fdomain")]
207 client,
208 }
209 }
210}
211
212#[derive(Clone)]
215pub struct WeakExecutionScope {
216 executor: Weak<Executor>,
217 #[cfg(feature = "fdomain")]
218 client: Weak<flex_client::Client>,
219}
220
221impl WeakExecutionScope {
222 pub fn spawn(&self, task: impl Future<Output = ()> + Send + 'static) {
225 let executor = self.executor.upgrade();
226 if let Some(executor) = executor {
227 _ = executor.scope().spawn(task)
228 }
229 }
230
231 #[cfg(feature = "fdomain")]
233 pub fn domain(&self) -> Option<Arc<flex_client::Client>> {
234 self.client.upgrade()
235 }
236
237 #[cfg(not(feature = "fdomain"))]
238 pub fn domain(&self) -> Option<fidl::endpoints::ZirconClient> {
239 Some(fidl::endpoints::ZirconClient)
240 }
241}
242
243impl Executor {
244 fn scope(&self) -> MappedMutexGuard<'_, Scope> {
245 MutexGuard::map(self.scope.lock(), |s| {
249 s.get_or_insert_with(|| {
250 #[cfg(target_os = "fuchsia")]
251 return Scope::global().new_child();
252 #[cfg(not(target_os = "fuchsia"))]
253 return Scope::new();
254 })
255 })
256 }
257
258 fn shutdown(&self) {
259 if let Some(scope) = &*self.scope.lock() {
260 scope.wake_all_with_active_guard();
261 let _ = ScopeHandle::clone(&*scope).cancel();
262 }
263 }
264}
265
266impl Drop for Executor {
267 fn drop(&mut self) {
268 self.shutdown();
269 if let Some(scope) = self.scope.get_mut().take() {
272 scope.detach();
273 }
274 }
275}
276
277pub async fn yield_to_executor() {
279 let mut done = false;
280 poll_fn(|cx| {
281 if done {
282 Poll::Ready(())
283 } else {
284 done = true;
285 cx.waker().wake_by_ref();
286 Poll::Pending
287 }
288 })
289 .await;
290}
291
292pub struct Task(Arc<Executor>, SpawnableFuture<'static, ()>);
293
294impl Task {
295 pub fn spawn(self) {
297 self.0.scope().spawn(self.1);
298 }
299}
300
301impl Future for Task {
302 type Output = ();
303
304 fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
305 Pin::new(&mut &mut self.1).poll(cx)
306 }
307}
308
309#[cfg(test)]
310mod tests {
311 use super::{ExecutionScope, yield_to_executor};
312
313 use fuchsia_async::{TestExecutor, Timer};
314 use futures::Future;
315 use futures::channel::oneshot;
316 use std::pin::pin;
317 use std::sync::Arc;
318 #[cfg(target_os = "fuchsia")]
319 use std::sync::atomic::{AtomicBool, Ordering};
320 #[cfg(target_os = "fuchsia")]
321 use std::task::Poll;
322 use std::time::Duration;
323
324 #[cfg(target_os = "fuchsia")]
325 fn run_test<GetTest, GetTestRes>(get_test: GetTest)
326 where
327 GetTest: FnOnce(ExecutionScope) -> GetTestRes,
328 GetTestRes: Future<Output = ()>,
329 {
330 let mut exec = TestExecutor::new();
331
332 #[cfg(feature = "fdomain")]
333 let scope = crate::execution_scope::ExecutionScope::new(flex_local::local_client_empty());
334 #[cfg(not(feature = "fdomain"))]
335 let scope = crate::execution_scope::ExecutionScope::new();
336
337 let test = get_test(scope);
338
339 assert_eq!(
340 exec.run_until_stalled(&mut pin!(test)),
341 Poll::Ready(()),
342 "Test did not complete"
343 );
344 }
345
346 #[cfg(not(target_os = "fuchsia"))]
347 fn run_test<GetTest, GetTestRes>(get_test: GetTest)
348 where
349 GetTest: FnOnce(ExecutionScope) -> GetTestRes,
350 GetTestRes: Future<Output = ()>,
351 {
352 use fuchsia_async::TimeoutExt;
353 let mut exec = TestExecutor::new();
354
355 #[cfg(feature = "fdomain")]
356 let scope = crate::execution_scope::ExecutionScope::new(flex_local::local_client_empty());
357 #[cfg(not(feature = "fdomain"))]
358 let scope = crate::execution_scope::ExecutionScope::new();
359
360 let test =
364 get_test(scope).on_stalled(Duration::from_secs(30), || panic!("Test did not complete"));
365
366 exec.run_singlethreaded(&mut pin!(test));
367 }
368
369 #[test]
370 fn simple() {
371 run_test(|scope| {
372 async move {
373 let (sender, receiver) = oneshot::channel();
374 let (counters, task) = mocks::ImmediateTask::new(sender);
375
376 scope.spawn(task);
377
378 receiver.await.unwrap();
380
381 assert_eq!(counters.drop_call(), 1);
382 assert_eq!(counters.poll_call(), 1);
383 }
384 });
385 }
386
387 #[test]
388 fn simple_drop() {
389 run_test(|scope| {
390 async move {
391 let (poll_sender, poll_receiver) = oneshot::channel();
392 let (processing_done_sender, processing_done_receiver) = oneshot::channel();
393 let (drop_sender, drop_receiver) = oneshot::channel();
394 let (counters, task) =
395 mocks::ControlledTask::new(poll_sender, processing_done_receiver, drop_sender);
396
397 scope.spawn(task);
398
399 poll_receiver.await.unwrap();
400
401 processing_done_sender.send(()).unwrap();
402
403 scope.shutdown();
404
405 drop_receiver.await.unwrap();
406
407 let poll_count = counters.poll_call();
410 assert!(poll_count >= 1, "poll was not called");
411
412 assert_eq!(counters.drop_call(), 1);
413 }
414 });
415 }
416
417 #[test]
418 fn test_wait_waits_for_tasks_to_finish() {
419 let mut executor = TestExecutor::new();
420 #[cfg(feature = "fdomain")]
421 let scope = crate::execution_scope::ExecutionScope::new(flex_local::local_client_empty());
422 #[cfg(not(feature = "fdomain"))]
423 let scope = crate::execution_scope::ExecutionScope::new();
424 executor.run_singlethreaded(async {
425 let (poll_sender, poll_receiver) = oneshot::channel();
426 let (processing_done_sender, processing_done_receiver) = oneshot::channel();
427 let (drop_sender, _drop_receiver) = oneshot::channel();
428 let (_, task) =
429 mocks::ControlledTask::new(poll_sender, processing_done_receiver, drop_sender);
430
431 scope.spawn(task);
432
433 poll_receiver.await.unwrap();
434
435 let done = fuchsia_sync::Mutex::new(false);
438 futures::join!(
439 async {
440 scope.wait().await;
441 assert_eq!(*done.lock(), true);
442 },
443 async {
444 Timer::new(Duration::from_millis(100)).await;
446 *done.lock() = true;
447 processing_done_sender.send(()).unwrap();
448 }
449 );
450 });
451 }
452
453 #[cfg(target_os = "fuchsia")]
454 #[fuchsia::test]
455 async fn test_shutdown_waits_for_channels() {
456 use fuchsia_async as fasync;
457
458 #[cfg(feature = "fdomain")]
459 let scope = crate::execution_scope::ExecutionScope::new(flex_local::local_client_empty());
460 #[cfg(not(feature = "fdomain"))]
461 let scope = crate::execution_scope::ExecutionScope::new();
462 let (rx, tx) = zx::Channel::create();
463 let received_msg = Arc::new(AtomicBool::new(false));
464 let (sender, receiver) = futures::channel::oneshot::channel();
465 {
466 let received_msg = received_msg.clone();
467 scope.spawn(async move {
468 let mut msg_buf = zx::MessageBuf::new();
469 msg_buf.ensure_capacity_bytes(64);
470 let _ = sender.send(());
471 let _ = fasync::Channel::from_channel(rx).recv_msg(&mut msg_buf).await;
472 received_msg.store(true, Ordering::Relaxed);
473 });
474 }
475 let _ = receiver.await;
477
478 tx.write(b"hello", &mut []).expect("write failed");
479 scope.shutdown();
480 scope.wait().await;
481 assert!(received_msg.load(Ordering::Relaxed));
482 }
483
484 #[fuchsia::test]
485 async fn test_force_shutdown() {
486 #[cfg(feature = "fdomain")]
487 let scope = crate::execution_scope::ExecutionScope::new(flex_local::local_client_empty());
488 #[cfg(not(feature = "fdomain"))]
489 let scope = crate::execution_scope::ExecutionScope::new();
490 let scope_clone = scope.clone();
491 let ref_count = Arc::new(());
492 let ref_count_clone = ref_count.clone();
493
494 scope.spawn(async move {
497 let _ref_count_clone = ref_count_clone;
498
499 let _guard = scope_clone.try_active_guard().unwrap();
501
502 let _: () = std::future::pending().await;
503 });
504
505 scope.force_shutdown();
506 scope.wait().await;
507
508 assert_eq!(Arc::strong_count(&ref_count), 1);
510
511 scope.resurrect();
513
514 let ref_count_clone = ref_count.clone();
515 scope.spawn(async move {
516 yield_to_executor().await;
518
519 let _ref_count = ref_count_clone.clone();
521
522 let _: () = std::future::pending().await;
523 });
524
525 while Arc::strong_count(&ref_count) != 3 {
526 yield_to_executor().await;
527 }
528
529 for _ in 0..5 {
531 yield_to_executor().await;
532 assert_eq!(Arc::strong_count(&ref_count), 3);
533 }
534 }
535
536 mod mocks {
537 use futures::Future;
538 use futures::channel::oneshot;
539 use futures::task::{Context, Poll};
540 use std::pin::Pin;
541 use std::sync::Arc;
542 use std::sync::atomic::{AtomicUsize, Ordering};
543
544 pub(super) struct TaskCounters {
545 poll_call_count: Arc<AtomicUsize>,
546 drop_call_count: Arc<AtomicUsize>,
547 }
548
549 impl TaskCounters {
550 fn new() -> (Arc<AtomicUsize>, Arc<AtomicUsize>, Self) {
551 let poll_call_count = Arc::new(AtomicUsize::new(0));
552 let drop_call_count = Arc::new(AtomicUsize::new(0));
553
554 (
555 poll_call_count.clone(),
556 drop_call_count.clone(),
557 Self { poll_call_count, drop_call_count },
558 )
559 }
560
561 pub(super) fn poll_call(&self) -> usize {
562 self.poll_call_count.load(Ordering::Relaxed)
563 }
564
565 pub(super) fn drop_call(&self) -> usize {
566 self.drop_call_count.load(Ordering::Relaxed)
567 }
568 }
569
570 pub(super) struct ImmediateTask {
571 poll_call_count: Arc<AtomicUsize>,
572 drop_call_count: Arc<AtomicUsize>,
573 done_sender: Option<oneshot::Sender<()>>,
574 }
575
576 impl ImmediateTask {
577 pub(super) fn new(done_sender: oneshot::Sender<()>) -> (TaskCounters, Self) {
578 let (poll_call_count, drop_call_count, counters) = TaskCounters::new();
579 (
580 counters,
581 Self { poll_call_count, drop_call_count, done_sender: Some(done_sender) },
582 )
583 }
584 }
585
586 impl Future for ImmediateTask {
587 type Output = ();
588
589 fn poll(mut self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll<Self::Output> {
590 self.poll_call_count.fetch_add(1, Ordering::Relaxed);
591
592 if let Some(sender) = self.done_sender.take() {
593 sender.send(()).unwrap();
594 }
595
596 Poll::Ready(())
597 }
598 }
599
600 impl Drop for ImmediateTask {
601 fn drop(&mut self) {
602 self.drop_call_count.fetch_add(1, Ordering::Relaxed);
603 }
604 }
605
606 impl Unpin for ImmediateTask {}
607
608 pub(super) struct ControlledTask {
609 poll_call_count: Arc<AtomicUsize>,
610 drop_call_count: Arc<AtomicUsize>,
611
612 drop_sender: Option<oneshot::Sender<()>>,
613 future: Pin<Box<dyn Future<Output = ()> + Send>>,
614 }
615
616 impl ControlledTask {
617 pub(super) fn new(
618 poll_sender: oneshot::Sender<()>,
619 processing_complete: oneshot::Receiver<()>,
620 drop_sender: oneshot::Sender<()>,
621 ) -> (TaskCounters, Self) {
622 let (poll_call_count, drop_call_count, counters) = TaskCounters::new();
623 (
624 counters,
625 Self {
626 poll_call_count,
627 drop_call_count,
628 drop_sender: Some(drop_sender),
629 future: Box::pin(async move {
630 poll_sender.send(()).unwrap();
631 processing_complete.await.unwrap();
632 }),
633 },
634 )
635 }
636 }
637
638 impl Future for ControlledTask {
639 type Output = ();
640
641 fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
642 self.poll_call_count.fetch_add(1, Ordering::Relaxed);
643 self.future.as_mut().poll(cx)
644 }
645 }
646
647 impl Drop for ControlledTask {
648 fn drop(&mut self) {
649 self.drop_call_count.fetch_add(1, Ordering::Relaxed);
650 self.drop_sender.take().unwrap().send(()).unwrap();
651 }
652 }
653 }
654}