Skip to main content

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}