zerodds_dcps_async/factory.rs
1// SPDX-License-Identifier: Apache-2.0
2// Copyright 2026 ZeroDDS Contributors
3//! AsyncDomainParticipantFactory — singleton wrapper.
4
5use zerodds_dcps::{DomainParticipantFactory, DomainParticipantQos, Result};
6
7use crate::AsyncDomainParticipant;
8
9/// Async wrapper around the sync singleton. `instance()` always returns
10/// the same singleton; the create_participant methods share state
11/// with the sync API.
12pub struct AsyncDomainParticipantFactory {
13 inner: &'static DomainParticipantFactory,
14}
15
16impl AsyncDomainParticipantFactory {
17 /// Singleton access.
18 #[must_use]
19 pub fn instance() -> Self {
20 Self {
21 inner: DomainParticipantFactory::instance(),
22 }
23 }
24
25 /// Offline participant (no UDP bind). Spec §1.1.
26 #[must_use]
27 pub fn create_participant_offline(&self, domain_id: i32) -> AsyncDomainParticipant {
28 let p = self
29 .inner
30 .create_participant_offline(domain_id, DomainParticipantQos::default());
31 AsyncDomainParticipant::from_sync(p)
32 }
33
34 /// Live participant (UDP + SPDP/SEDP).
35 ///
36 /// # Errors
37 /// As `DomainParticipantFactory::create_participant`.
38 pub fn create_participant(&self, domain_id: i32) -> Result<AsyncDomainParticipant> {
39 let p = self
40 .inner
41 .create_participant(domain_id, DomainParticipantQos::default())?;
42 Ok(AsyncDomainParticipant::from_sync(p))
43 }
44
45 /// Like `create_participant` but with custom QoS.
46 ///
47 /// # Errors
48 /// As `DomainParticipantFactory::create_participant`.
49 pub fn create_participant_with_qos(
50 &self,
51 domain_id: i32,
52 qos: DomainParticipantQos,
53 ) -> Result<AsyncDomainParticipant> {
54 let p = self.inner.create_participant(domain_id, qos)?;
55 Ok(AsyncDomainParticipant::from_sync(p))
56 }
57}
58
59/// zerodds-async-1.0 §4 — Tokio-Glue. With `--features tokio-glue` the factory
60/// gains `spawn_in_tokio`, which runs a participant's periodic DDS tick loop on
61/// a tokio runtime instead of a dedicated `std::thread`.
62#[cfg(feature = "tokio-glue")]
63impl AsyncDomainParticipantFactory {
64 /// Creates a live participant whose periodic tick loop (SPDP announce,
65 /// SEDP/WLP, deadline/lifespan/liveliness) runs as a task on the given
66 /// tokio runtime instead of the dedicated `zdds-tick` `std::thread` —
67 /// saving one thread per participant. With many participants on one tokio
68 /// runtime their tick loops multiplex onto the tokio worker pool. The recv
69 /// worker threads are unaffected: they block on socket recv and stay.
70 ///
71 /// The spawned task observes shutdown via the runtime stop flag and returns
72 /// when the participant is dropped — no explicit join needed.
73 ///
74 /// # Errors
75 /// As [`zerodds_dcps::DomainParticipantFactory::create_participant_with_config`].
76 pub fn spawn_in_tokio(
77 &self,
78 domain_id: i32,
79 handle: &tokio::runtime::Handle,
80 ) -> Result<AsyncDomainParticipant> {
81 self.spawn_in_tokio_with_qos(domain_id, DomainParticipantQos::default(), handle)
82 }
83
84 /// Like [`Self::spawn_in_tokio`] but with explicit participant QoS.
85 ///
86 /// # Errors
87 /// As [`zerodds_dcps::DomainParticipantFactory::create_participant_with_config`].
88 pub fn spawn_in_tokio_with_qos(
89 &self,
90 domain_id: i32,
91 qos: DomainParticipantQos,
92 handle: &tokio::runtime::Handle,
93 ) -> Result<AsyncDomainParticipant> {
94 let config = zerodds_dcps::runtime::RuntimeConfig {
95 user_data: qos.user_data.value.clone(),
96 external_tick: true,
97 ..zerodds_dcps::runtime::RuntimeConfig::default()
98 };
99 let p = self
100 .inner
101 .create_participant_with_config(domain_id, qos, config)?;
102 // Drive the suppressed tick loop on the tokio runtime. `tick_period`
103 // mirrors the internal thread's cadence; the loop self-terminates once
104 // the runtime is shutting down.
105 if let Some(rt) = p.runtime() {
106 let mut driver = rt.tick_driver();
107 let period = driver.tick_period();
108 handle.spawn(async move {
109 while !driver.is_stopped() {
110 driver.tick();
111 tokio::time::sleep(period).await;
112 }
113 });
114 }
115 Ok(AsyncDomainParticipant::from_sync(p))
116 }
117}
118
119impl Default for AsyncDomainParticipantFactory {
120 fn default() -> Self {
121 Self::instance()
122 }
123}