1extern crate alloc;
18use alloc::boxed::Box;
19use alloc::string::ToString;
20use alloc::sync::Arc;
21use alloc::vec::Vec;
22use core::marker::PhantomData;
23
24#[cfg(feature = "std")]
25use std::sync::Mutex;
26#[cfg(feature = "std")]
27use std::sync::mpsc;
28
29use crate::dds_type::DdsType;
30use crate::entity::StatusMask;
31use crate::error::{DdsError, Result};
32#[cfg(feature = "std")]
33use crate::instance_handle::{HANDLE_NIL, InstanceHandle};
34#[cfg(feature = "std")]
35use crate::instance_tracker::InstanceTracker;
36use crate::listener::{ArcDataReaderListener, ArcSubscriberListener};
37use crate::qos::{DataReaderQos, SubscriberQos};
38#[cfg(feature = "std")]
39use crate::sample::Sample;
40#[cfg(feature = "std")]
41use crate::sample_info::{
42 InstanceStateKind, SampleInfo, SampleStateKind, ViewStateKind, instance_state_mask,
43 sample_state_mask, view_state_mask,
44};
45#[cfg(feature = "std")]
46use crate::time::{Time, get_current_time};
47use crate::topic::Topic;
48
49#[cfg(feature = "std")]
50use crate::runtime::DcpsRuntime;
51#[cfg(feature = "std")]
52use zerodds_qos::ReliabilityKind;
53#[cfg(feature = "std")]
54use zerodds_rtps::wire_types::EntityId;
55
56#[inline]
70fn decode_for_encap<T: DdsType>(
71 bytes: &[u8],
72 representation: u8,
73 big_endian: bool,
74) -> core::result::Result<T, crate::dds_type::DecodeError> {
75 match (representation, big_endian) {
76 (0, false) => T::decode_xcdr1(bytes),
77 (0, true) => T::decode_xcdr1_be(bytes),
78 (_, false) => T::decode(bytes),
79 (_, true) => T::decode_be(bytes),
80 }
81}
82
83#[derive(Debug)]
85pub struct Subscriber {
86 pub(crate) inner: Arc<SubscriberInner>,
87}
88
89pub(crate) struct SubscriberInner {
90 #[cfg(feature = "std")]
91 pub(crate) qos: std::sync::Mutex<SubscriberQos>,
92 #[cfg(not(feature = "std"))]
93 #[allow(dead_code)]
94 pub(crate) qos: SubscriberQos,
95 pub(crate) entity_state: alloc::sync::Arc<crate::entity::EntityState>,
96 #[cfg(feature = "std")]
97 pub(crate) runtime: Option<Arc<DcpsRuntime>>,
98 #[cfg(feature = "std")]
101 pub(crate) listener: std::sync::Mutex<Option<(ArcSubscriberListener, StatusMask)>>,
102 #[cfg(feature = "std")]
105 pub(crate) participant:
106 std::sync::Mutex<Option<alloc::sync::Weak<crate::participant::ParticipantInner>>>,
107 pub(crate) access_scope: Arc<crate::coherent_set::GroupAccessScope>,
110 #[cfg(feature = "std")]
113 pub(crate) datareaders:
114 std::sync::Mutex<alloc::vec::Vec<crate::instance_handle::InstanceHandle>>,
115}
116
117impl core::fmt::Debug for SubscriberInner {
118 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
119 let listener_present = self.listener.lock().map(|s| s.is_some()).unwrap_or(false);
120 f.debug_struct("SubscriberInner")
121 .field("entity_state", &self.entity_state)
122 .field("listener_present", &listener_present)
123 .finish_non_exhaustive()
124 }
125}
126
127impl Subscriber {
128 #[cfg(feature = "std")]
129 pub(crate) fn new(qos: SubscriberQos, runtime: Option<Arc<DcpsRuntime>>) -> Self {
130 Self {
131 inner: Arc::new(SubscriberInner {
132 qos: std::sync::Mutex::new(qos),
133 entity_state: crate::entity::EntityState::new(),
134 runtime,
135 listener: std::sync::Mutex::new(None),
136 participant: std::sync::Mutex::new(None),
137 access_scope: crate::coherent_set::GroupAccessScope::new(),
138 datareaders: std::sync::Mutex::new(alloc::vec::Vec::new()),
139 }),
140 }
141 }
142
143 #[cfg(feature = "std")]
146 #[must_use]
147 pub fn contains_reader(&self, handle: crate::instance_handle::InstanceHandle) -> bool {
148 self.inner
149 .datareaders
150 .lock()
151 .map(|v| v.contains(&handle))
152 .unwrap_or(false)
153 }
154
155 #[cfg(feature = "std")]
156 fn track_reader(&self, handle: crate::instance_handle::InstanceHandle) {
157 if let Ok(mut list) = self.inner.datareaders.lock() {
158 list.push(handle);
159 }
160 if let Ok(slot) = self.inner.participant.lock() {
162 if let Some(weak) = slot.as_ref() {
163 if let Some(p_inner) = weak.upgrade() {
164 if let Ok(mut drs) = p_inner.datareaders.lock() {
165 drs.push(handle);
166 }
167 }
168 }
169 }
170 }
171 #[cfg(not(feature = "std"))]
172 pub(crate) fn new(qos: SubscriberQos) -> Self {
173 Self {
174 inner: Arc::new(SubscriberInner {
175 qos,
176 entity_state: crate::entity::EntityState::new(),
177 access_scope: crate::coherent_set::GroupAccessScope::new(),
178 }),
179 }
180 }
181
182 pub fn begin_access(&self) {
186 self.inner.access_scope.begin();
187 }
188
189 pub fn end_access(&self) -> Result<()> {
195 self.inner.access_scope.end()
196 }
197
198 #[must_use]
200 pub fn is_access_open(&self) -> bool {
201 self.inner.access_scope.is_active()
202 }
203
204 #[cfg(feature = "std")]
207 pub fn set_listener(&self, listener: Option<ArcSubscriberListener>, mask: StatusMask) {
208 if let Ok(mut slot) = self.inner.listener.lock() {
209 *slot = listener.map(|l| (l, mask));
210 }
211 self.inner.entity_state.set_listener_mask(mask);
212 }
213
214 #[cfg(feature = "std")]
216 #[must_use]
217 pub fn get_listener(&self) -> Option<ArcSubscriberListener> {
218 self.inner
219 .listener
220 .lock()
221 .ok()
222 .and_then(|s| s.as_ref().map(|(l, _)| Arc::clone(l)))
223 }
224
225 #[cfg(feature = "std")]
227 pub(crate) fn attach_participant(
228 &self,
229 participant: alloc::sync::Weak<crate::participant::ParticipantInner>,
230 ) {
231 if let Ok(mut slot) = self.inner.participant.lock() {
232 *slot = Some(participant);
233 }
234 }
235
236 #[cfg(feature = "std")]
239 #[must_use]
240 pub(crate) fn snapshot_reader_chain(
241 &self,
242 reader_listener: Option<(ArcDataReaderListener, StatusMask)>,
243 ) -> crate::listener_dispatch::ReaderListenerChain {
244 let subscriber = self
245 .inner
246 .listener
247 .lock()
248 .ok()
249 .and_then(|s| s.as_ref().map(|(l, m)| (Arc::clone(l), *m)));
250 let participant = {
251 let weak = self.inner.participant.lock().ok().and_then(|s| s.clone());
252 weak.and_then(|w| w.upgrade()).and_then(|inner| {
253 inner
254 .listener
255 .lock()
256 .ok()
257 .and_then(|s| s.as_ref().map(|(l, m)| (Arc::clone(l), *m)))
258 })
259 };
260 crate::listener_dispatch::ReaderListenerChain {
261 reader: reader_listener,
262 subscriber,
263 participant,
264 }
265 }
266
267 pub fn create_datareader<T: DdsType + Send + 'static>(
272 &self,
273 topic: &Topic<T>,
274 qos: DataReaderQos,
275 ) -> Result<DataReader<T>> {
276 if topic.type_name() != T::TYPE_NAME {
277 return Err(DdsError::BadParameter {
278 what: "topic.type_name mismatch",
279 });
280 }
281 #[cfg(feature = "std")]
282 if let Some(rt) = self.inner.runtime.as_ref() {
283 let reliable = qos.reliability.kind == ReliabilityKind::Reliable;
284 let (eid, rx) = rt.register_user_reader_kind(
290 crate::runtime::UserReaderConfig {
291 topic_name: topic.name().into(),
292 type_name: T::TYPE_NAME.into(),
293 reliable,
294 durability: qos.durability.kind,
295 deadline: qos.deadline,
296 liveliness: qos.liveliness,
297 ownership: qos.ownership.kind,
298 partition: qos.partition.names.clone(),
299 user_data: qos.user_data.value.clone(),
300 topic_data: qos.topic_data.value.clone(),
301 group_data: qos.group_data.value.clone(),
302 type_identifier: T::TYPE_IDENTIFIER.clone(),
304 type_consistency: zerodds_types::qos::TypeConsistencyEnforcement::default(),
305 data_representation_offer: qos.data_representation.clone(),
308 },
309 T::HAS_KEY,
310 )?;
311 let dr = DataReader::new_live(
312 topic.clone(),
313 qos,
314 self.inner.clone(),
315 Arc::clone(rt),
316 eid,
317 rx,
318 );
319 self.track_reader(dr.entity_state.instance_handle());
320 return Ok(dr);
321 }
322 let dr = DataReader::new_offline(topic.clone(), qos, self.inner.clone());
323 #[cfg(feature = "std")]
324 self.track_reader(dr.entity_state.instance_handle());
325 Ok(dr)
326 }
327}
328
329#[cfg(feature = "std")]
334impl crate::entity::Entity for Subscriber {
335 type Qos = SubscriberQos;
336
337 fn get_qos(&self) -> Self::Qos {
338 self.inner.qos.lock().map(|q| q.clone()).unwrap_or_default()
339 }
340
341 fn set_qos(&self, qos: Self::Qos) -> Result<()> {
342 if let Ok(mut current) = self.inner.qos.lock() {
345 *current = qos;
346 }
347 Ok(())
348 }
349
350 fn enable(&self) -> Result<()> {
351 self.inner.entity_state.enable();
352 Ok(())
353 }
354
355 fn entity_state(&self) -> alloc::sync::Arc<crate::entity::EntityState> {
356 alloc::sync::Arc::clone(&self.inner.entity_state)
357 }
358}
359
360pub struct DataReader<T: DdsType> {
366 topic: Topic<T>,
367 qos: Mutex<DataReaderQos>,
368 entity_state: Arc<crate::entity::EntityState>,
370 subscriber: Arc<SubscriberInner>,
373 #[cfg(feature = "std")]
375 listener: Mutex<Option<(ArcDataReaderListener, StatusMask)>>,
376 #[cfg(feature = "std")]
379 last_match_count: std::sync::atomic::AtomicI64,
380 #[cfg(feature = "std")]
382 last_requested_deadline_missed: std::sync::atomic::AtomicU64,
383 #[cfg(feature = "std")]
385 last_liveliness_alive: std::sync::atomic::AtomicI64,
386 #[cfg(feature = "std")]
388 last_liveliness_not_alive: std::sync::atomic::AtomicI64,
389 #[cfg(feature = "std")]
391 last_requested_incompatible_qos: std::sync::atomic::AtomicI64,
392 #[cfg(feature = "std")]
394 last_sample_lost: std::sync::atomic::AtomicU64,
395 #[cfg(feature = "std")]
397 last_sample_rejected: std::sync::atomic::AtomicI64,
398 inbox: Arc<Mutex<Vec<crate::runtime::UserSample>>>,
403 #[cfg(feature = "std")]
404 #[allow(dead_code)]
405 runtime: Option<Arc<DcpsRuntime>>,
406 #[cfg(feature = "std")]
407 #[allow(dead_code)]
408 entity_id: Option<EntityId>,
409 #[cfg(feature = "std")]
411 rx: Option<Mutex<mpsc::Receiver<crate::runtime::UserSample>>>,
412 #[allow(clippy::type_complexity)]
421 filter: Option<Arc<dyn Fn(&T) -> bool + Send + Sync>>,
422 #[cfg(feature = "std")]
424 instances: InstanceTracker,
425 #[cfg(feature = "std")]
429 cache: Arc<Mutex<Vec<CachedSample>>>,
430 #[cfg(all(feature = "std", feature = "flatdata-integration"))]
435 #[allow(clippy::type_complexity)]
436 pub(crate) flat_backend: Mutex<
437 Option<(
438 Arc<dyn zerodds_flatdata::SlotBackend>,
439 u8, std::sync::atomic::AtomicU32,
441 )>,
442 >,
443 _t: PhantomData<fn() -> T>,
444}
445
446#[cfg(feature = "std")]
452#[derive(Debug)]
453pub(crate) struct CachedSample {
454 pub bytes: Option<crate::sample_bytes::SampleBytes>,
457 pub info: SampleInfo,
458}
459
460impl<T: DdsType> core::fmt::Debug for DataReader<T> {
461 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
462 f.debug_struct("DataReader")
463 .field("topic", &self.topic.name())
464 .field("type", &T::TYPE_NAME)
465 .field("qos", &self.qos)
466 .finish_non_exhaustive()
467 }
468}
469
470#[cfg(feature = "std")]
474type RawIngestSample = (
475 crate::sample_bytes::SampleBytes,
476 [u8; 16],
477 i32,
478 Option<zerodds_rtps::header_extension::HeTimestamp>,
479 bool,
480 u8,
481);
482
483#[cfg(feature = "std")]
488impl<T: DdsType> Drop for DataReader<T> {
489 fn drop(&mut self) {
490 if let (Some(rt), Some(eid)) = (self.runtime.as_ref(), self.entity_id) {
491 rt.remove_user_reader(eid);
492 }
493 }
494}
495
496impl<T: DdsType> DataReader<T> {
497 #[cfg(feature = "std")]
498 fn new_offline(topic: Topic<T>, qos: DataReaderQos, subscriber: Arc<SubscriberInner>) -> Self {
499 Self {
500 topic,
501 qos: Mutex::new(qos),
502 entity_state: crate::entity::EntityState::new(),
503 subscriber,
504 listener: Mutex::new(None),
505 last_match_count: std::sync::atomic::AtomicI64::new(-1),
506 last_requested_deadline_missed: std::sync::atomic::AtomicU64::new(0),
507 last_liveliness_alive: std::sync::atomic::AtomicI64::new(-1),
508 last_liveliness_not_alive: std::sync::atomic::AtomicI64::new(-1),
509 last_requested_incompatible_qos: std::sync::atomic::AtomicI64::new(-1),
510 last_sample_lost: std::sync::atomic::AtomicU64::new(0),
511 last_sample_rejected: std::sync::atomic::AtomicI64::new(-1),
512 inbox: Arc::new(Mutex::new(Vec::new())),
513 runtime: None,
514 entity_id: None,
515 rx: None,
516 filter: None,
517 instances: InstanceTracker::new(),
518 cache: Arc::new(Mutex::new(Vec::new())),
519 #[cfg(feature = "flatdata-integration")]
520 flat_backend: Mutex::new(None),
521 _t: PhantomData,
522 }
523 }
524
525 #[cfg(feature = "std")]
526 fn new_live(
527 topic: Topic<T>,
528 qos: DataReaderQos,
529 subscriber: Arc<SubscriberInner>,
530 runtime: Arc<DcpsRuntime>,
531 entity_id: EntityId,
532 rx: mpsc::Receiver<crate::runtime::UserSample>,
533 ) -> Self {
534 Self {
535 topic,
536 qos: Mutex::new(qos),
537 entity_state: crate::entity::EntityState::new(),
538 subscriber,
539 listener: Mutex::new(None),
540 last_match_count: std::sync::atomic::AtomicI64::new(-1),
541 last_requested_deadline_missed: std::sync::atomic::AtomicU64::new(0),
542 last_liveliness_alive: std::sync::atomic::AtomicI64::new(-1),
543 last_liveliness_not_alive: std::sync::atomic::AtomicI64::new(-1),
544 last_requested_incompatible_qos: std::sync::atomic::AtomicI64::new(-1),
545 last_sample_lost: std::sync::atomic::AtomicU64::new(0),
546 last_sample_rejected: std::sync::atomic::AtomicI64::new(-1),
547 inbox: Arc::new(Mutex::new(Vec::new())),
548 runtime: Some(runtime),
549 entity_id: Some(entity_id),
550 rx: Some(Mutex::new(rx)),
551 filter: None,
552 instances: InstanceTracker::new(),
553 cache: Arc::new(Mutex::new(Vec::new())),
554 #[cfg(feature = "flatdata-integration")]
555 flat_backend: Mutex::new(None),
556 _t: PhantomData,
557 }
558 }
559
560 #[cfg(not(feature = "std"))]
561 fn new(topic: Topic<T>, qos: DataReaderQos, subscriber: Arc<SubscriberInner>) -> Self {
562 Self {
563 topic,
564 qos,
565 subscriber,
566 inbox: Arc::new(Mutex::new(Vec::new())),
567 filter: None,
568 _t: PhantomData,
569 }
570 }
571
572 #[cfg(feature = "std")]
579 pub(crate) fn new_builtin(
580 topic: Topic<T>,
581 qos: DataReaderQos,
582 subscriber: Arc<SubscriberInner>,
583 inbox: Arc<Mutex<Vec<crate::runtime::UserSample>>>,
584 ) -> Self {
585 Self {
586 topic,
587 qos: Mutex::new(qos),
588 entity_state: crate::entity::EntityState::new(),
589 subscriber,
590 listener: Mutex::new(None),
591 last_match_count: std::sync::atomic::AtomicI64::new(-1),
592 last_requested_deadline_missed: std::sync::atomic::AtomicU64::new(0),
593 last_liveliness_alive: std::sync::atomic::AtomicI64::new(-1),
594 last_liveliness_not_alive: std::sync::atomic::AtomicI64::new(-1),
595 last_requested_incompatible_qos: std::sync::atomic::AtomicI64::new(-1),
596 last_sample_lost: std::sync::atomic::AtomicU64::new(0),
597 last_sample_rejected: std::sync::atomic::AtomicI64::new(-1),
598 inbox,
599 runtime: None,
600 entity_id: None,
601 rx: None,
602 filter: None,
603 instances: InstanceTracker::new(),
604 cache: Arc::new(Mutex::new(Vec::new())),
605 #[cfg(feature = "flatdata-integration")]
606 flat_backend: Mutex::new(None),
607 _t: PhantomData,
608 }
609 }
610
611 #[must_use]
619 pub fn with_filter<F>(mut self, filter: F) -> Self
620 where
621 F: Fn(&T) -> bool + Send + Sync + 'static,
622 {
623 self.filter = Some(Arc::new(filter));
624 self
625 }
626
627 #[must_use]
629 pub fn topic(&self) -> &Topic<T> {
630 &self.topic
631 }
632
633 #[must_use]
637 pub fn subscription_handle(&self) -> crate::instance_handle::InstanceHandle {
638 self.entity_state.instance_handle()
639 }
640
641 #[cfg(feature = "std")]
644 pub fn set_listener(&self, listener: Option<ArcDataReaderListener>, mask: StatusMask) {
645 if let Ok(mut slot) = self.listener.lock() {
646 *slot = listener.map(|l| (l, mask));
647 }
648 self.entity_state.set_listener_mask(mask);
649 }
650
651 #[cfg(feature = "std")]
653 #[must_use]
654 pub fn get_listener(&self) -> Option<ArcDataReaderListener> {
655 self.listener
656 .lock()
657 .ok()
658 .and_then(|s| s.as_ref().map(|(l, _)| Arc::clone(l)))
659 }
660
661 #[cfg(feature = "std")]
664 #[must_use]
665 pub(crate) fn listener_chain(&self) -> crate::listener_dispatch::ReaderListenerChain {
666 let reader = self
667 .listener
668 .lock()
669 .ok()
670 .and_then(|s| s.as_ref().map(|(l, m)| (Arc::clone(l), *m)));
671 let sub_handle = Subscriber {
672 inner: Arc::clone(&self.subscriber),
673 };
674 sub_handle.snapshot_reader_chain(reader)
675 }
676
677 #[must_use]
679 pub fn qos(&self) -> DataReaderQos {
680 self.qos.lock().map(|q| q.clone()).unwrap_or_default()
681 }
682
683 pub fn take(&self) -> Result<Vec<T>> {
690 #[cfg(feature = "std")]
693 {
694 let now = get_current_time();
695 let mut empty: Vec<CachedSample> = Vec::new();
696 self.run_reader_autopurge(now, &mut empty);
697 }
698 #[cfg(all(feature = "std", feature = "cyclone-iox", target_os = "linux"))]
702 {
703 let iox = crate::cyclone_iox_integration::take::<T>(self.topic.name());
704 if !iox.is_empty() {
705 return Ok(iox);
706 }
707 }
708 #[cfg(feature = "std")]
711 if let Some(rx_mu) = self.rx.as_ref() {
712 let mut out = Vec::new();
713 let min_sep_nanos = {
716 let qos = self.qos.lock().unwrap_or_else(|e| e.into_inner());
717 qos.time_based_filter.minimum_separation.to_nanos()
718 };
719 let staged = {
720 let mut inbox = self
721 .inbox
722 .lock()
723 .map_err(|_| DdsError::PreconditionNotMet {
724 reason: "datareader inbox poisoned",
725 })?;
726 core::mem::take(&mut *inbox)
727 };
728 for staged_item in staged {
729 match staged_item {
730 crate::runtime::UserSample::Alive {
731 payload: bytes,
732 writer_guid,
733 writer_strength,
734 representation,
735 big_endian,
736 ..
737 } => {
738 let sample = decode_for_encap::<T>(&bytes, representation, big_endian)
739 .map_err(|e| DdsError::WireError {
740 message: e.to_string(),
741 })?;
742 if !self.sample_passes_filter(&sample) {
743 continue;
744 }
745 if !self.live_mode_time_based_filter_pass(&sample, min_sep_nanos) {
746 continue;
747 }
748 if !self.passes_exclusive_ownership(&sample, writer_guid, writer_strength) {
750 continue;
751 }
752 out.push(sample);
753 }
754 crate::runtime::UserSample::Lifecycle { .. } => {
755 }
760 }
761 }
762 let rx = rx_mu.lock().map_err(|_| DdsError::PreconditionNotMet {
763 reason: "datareader rx poisoned",
764 })?;
765 while let Ok(item) = rx.try_recv() {
766 match item {
767 crate::runtime::UserSample::Alive {
768 payload: bytes,
769 writer_guid,
770 writer_strength,
771 representation,
772 big_endian,
773 ..
774 } => {
775 let sample = decode_for_encap::<T>(&bytes, representation, big_endian)
776 .map_err(|e| DdsError::WireError {
777 message: e.to_string(),
778 })?;
779 if !self.sample_passes_filter(&sample) {
780 continue;
781 }
782 if !self.live_mode_time_based_filter_pass(&sample, min_sep_nanos) {
783 continue;
784 }
785 if !self.passes_exclusive_ownership(&sample, writer_guid, writer_strength) {
787 continue;
788 }
789 out.push(sample);
790 }
791 crate::runtime::UserSample::Lifecycle { key_hash, kind } => {
792 let mut holder_bytes = Vec::with_capacity(16);
795 holder_bytes.extend_from_slice(&key_hash);
796 let lc_kind = match kind {
797 zerodds_rtps::history_cache::ChangeKind::NotAliveDisposed
798 | zerodds_rtps::history_cache::ChangeKind::NotAliveDisposedUnregistered => {
799 crate::sample_info::InstanceStateKind::NotAliveDisposed
800 }
801 zerodds_rtps::history_cache::ChangeKind::NotAliveUnregistered => {
802 crate::sample_info::InstanceStateKind::NotAliveNoWriters
803 }
804 _ => crate::sample_info::InstanceStateKind::Alive,
805 };
806 let _ = self.__push_lifecycle(key_hash, holder_bytes, lc_kind);
807 }
808 }
809 }
810 return Ok(out);
811 }
812 let raw = {
814 let mut inbox = self
815 .inbox
816 .lock()
817 .map_err(|_| DdsError::PreconditionNotMet {
818 reason: "datareader inbox poisoned",
819 })?;
820 core::mem::take(&mut *inbox)
821 };
822 let mut out = Vec::with_capacity(raw.len());
823 for staged_item in raw {
824 let crate::runtime::UserSample::Alive {
825 payload: bytes,
826 writer_guid,
827 writer_strength,
828 representation,
829 big_endian,
830 ..
831 } = staged_item
832 else {
833 continue;
834 };
835 let sample =
836 decode_for_encap::<T>(&bytes, representation, big_endian).map_err(|e| {
837 DdsError::WireError {
838 message: e.to_string(),
839 }
840 })?;
841 if !self.sample_passes_filter(&sample) {
842 continue;
843 }
844 if !self.passes_exclusive_ownership(&sample, writer_guid, writer_strength) {
849 continue;
850 }
851 out.push(sample);
852 }
853 Ok(out)
854 }
855
856 fn sample_passes_filter(&self, sample: &T) -> bool {
858 match &self.filter {
859 Some(f) => f(sample),
860 None => true,
861 }
862 }
863
864 #[cfg(feature = "std")]
875 fn passes_exclusive_ownership(
876 &self,
877 sample: &T,
878 writer_guid: [u8; 16],
879 writer_strength: i32,
880 ) -> bool {
881 let kind = {
882 let qos = self.qos.lock().unwrap_or_else(|e| e.into_inner());
883 qos.ownership.kind
884 };
885 if kind != zerodds_qos::OwnershipKind::Exclusive {
886 return true;
887 }
888 let (kh, key_bytes) = if T::HAS_KEY {
892 let mut holder = crate::dds_type::PlainCdr2BeKeyHolder::new();
893 sample.encode_key_holder_be(&mut holder);
894 let kb = holder.as_bytes().to_vec();
895 let max = T::KEY_HOLDER_MAX_SIZE.unwrap_or(usize::MAX);
896 (crate::dds_type::compute_key_hash(&kb, max), kb)
897 } else {
898 ([0u8; 16], Vec::new())
899 };
900 let _ = self.instances.observe_sample(kh, key_bytes, None);
904 self.instances
905 .should_accept_sample_under_exclusive_ownership(&kh, writer_guid, writer_strength)
906 }
907
908 #[cfg(feature = "std")]
916 fn live_mode_time_based_filter_pass(&self, sample: &T, min_sep_nanos: u128) -> bool {
917 if min_sep_nanos == 0 || !T::HAS_KEY {
918 return true;
919 }
920 let mut holder = crate::dds_type::PlainCdr2BeKeyHolder::new();
921 sample.encode_key_holder_be(&mut holder);
922 let key_bytes = holder.as_bytes().to_vec();
923 let max = T::KEY_HOLDER_MAX_SIZE.unwrap_or(usize::MAX);
924 let kh = crate::dds_type::compute_key_hash(&key_bytes, max);
925 let now = get_current_time();
926 if !self
927 .instances
928 .should_deliver_under_time_based_filter(&kh, now, min_sep_nanos)
929 {
930 return false;
931 }
932 let _ = self.instances.observe_sample(kh, key_bytes, Some(now));
933 self.instances.record_delivery(&kh, now);
934 true
935 }
936
937 pub fn read(&self) -> Result<Vec<T>> {
944 let raw = {
945 let inbox = self
946 .inbox
947 .lock()
948 .map_err(|_| DdsError::PreconditionNotMet {
949 reason: "datareader inbox poisoned",
950 })?;
951 inbox.clone()
952 };
953 let mut out = Vec::with_capacity(raw.len());
954 for staged_item in raw {
955 let crate::runtime::UserSample::Alive {
956 payload: bytes,
957 writer_guid,
958 writer_strength,
959 representation,
960 big_endian,
961 ..
962 } = staged_item
963 else {
964 continue;
965 };
966 let sample =
967 decode_for_encap::<T>(&bytes, representation, big_endian).map_err(|e| {
968 DdsError::WireError {
969 message: e.to_string(),
970 }
971 })?;
972 if !self.sample_passes_filter(&sample) {
973 continue;
974 }
975 if !self.passes_exclusive_ownership(&sample, writer_guid, writer_strength) {
980 continue;
981 }
982 out.push(sample);
983 }
984 Ok(out)
985 }
986
987 #[must_use]
995 pub fn matched_publication_count(&self) -> usize {
996 #[cfg(feature = "std")]
997 if let (Some(rt), Some(eid)) = (&self.runtime, self.entity_id) {
998 let n = rt.user_reader_matched_count(eid);
999 self.poll_subscription_matched(n);
1000 return n;
1001 }
1002 0
1003 }
1004
1005 #[cfg(feature = "std")]
1007 pub(crate) fn poll_subscription_matched(&self, current: usize) {
1008 let curr = current as i64;
1009 let prev = self
1010 .last_match_count
1011 .swap(curr, std::sync::atomic::Ordering::AcqRel);
1012 if prev == curr {
1013 return;
1014 }
1015 let total = if curr > prev.max(0) {
1016 curr
1017 } else {
1018 prev.max(0)
1019 };
1020 let delta = curr - prev.max(0);
1021 let status = crate::status::SubscriptionMatchedStatus {
1022 total_count: total as i32,
1023 total_count_change: delta.max(0) as i32,
1024 current_count: curr as i32,
1025 current_count_change: delta as i32,
1026 last_publication_handle: crate::instance_handle::HANDLE_NIL,
1027 };
1028 let chain = self.listener_chain();
1029 crate::listener_dispatch::dispatch_subscription_matched(
1030 &chain,
1031 self.entity_state.instance_handle(),
1032 status,
1033 );
1034 }
1035
1036 #[cfg(feature = "std")]
1039 pub(crate) fn poll_requested_deadline_missed(&self, current: u64) {
1040 let prev = self
1041 .last_requested_deadline_missed
1042 .swap(current, std::sync::atomic::Ordering::AcqRel);
1043 if current == prev {
1044 return;
1045 }
1046 let total_change = current.saturating_sub(prev);
1047 let status = crate::status::RequestedDeadlineMissedStatus {
1048 total_count: current as i32,
1049 total_count_change: total_change as i32,
1050 last_instance_handle: crate::instance_handle::HANDLE_NIL,
1051 };
1052 let chain = self.listener_chain();
1053 crate::listener_dispatch::dispatch_requested_deadline_missed(
1054 &chain,
1055 self.entity_state.instance_handle(),
1056 status,
1057 );
1058 }
1059
1060 #[cfg(feature = "std")]
1064 pub(crate) fn poll_liveliness_changed(&self, alive_count: u64, not_alive_count: u64) {
1065 let curr_alive = alive_count as i64;
1066 let curr_not = not_alive_count as i64;
1067 let prev_alive = self
1068 .last_liveliness_alive
1069 .swap(curr_alive, std::sync::atomic::Ordering::AcqRel);
1070 let prev_not = self
1071 .last_liveliness_not_alive
1072 .swap(curr_not, std::sync::atomic::Ordering::AcqRel);
1073 let alive_changed = if prev_alive < 0 {
1076 curr_alive != 0
1077 } else {
1078 prev_alive != curr_alive
1079 };
1080 let not_changed = if prev_not < 0 {
1081 curr_not != 0
1082 } else {
1083 prev_not != curr_not
1084 };
1085 if !alive_changed && !not_changed {
1086 return;
1087 }
1088 let alive_delta = if prev_alive < 0 {
1089 curr_alive
1090 } else {
1091 curr_alive - prev_alive
1092 };
1093 let not_delta = if prev_not < 0 {
1094 curr_not
1095 } else {
1096 curr_not - prev_not
1097 };
1098 let status = crate::status::LivelinessChangedStatus {
1099 alive_count: curr_alive as i32,
1100 not_alive_count: curr_not as i32,
1101 alive_count_change: alive_delta as i32,
1102 not_alive_count_change: not_delta as i32,
1103 last_publication_handle: crate::instance_handle::HANDLE_NIL,
1104 };
1105 let chain = self.listener_chain();
1106 crate::listener_dispatch::dispatch_liveliness_changed(
1107 &chain,
1108 self.entity_state.instance_handle(),
1109 status,
1110 );
1111 }
1112
1113 #[cfg(feature = "std")]
1116 pub(crate) fn poll_requested_incompatible_qos(
1117 &self,
1118 snapshot: crate::status::RequestedIncompatibleQosStatus,
1119 ) {
1120 let curr = i64::from(snapshot.total_count);
1121 let prev = self
1122 .last_requested_incompatible_qos
1123 .swap(curr, std::sync::atomic::Ordering::AcqRel);
1124 if curr == prev {
1125 return;
1126 }
1127 let delta = curr - prev.max(0);
1128 let status = crate::status::RequestedIncompatibleQosStatus {
1129 total_count: curr as i32,
1130 total_count_change: delta.max(0) as i32,
1131 last_policy_id: snapshot.last_policy_id,
1132 policies: snapshot.policies,
1133 };
1134 let chain = self.listener_chain();
1135 crate::listener_dispatch::dispatch_requested_incompatible_qos(
1136 &chain,
1137 self.entity_state.instance_handle(),
1138 status,
1139 );
1140 }
1141
1142 #[cfg(feature = "std")]
1144 pub(crate) fn poll_sample_lost(&self, current: u64) {
1145 let prev = self
1146 .last_sample_lost
1147 .swap(current, std::sync::atomic::Ordering::AcqRel);
1148 if current == prev {
1149 return;
1150 }
1151 let delta = current.saturating_sub(prev);
1152 let status = crate::status::SampleLostStatus {
1153 total_count: current as i32,
1154 total_count_change: delta as i32,
1155 };
1156 let chain = self.listener_chain();
1157 crate::listener_dispatch::dispatch_sample_lost(
1158 &chain,
1159 self.entity_state.instance_handle(),
1160 status,
1161 );
1162 }
1163
1164 #[cfg(feature = "std")]
1166 pub(crate) fn poll_sample_rejected(&self, snapshot: crate::status::SampleRejectedStatus) {
1167 let curr = i64::from(snapshot.total_count);
1168 let prev = self
1169 .last_sample_rejected
1170 .swap(curr, std::sync::atomic::Ordering::AcqRel);
1171 if curr == prev {
1172 return;
1173 }
1174 let delta = curr - prev.max(0);
1175 let status = crate::status::SampleRejectedStatus {
1176 total_count: curr as i32,
1177 total_count_change: delta.max(0) as i32,
1178 last_reason: snapshot.last_reason,
1179 last_instance_handle: snapshot.last_instance_handle,
1180 };
1181 let chain = self.listener_chain();
1182 crate::listener_dispatch::dispatch_sample_rejected(
1183 &chain,
1184 self.entity_state.instance_handle(),
1185 status,
1186 );
1187 }
1188
1189 #[cfg(feature = "std")]
1198 pub fn wait_for_matched_publication(
1199 &self,
1200 min_count: usize,
1201 timeout: core::time::Duration,
1202 ) -> Result<()> {
1203 let deadline = std::time::Instant::now() + timeout;
1204 loop {
1205 if self.matched_publication_count() >= min_count {
1206 return Ok(());
1207 }
1208 let now = std::time::Instant::now();
1209 if now >= deadline {
1210 return Err(DdsError::Timeout);
1211 }
1212 if let Some(rt) = self.runtime.as_ref() {
1215 let _ = rt.wait_match_event(deadline - now);
1216 } else {
1217 std::thread::sleep(core::time::Duration::from_millis(20));
1219 }
1220 }
1221 }
1222
1223 #[must_use]
1230 pub fn requested_deadline_missed_count(&self) -> u64 {
1231 #[cfg(feature = "std")]
1232 if let (Some(rt), Some(eid)) = (&self.runtime, self.entity_id) {
1233 let n = rt.user_reader_requested_deadline_missed(eid);
1234 self.poll_requested_deadline_missed(n);
1235 return n;
1236 }
1237 0
1238 }
1239
1240 #[must_use]
1243 pub fn requested_incompatible_qos_status(
1244 &self,
1245 ) -> crate::status::RequestedIncompatibleQosStatus {
1246 #[cfg(feature = "std")]
1247 if let (Some(rt), Some(eid)) = (&self.runtime, self.entity_id) {
1248 let s = rt.user_reader_requested_incompatible_qos(eid);
1249 self.poll_requested_incompatible_qos(s.clone());
1250 return s;
1251 }
1252 crate::status::RequestedIncompatibleQosStatus::default()
1253 }
1254
1255 #[must_use]
1257 pub fn sample_lost_count(&self) -> u64 {
1258 #[cfg(feature = "std")]
1259 if let (Some(rt), Some(eid)) = (&self.runtime, self.entity_id) {
1260 let n = rt.user_reader_sample_lost(eid);
1261 self.poll_sample_lost(n);
1262 return n;
1263 }
1264 0
1265 }
1266
1267 #[must_use]
1269 pub fn sample_rejected_status(&self) -> crate::status::SampleRejectedStatus {
1270 #[cfg(feature = "std")]
1271 if let (Some(rt), Some(eid)) = (&self.runtime, self.entity_id) {
1272 let s = rt.user_reader_sample_rejected(eid);
1273 self.poll_sample_rejected(s);
1274 return s;
1275 }
1276 crate::status::SampleRejectedStatus::default()
1277 }
1278
1279 #[cfg(feature = "std")]
1282 pub fn drive_listeners(&self) {
1283 let _ = self.matched_publication_count();
1284 let _ = self.requested_deadline_missed_count();
1285 let (_, alive, not_alive) = self.liveliness_changed_status();
1286 self.poll_liveliness_changed(alive, not_alive);
1287 let _ = self.requested_incompatible_qos_status();
1288 let _ = self.sample_lost_count();
1289 let _ = self.sample_rejected_status();
1290 }
1291
1292 #[must_use]
1304 pub fn liveliness_changed_status(&self) -> (bool, u64, u64) {
1305 #[cfg(feature = "std")]
1306 if let (Some(rt), Some(eid)) = (&self.runtime, self.entity_id) {
1307 let triple = rt.user_reader_liveliness_status(eid);
1308 self.poll_liveliness_changed(triple.1, triple.2);
1310 return triple;
1311 }
1312 (false, 0, 0)
1313 }
1314
1315 #[cfg(feature = "std")]
1328 pub fn wait_for_data(&self, timeout: core::time::Duration) -> Result<()> {
1329 let Some(rx_mu) = self.rx.as_ref() else {
1330 let inbox_has = self.inbox.lock().map(|i| !i.is_empty()).unwrap_or(false);
1333 if inbox_has {
1334 return Ok(());
1335 }
1336 return Err(DdsError::Timeout);
1337 };
1338
1339 {
1341 let inbox = self
1342 .inbox
1343 .lock()
1344 .map_err(|_| DdsError::PreconditionNotMet {
1345 reason: "datareader inbox poisoned",
1346 })?;
1347 if !inbox.is_empty() {
1348 return Ok(());
1349 }
1350 }
1351
1352 let rx = rx_mu.lock().map_err(|_| DdsError::PreconditionNotMet {
1353 reason: "datareader rx poisoned",
1354 })?;
1355 let result = match rx.recv_timeout(timeout) {
1356 Ok(item) => {
1357 match item {
1358 sample @ crate::runtime::UserSample::Alive { .. } => {
1359 let mut inbox =
1360 self.inbox
1361 .lock()
1362 .map_err(|_| DdsError::PreconditionNotMet {
1363 reason: "datareader inbox poisoned",
1364 })?;
1365 inbox.push(sample);
1366 }
1367 crate::runtime::UserSample::Lifecycle { key_hash, kind } => {
1368 let lc_kind = match kind {
1369 zerodds_rtps::history_cache::ChangeKind::NotAliveDisposed
1370 | zerodds_rtps::history_cache::ChangeKind::NotAliveDisposedUnregistered => {
1371 crate::sample_info::InstanceStateKind::NotAliveDisposed
1372 }
1373 zerodds_rtps::history_cache::ChangeKind::NotAliveUnregistered => {
1374 crate::sample_info::InstanceStateKind::NotAliveNoWriters
1375 }
1376 _ => crate::sample_info::InstanceStateKind::Alive,
1377 };
1378 let mut holder_bytes = Vec::with_capacity(16);
1379 holder_bytes.extend_from_slice(&key_hash);
1380 let _ = self.__push_lifecycle(key_hash, holder_bytes, lc_kind);
1381 }
1382 }
1383 Ok(())
1384 }
1385 Err(std::sync::mpsc::RecvTimeoutError::Timeout) => Err(DdsError::Timeout),
1386 Err(std::sync::mpsc::RecvTimeoutError::Disconnected) => {
1387 Err(DdsError::PreconditionNotMet {
1388 reason: "datareader rx disconnected",
1389 })
1390 }
1391 };
1392 drop(rx);
1394 if result.is_ok() {
1395 self.notify_data_arrived();
1396 }
1397 result
1398 }
1399
1400 #[doc(hidden)]
1403 #[cfg(feature = "std")]
1404 pub fn __inbox_handle(&self) -> Arc<Mutex<Vec<crate::runtime::UserSample>>> {
1405 Arc::clone(&self.inbox)
1406 }
1407
1408 #[doc(hidden)]
1415 pub fn __push_raw(&self, bytes: Vec<u8>) -> Result<()> {
1416 self.__push_raw_with_writer(bytes, [0u8; 16], 0)
1417 }
1418
1419 #[doc(hidden)]
1423 pub fn __push_raw_with_writer(
1424 &self,
1425 bytes: Vec<u8>,
1426 writer_guid: [u8; 16],
1427 writer_strength: i32,
1428 ) -> Result<()> {
1429 {
1430 let mut inbox = self
1431 .inbox
1432 .lock()
1433 .map_err(|_| DdsError::PreconditionNotMet {
1434 reason: "datareader inbox poisoned",
1435 })?;
1436 inbox.push(crate::runtime::UserSample::Alive {
1437 payload: crate::sample_bytes::SampleBytes::from_vec(bytes),
1438 writer_guid,
1439 writer_strength,
1440 representation: 0,
1442 big_endian: false,
1443 source_timestamp: None,
1444 });
1445 }
1446 self.notify_data_arrived();
1448 Ok(())
1449 }
1450
1451 #[cfg(feature = "std")]
1458 pub(crate) fn notify_data_arrived(&self) {
1459 let chain = self.listener_chain();
1460 let reader_handle = self.entity_state.instance_handle();
1461 crate::listener_dispatch::dispatch_data_on_readers(&chain, reader_handle);
1462 crate::listener_dispatch::dispatch_data_available(&chain, reader_handle);
1463 }
1464
1465 #[cfg(feature = "std")]
1473 #[must_use]
1474 pub fn instance_tracker(&self) -> InstanceTracker {
1475 self.instances.clone()
1476 }
1477
1478 #[must_use]
1483 pub fn instance_handle(&self) -> crate::instance_handle::InstanceHandle {
1484 self.entity_state.instance_handle()
1485 }
1486
1487 #[doc(hidden)]
1491 #[cfg(feature = "std")]
1492 pub fn runtime_handle(
1493 &self,
1494 ) -> Option<(alloc::sync::Arc<crate::runtime::DcpsRuntime>, EntityId)> {
1495 match (&self.runtime, self.entity_id) {
1496 (Some(rt), Some(eid)) => Some((alloc::sync::Arc::clone(rt), eid)),
1497 _ => None,
1498 }
1499 }
1500
1501 #[must_use]
1512 pub fn notify_writer_liveliness_lost(&self, writer_guid: [u8; 16]) -> usize {
1513 self.instances.clear_owner_for_writer(writer_guid)
1514 }
1515
1516 #[must_use]
1520 pub fn notify_participant_liveliness_lost(&self, prefix: [u8; 12]) -> usize {
1521 self.instances.clear_owner_for_writer_prefix(prefix)
1522 }
1523
1524 #[cfg(feature = "std")]
1528 #[must_use]
1529 pub fn lookup_instance(&self, instance: &T) -> InstanceHandle {
1530 if !T::HAS_KEY {
1531 return HANDLE_NIL;
1532 }
1533 let mut holder = crate::dds_type::PlainCdr2BeKeyHolder::new();
1534 instance.encode_key_holder_be(&mut holder);
1535 let bytes = holder.as_bytes();
1536 let max = T::KEY_HOLDER_MAX_SIZE.unwrap_or(usize::MAX);
1537 let kh = crate::dds_type::compute_key_hash(bytes, max);
1538 self.instances.lookup(&kh).unwrap_or(HANDLE_NIL)
1539 }
1540
1541 #[cfg(feature = "std")]
1549 pub fn get_key_value(&self, handle: InstanceHandle) -> Result<T> {
1550 let Some(bytes) = self.instances.get_key_holder(handle) else {
1551 return Err(DdsError::BadParameter {
1552 what: "unknown instance handle",
1553 });
1554 };
1555 T::decode(&bytes).map_err(|e| DdsError::WireError {
1556 message: alloc::string::ToString::to_string(&e),
1557 })
1558 }
1559
1560 #[cfg(feature = "std")]
1566 fn ingest_into_cache(&self) -> Result<()> {
1567 let mut raw: Vec<RawIngestSample> = Vec::new();
1576 {
1577 let mut inbox = self
1578 .inbox
1579 .lock()
1580 .map_err(|_| DdsError::PreconditionNotMet {
1581 reason: "datareader inbox poisoned",
1582 })?;
1583 for item in inbox.drain(..) {
1584 if let crate::runtime::UserSample::Alive {
1585 payload,
1586 writer_guid,
1587 writer_strength,
1588 source_timestamp,
1589 big_endian,
1590 representation,
1591 ..
1592 } = item
1593 {
1594 raw.push((
1595 payload,
1596 writer_guid,
1597 writer_strength,
1598 source_timestamp,
1599 big_endian,
1600 representation,
1601 ));
1602 }
1603 }
1604 }
1605 let mut lifecycle_pending: Vec<(
1608 crate::instance_tracker::KeyHash,
1609 crate::sample_info::InstanceStateKind,
1610 )> = Vec::new();
1611 if let Some(rx_mu) = self.rx.as_ref() {
1612 let rx = rx_mu.lock().map_err(|_| DdsError::PreconditionNotMet {
1613 reason: "datareader rx poisoned",
1614 })?;
1615 while let Ok(item) = rx.try_recv() {
1616 match item {
1617 crate::runtime::UserSample::Alive {
1618 payload: bytes,
1619 writer_guid,
1620 writer_strength,
1621 source_timestamp,
1622 big_endian,
1623 representation,
1624 ..
1625 } => raw.push((
1626 bytes,
1627 writer_guid,
1628 writer_strength,
1629 source_timestamp,
1630 big_endian,
1631 representation,
1632 )),
1633 crate::runtime::UserSample::Lifecycle { key_hash, kind } => {
1634 let lc_kind = match kind {
1635 zerodds_rtps::history_cache::ChangeKind::NotAliveDisposed
1636 | zerodds_rtps::history_cache::ChangeKind::NotAliveDisposedUnregistered => {
1637 crate::sample_info::InstanceStateKind::NotAliveDisposed
1638 }
1639 zerodds_rtps::history_cache::ChangeKind::NotAliveUnregistered => {
1640 crate::sample_info::InstanceStateKind::NotAliveNoWriters
1641 }
1642 _ => crate::sample_info::InstanceStateKind::Alive,
1643 };
1644 lifecycle_pending.push((key_hash, lc_kind));
1645 }
1646 }
1647 }
1648 }
1649 for (kh, lc_kind) in lifecycle_pending {
1652 let mut holder_bytes = Vec::with_capacity(16);
1653 holder_bytes.extend_from_slice(&kh);
1654 let _ = self.__push_lifecycle(kh, holder_bytes, lc_kind);
1655 }
1656 let now = get_current_time();
1657 let mut cache = self
1658 .cache
1659 .lock()
1660 .map_err(|_| DdsError::PreconditionNotMet {
1661 reason: "datareader cache poisoned",
1662 })?;
1663 if raw.is_empty() {
1664 self.run_reader_autopurge(now, &mut cache);
1667 return Ok(());
1668 }
1669 for (bytes, writer_guid, writer_strength, src_ts, big_endian, representation) in raw {
1670 let sample_source_ts = src_ts.map_or(now, crate::time::he_timestamp_to_time);
1674 let sample =
1677 decode_for_encap::<T>(&bytes, representation, big_endian).map_err(|e| {
1678 DdsError::WireError {
1679 message: alloc::string::ToString::to_string(&e),
1680 }
1681 })?;
1682 if !self.sample_passes_filter(&sample) {
1683 continue;
1684 }
1685 if !self.passes_exclusive_ownership(&sample, writer_guid, writer_strength) {
1688 continue;
1689 }
1690 let info = if T::HAS_KEY {
1691 let mut holder = crate::dds_type::PlainCdr2BeKeyHolder::new();
1692 sample.encode_key_holder_be(&mut holder);
1693 let key_bytes = holder.as_bytes().to_vec();
1694 let max = T::KEY_HOLDER_MAX_SIZE.unwrap_or(usize::MAX);
1695 let kh = crate::dds_type::compute_key_hash(&key_bytes, max);
1696 let (min_sep_nanos, by_source_ts) = {
1699 let qos = self.qos.lock().unwrap_or_else(|e| e.into_inner());
1700 (
1701 qos.time_based_filter.minimum_separation.to_nanos(),
1702 qos.destination_order.kind
1703 == zerodds_qos::DestinationOrderKind::BySourceTimestamp,
1704 )
1705 };
1706 if !self
1710 .instances
1711 .should_deliver_under_time_based_filter(&kh, now, min_sep_nanos)
1712 {
1713 continue;
1714 }
1715 let order_ts = if by_source_ts { sample_source_ts } else { now };
1721 if !self.instances.should_deliver_under_destination_order(
1722 &kh,
1723 order_ts,
1724 by_source_ts,
1725 ) {
1726 continue;
1727 }
1728 let (handle, _) = self.instances.observe_sample(kh, key_bytes, Some(now));
1729 self.instances.record_delivery(&kh, order_ts);
1730 let state = match self.instances.get_by_handle(handle) {
1731 Some(s) => s,
1732 None => continue, };
1734 SampleInfo {
1735 sample_state: SampleStateKind::NotRead,
1736 view_state: if state.reader_view_new {
1737 ViewStateKind::New
1738 } else {
1739 ViewStateKind::NotNew
1740 },
1741 instance_state: state.kind,
1742 disposed_generation_count: state.disposed_generation_count,
1743 no_writers_generation_count: state.no_writers_generation_count,
1744 source_timestamp: sample_source_ts,
1745 instance_handle: handle,
1746 valid_data: true,
1747 ..SampleInfo::default()
1748 }
1749 } else {
1750 SampleInfo {
1755 sample_state: SampleStateKind::NotRead,
1756 view_state: ViewStateKind::NotNew,
1757 instance_handle: HANDLE_NIL,
1758 source_timestamp: sample_source_ts,
1759 valid_data: true,
1760 ..SampleInfo::default()
1761 }
1762 };
1763 cache.push(CachedSample {
1764 bytes: Some(bytes),
1765 info,
1766 });
1767 }
1768 self.run_reader_autopurge(now, &mut cache);
1772 Ok(())
1773 }
1774
1775 #[cfg(feature = "std")]
1779 fn run_reader_autopurge(&self, now: Time, cache: &mut Vec<CachedSample>) {
1780 let (purge_disp, purge_now) = {
1781 let qos = self.qos.lock().unwrap_or_else(|e| e.into_inner());
1782 (
1783 qos.reader_data_lifecycle
1784 .autopurge_disposed_samples_delay
1785 .to_nanos(),
1786 qos.reader_data_lifecycle
1787 .autopurge_nowriter_samples_delay
1788 .to_nanos(),
1789 )
1790 };
1791 if purge_disp == u128::MAX && purge_now == u128::MAX {
1792 return;
1793 }
1794 let purged = self.instances.autopurge(now, purge_disp, purge_now);
1795 if purged > 0 {
1796 cache.retain(|s| {
1797 s.info.instance_handle.is_nil()
1798 || self
1799 .instances
1800 .get_by_handle(s.info.instance_handle)
1801 .is_some()
1802 });
1803 }
1804 }
1805
1806 #[cfg(feature = "std")]
1810 #[doc(hidden)]
1811 pub fn __push_lifecycle(
1812 &self,
1813 keyhash: crate::instance_tracker::KeyHash,
1814 key_holder: Vec<u8>,
1815 kind: InstanceStateKind,
1816 ) -> Result<()> {
1817 let now = get_current_time();
1818 let (handle, _) = self
1821 .instances
1822 .observe_sample(keyhash, key_holder, Some(now));
1823 match kind {
1824 InstanceStateKind::NotAliveDisposed => {
1825 self.instances.dispose(handle, Some(now));
1826 }
1827 InstanceStateKind::NotAliveNoWriters => {
1828 self.instances.unregister(handle, Some(now));
1829 }
1830 InstanceStateKind::Alive => {}
1831 }
1832 let Some(state) = self.instances.get_by_handle(handle) else {
1833 return Ok(()); };
1835 let info = SampleInfo {
1836 source_timestamp: now,
1837 valid_data: false,
1838 instance_handle: handle,
1839 instance_state: state.kind,
1840 disposed_generation_count: state.disposed_generation_count,
1841 no_writers_generation_count: state.no_writers_generation_count,
1842 view_state: if state.reader_view_new {
1843 ViewStateKind::New
1844 } else {
1845 ViewStateKind::NotNew
1846 },
1847 ..SampleInfo::default()
1848 };
1849 let mut cache = self
1850 .cache
1851 .lock()
1852 .map_err(|_| DdsError::PreconditionNotMet {
1853 reason: "datareader cache poisoned",
1854 })?;
1855 cache.push(CachedSample { bytes: None, info });
1856 Ok(())
1857 }
1858
1859 #[cfg(feature = "std")]
1866 pub fn take_with_info(&self) -> Result<Vec<Sample<T>>> {
1867 self.take_filtered(
1868 sample_state_mask::ANY,
1869 view_state_mask::ANY,
1870 instance_state_mask::ANY,
1871 )
1872 }
1873
1874 #[cfg(feature = "std")]
1880 pub fn read_with_info(&self) -> Result<Vec<Sample<T>>> {
1881 self.read_filtered(
1882 sample_state_mask::ANY,
1883 view_state_mask::ANY,
1884 instance_state_mask::ANY,
1885 )
1886 }
1887
1888 #[cfg(feature = "std")]
1893 pub fn take_filtered(
1894 &self,
1895 sample_mask: u32,
1896 view_mask: u32,
1897 instance_mask: u32,
1898 ) -> Result<Vec<Sample<T>>> {
1899 self.ingest_into_cache()?;
1900 let mut cache = self
1901 .cache
1902 .lock()
1903 .map_err(|_| DdsError::PreconditionNotMet {
1904 reason: "datareader cache poisoned",
1905 })?;
1906 let mut out = Vec::new();
1907 let mut keep = Vec::with_capacity(cache.len());
1908 for s in cache.drain(..) {
1909 if s.info.matches_states(sample_mask, view_mask, instance_mask) {
1910 let sample = self.materialize(s)?;
1911 self.instances.mark_view_seen(sample.info.instance_handle);
1912 if sample.info.instance_handle != HANDLE_NIL {
1913 self.instances.drain_samples(sample.info.instance_handle, 1);
1914 }
1915 out.push(sample);
1916 } else {
1917 keep.push(s);
1918 }
1919 }
1920 *cache = keep;
1921 Ok(out)
1922 }
1923
1924 #[cfg(feature = "std")]
1929 pub fn read_filtered(
1930 &self,
1931 sample_mask: u32,
1932 view_mask: u32,
1933 instance_mask: u32,
1934 ) -> Result<Vec<Sample<T>>> {
1935 self.ingest_into_cache()?;
1936 let mut cache = self
1937 .cache
1938 .lock()
1939 .map_err(|_| DdsError::PreconditionNotMet {
1940 reason: "datareader cache poisoned",
1941 })?;
1942 let mut out = Vec::with_capacity(cache.len());
1943 for s in cache.iter_mut() {
1944 if !s.info.matches_states(sample_mask, view_mask, instance_mask) {
1945 continue;
1946 }
1947 let snapshot = Sample::new(
1949 self.decode_or_keyholder(s.bytes.as_deref(), s.info.instance_handle)?,
1950 s.info,
1951 );
1952 s.info.sample_state = SampleStateKind::Read;
1954 self.instances.mark_view_seen(s.info.instance_handle);
1955 out.push(snapshot);
1956 }
1957 Ok(out)
1958 }
1959
1960 #[cfg(feature = "std")]
1967 pub fn read_w_condition(
1968 &self,
1969 condition: &Arc<crate::condition::QueryCondition>,
1970 ) -> Result<Vec<Sample<T>>> {
1971 let base = condition.base();
1972 let sample_mask = base.get_sample_state_mask();
1973 let view_mask = base.get_view_state_mask();
1974 let instance_mask = base.get_instance_state_mask();
1975
1976 self.ingest_into_cache()?;
1977 let mut cache = self
1978 .cache
1979 .lock()
1980 .map_err(|_| DdsError::PreconditionNotMet {
1981 reason: "datareader cache poisoned",
1982 })?;
1983 let mut out = Vec::with_capacity(cache.len());
1984 for s in cache.iter_mut() {
1985 if !s.info.matches_states(sample_mask, view_mask, instance_mask) {
1986 continue;
1987 }
1988 let decoded = self.decode_or_keyholder(s.bytes.as_deref(), s.info.instance_handle)?;
1989 let row = crate::dds_type::DdsTypeRow::new(&decoded);
1990 if !condition.evaluate(&row).unwrap_or(false) {
1994 continue;
1995 }
1996 let snapshot = Sample::new(decoded, s.info);
1997 s.info.sample_state = SampleStateKind::Read;
1998 self.instances.mark_view_seen(s.info.instance_handle);
1999 out.push(snapshot);
2000 }
2001 Ok(out)
2002 }
2003
2004 #[cfg(feature = "std")]
2010 pub fn take_w_condition(
2011 &self,
2012 condition: &Arc<crate::condition::QueryCondition>,
2013 ) -> Result<Vec<Sample<T>>> {
2014 let base = condition.base();
2015 let sample_mask = base.get_sample_state_mask();
2016 let view_mask = base.get_view_state_mask();
2017 let instance_mask = base.get_instance_state_mask();
2018
2019 self.ingest_into_cache()?;
2020 let mut cache = self
2021 .cache
2022 .lock()
2023 .map_err(|_| DdsError::PreconditionNotMet {
2024 reason: "datareader cache poisoned",
2025 })?;
2026 let mut out = Vec::new();
2027 let mut keep = Vec::with_capacity(cache.len());
2028 for s in cache.drain(..) {
2029 if !s.info.matches_states(sample_mask, view_mask, instance_mask) {
2030 keep.push(s);
2031 continue;
2032 }
2033 let decoded = self.decode_or_keyholder(s.bytes.as_deref(), s.info.instance_handle)?;
2034 let row = crate::dds_type::DdsTypeRow::new(&decoded);
2035 if !condition.evaluate(&row).unwrap_or(false) {
2036 keep.push(s);
2037 continue;
2038 }
2039 let sample = Sample::new(decoded, s.info);
2040 self.instances.mark_view_seen(sample.info.instance_handle);
2041 if sample.info.instance_handle != HANDLE_NIL {
2042 self.instances.drain_samples(sample.info.instance_handle, 1);
2043 }
2044 out.push(sample);
2045 }
2046 *cache = keep;
2047 Ok(out)
2048 }
2049
2050 #[cfg(feature = "std")]
2056 pub fn read_instance(&self, handle: InstanceHandle) -> Result<Vec<Sample<T>>> {
2057 if handle.is_nil() {
2058 return Err(DdsError::BadParameter {
2059 what: "read_instance with HANDLE_NIL",
2060 });
2061 }
2062 self.ingest_into_cache()?;
2063 let mut cache = self
2064 .cache
2065 .lock()
2066 .map_err(|_| DdsError::PreconditionNotMet {
2067 reason: "datareader cache poisoned",
2068 })?;
2069 let mut out = Vec::new();
2070 for s in cache.iter_mut() {
2071 if s.info.instance_handle != handle {
2072 continue;
2073 }
2074 let snap = Sample::new(
2075 self.decode_or_keyholder(s.bytes.as_deref(), s.info.instance_handle)?,
2076 s.info,
2077 );
2078 s.info.sample_state = SampleStateKind::Read;
2079 self.instances.mark_view_seen(handle);
2080 out.push(snap);
2081 }
2082 Ok(out)
2083 }
2084
2085 #[cfg(feature = "std")]
2090 pub fn take_instance(&self, handle: InstanceHandle) -> Result<Vec<Sample<T>>> {
2091 if handle.is_nil() {
2092 return Err(DdsError::BadParameter {
2093 what: "take_instance with HANDLE_NIL",
2094 });
2095 }
2096 self.ingest_into_cache()?;
2097 let mut cache = self
2098 .cache
2099 .lock()
2100 .map_err(|_| DdsError::PreconditionNotMet {
2101 reason: "datareader cache poisoned",
2102 })?;
2103 let mut out = Vec::new();
2104 let mut keep = Vec::with_capacity(cache.len());
2105 for s in cache.drain(..) {
2106 if s.info.instance_handle == handle {
2107 out.push(self.materialize(s)?);
2108 } else {
2109 keep.push(s);
2110 }
2111 }
2112 *cache = keep;
2113 if !out.is_empty() {
2114 self.instances.mark_view_seen(handle);
2115 self.instances.drain_samples(handle, out.len() as u32);
2116 }
2117 Ok(out)
2118 }
2119
2120 #[cfg(feature = "std")]
2128 pub fn read_next_instance(&self, previous: InstanceHandle) -> Result<Vec<Sample<T>>> {
2129 let Some(next) = self.instances.next_handle_after(previous) else {
2130 return Ok(Vec::new());
2131 };
2132 self.read_instance(next)
2133 }
2134
2135 #[cfg(feature = "std")]
2140 pub fn take_next_instance(&self, previous: InstanceHandle) -> Result<Vec<Sample<T>>> {
2141 let Some(next) = self.instances.next_handle_after(previous) else {
2142 return Ok(Vec::new());
2143 };
2144 self.take_instance(next)
2145 }
2146
2147 #[cfg(feature = "std")]
2152 fn materialize(&self, s: CachedSample) -> Result<Sample<T>> {
2153 let data = self.decode_or_keyholder(s.bytes.as_deref(), s.info.instance_handle)?;
2154 #[cfg(feature = "metrics")]
2155 crate::metrics::add_samples_read(self.topic.name(), 1);
2156 Ok(Sample::new(data, s.info))
2157 }
2158
2159 #[cfg(feature = "std")]
2163 fn decode_or_keyholder(&self, bytes: Option<&[u8]>, handle: InstanceHandle) -> Result<T> {
2164 if let Some(b) = bytes {
2165 return T::decode(b).map_err(|e| DdsError::WireError {
2166 message: alloc::string::ToString::to_string(&e),
2167 });
2168 }
2169 if let Some(holder) = self.instances.get_key_holder(handle) {
2170 return T::decode(&holder).map_err(|e| DdsError::WireError {
2171 message: alloc::string::ToString::to_string(&e),
2172 });
2173 }
2174 T::decode(&[]).map_err(|e| DdsError::WireError {
2175 message: alloc::string::ToString::to_string(&e),
2176 })
2177 }
2178}
2179
2180#[cfg(feature = "std")]
2181impl<T: DdsType> crate::entity::Entity for DataReader<T> {
2182 type Qos = DataReaderQos;
2183
2184 fn get_qos(&self) -> Self::Qos {
2185 self.qos.lock().map(|q| q.clone()).unwrap_or_default()
2186 }
2187
2188 fn set_qos(&self, qos: Self::Qos) -> Result<()> {
2191 let enabled = self.entity_state.is_enabled();
2192 if let Ok(mut current) = self.qos.lock() {
2193 if enabled {
2194 if current.durability != qos.durability {
2195 return Err(crate::entity::immutable_if_enabled("DURABILITY"));
2196 }
2197 if current.reliability != qos.reliability {
2198 return Err(crate::entity::immutable_if_enabled("RELIABILITY"));
2199 }
2200 if current.history != qos.history {
2201 return Err(crate::entity::immutable_if_enabled("HISTORY"));
2202 }
2203 if current.resource_limits != qos.resource_limits {
2204 return Err(crate::entity::immutable_if_enabled("RESOURCE_LIMITS"));
2205 }
2206 if current.ownership != qos.ownership {
2207 return Err(crate::entity::immutable_if_enabled("OWNERSHIP"));
2208 }
2209 if current.liveliness != qos.liveliness {
2210 return Err(crate::entity::immutable_if_enabled("LIVELINESS"));
2211 }
2212 }
2213 *current = qos;
2214 }
2215 Ok(())
2216 }
2217
2218 fn enable(&self) -> Result<()> {
2219 self.entity_state.enable();
2220 Ok(())
2221 }
2222
2223 fn entity_state(&self) -> Arc<crate::entity::EntityState> {
2224 Arc::clone(&self.entity_state)
2225 }
2226}
2227
2228#[allow(dead_code)]
2230pub(crate) trait AnyDataReader: Send + Sync + core::fmt::Debug {
2231 fn topic_name(&self) -> &str;
2232 fn type_name(&self) -> &'static str;
2233}
2234
2235impl<T: DdsType + Send + 'static> AnyDataReader for DataReader<T>
2236where
2237 T: Send + Sync,
2238{
2239 fn topic_name(&self) -> &str {
2240 self.topic.name()
2241 }
2242 fn type_name(&self) -> &'static str {
2243 T::TYPE_NAME
2244 }
2245}
2246
2247#[allow(dead_code)]
2248pub(crate) fn boxed_any_reader<T: DdsType + Send + Sync + 'static>(
2249 r: DataReader<T>,
2250) -> Box<dyn AnyDataReader> {
2251 Box::new(r)
2252}
2253
2254#[cfg(test)]
2255#[allow(clippy::expect_used, clippy::unwrap_used)]
2256mod tests {
2257 use super::*;
2258 use crate::dds_type::RawBytes;
2259 use crate::factory::DomainParticipantFactory;
2260 use crate::qos::{DomainParticipantQos, TopicQos};
2261
2262 fn mk_topic() -> Topic<RawBytes> {
2263 let p = DomainParticipantFactory::instance()
2264 .create_participant_offline(0, DomainParticipantQos::default());
2265 Topic::new("Chatter".into(), TopicQos::default(), p)
2266 }
2267
2268 #[test]
2273 fn decode_for_encap_dispatches_on_representation_and_endianness() {
2274 use crate::dds_type::{DecodeError, EncodeError};
2275
2276 #[derive(Debug, PartialEq)]
2279 struct Probe(u8);
2280 impl DdsType for Probe {
2281 const TYPE_NAME: &'static str = "test::Probe";
2282 fn encode(&self, _out: &mut Vec<u8>) -> core::result::Result<(), EncodeError> {
2283 Ok(())
2284 }
2285 fn decode(_b: &[u8]) -> core::result::Result<Self, DecodeError> {
2286 Ok(Probe(1)) }
2288 fn decode_be(_b: &[u8]) -> core::result::Result<Self, DecodeError> {
2289 Ok(Probe(2)) }
2291 fn decode_xcdr1(_b: &[u8]) -> core::result::Result<Self, DecodeError> {
2292 Ok(Probe(10)) }
2294 fn decode_xcdr1_be(_b: &[u8]) -> core::result::Result<Self, DecodeError> {
2295 Ok(Probe(20)) }
2297 }
2298
2299 assert_eq!(decode_for_encap::<Probe>(&[], 1, false).unwrap(), Probe(1));
2302 assert_eq!(decode_for_encap::<Probe>(&[], 1, true).unwrap(), Probe(2));
2303 assert_eq!(decode_for_encap::<Probe>(&[], 0, false).unwrap(), Probe(10));
2304 assert_eq!(decode_for_encap::<Probe>(&[], 0, true).unwrap(), Probe(20));
2305 }
2306
2307 #[test]
2308 fn subscriber_creates_datareader_for_matching_type() {
2309 let s = Subscriber::new(SubscriberQos::default(), None);
2310 let r = s
2311 .create_datareader::<RawBytes>(&mk_topic(), DataReaderQos::default())
2312 .unwrap();
2313 assert_eq!(r.topic().name(), "Chatter");
2314 }
2315
2316 #[test]
2317 fn datareader_take_returns_decoded_samples() {
2318 let s = Subscriber::new(SubscriberQos::default(), None);
2319 let r = s
2320 .create_datareader::<RawBytes>(&mk_topic(), DataReaderQos::default())
2321 .unwrap();
2322 r.__push_raw(vec![1, 2, 3]).unwrap();
2323 r.__push_raw(vec![4, 5]).unwrap();
2324 let samples = r.take().unwrap();
2325 assert_eq!(samples.len(), 2);
2326 assert_eq!(samples[0].data, vec![1, 2, 3]);
2327 assert_eq!(samples[1].data, vec![4, 5]);
2328 let again = r.take().unwrap();
2330 assert!(again.is_empty());
2331 }
2332
2333 #[test]
2334 fn datareader_read_preserves_samples() {
2335 let s = Subscriber::new(SubscriberQos::default(), None);
2336 let r = s
2337 .create_datareader::<RawBytes>(&mk_topic(), DataReaderQos::default())
2338 .unwrap();
2339 r.__push_raw(vec![0xAA]).unwrap();
2340 let first = r.read().unwrap();
2341 let second = r.read().unwrap();
2342 assert_eq!(first.len(), 1);
2343 assert_eq!(second.len(), 1);
2344 }
2345
2346 use core::sync::atomic::{AtomicU32, Ordering};
2349
2350 #[test]
2351 fn datareader_set_listener_stores_arc_and_mask() {
2352 struct L;
2353 impl crate::listener::DataReaderListener for L {}
2354 let s = Subscriber::new(SubscriberQos::default(), None);
2355 let r = s
2356 .create_datareader::<RawBytes>(&mk_topic(), DataReaderQos::default())
2357 .unwrap();
2358 assert!(r.get_listener().is_none());
2359 r.set_listener(Some(Arc::new(L)), crate::psm_constants::status::ANY);
2360 assert!(r.get_listener().is_some());
2361 assert_eq!(
2362 r.entity_state.listener_mask(),
2363 crate::psm_constants::status::ANY
2364 );
2365 }
2366
2367 #[test]
2368 fn poll_subscription_matched_fires_on_count_increase() {
2369 struct Cnt(AtomicU32);
2370 impl crate::listener::DataReaderListener for Cnt {
2371 fn on_subscription_matched(
2372 &self,
2373 _r: crate::InstanceHandle,
2374 _s: crate::status::SubscriptionMatchedStatus,
2375 ) {
2376 self.0.fetch_add(1, Ordering::Relaxed);
2377 }
2378 }
2379 let s = Subscriber::new(SubscriberQos::default(), None);
2380 let r = s
2381 .create_datareader::<RawBytes>(&mk_topic(), DataReaderQos::default())
2382 .unwrap();
2383 let cnt = Arc::new(Cnt(AtomicU32::new(0)));
2384 r.set_listener(Some(cnt.clone()), crate::psm_constants::status::ANY);
2385
2386 r.poll_subscription_matched(0);
2387 assert_eq!(cnt.0.load(Ordering::Relaxed), 1);
2388 r.poll_subscription_matched(1);
2389 assert_eq!(cnt.0.load(Ordering::Relaxed), 2);
2390 r.poll_subscription_matched(1);
2391 assert_eq!(cnt.0.load(Ordering::Relaxed), 2);
2392 r.poll_subscription_matched(0);
2393 assert_eq!(cnt.0.load(Ordering::Relaxed), 3);
2394 }
2395
2396 #[test]
2397 fn poll_subscription_matched_with_no_listener_is_noop() {
2398 let s = Subscriber::new(SubscriberQos::default(), None);
2399 let r = s
2400 .create_datareader::<RawBytes>(&mk_topic(), DataReaderQos::default())
2401 .unwrap();
2402 r.poll_subscription_matched(0);
2403 r.poll_subscription_matched(3);
2404 }
2405
2406 #[test]
2407 fn notify_data_arrived_fires_data_available_and_data_on_readers() {
2408 struct ReadCnt(AtomicU32, AtomicU32);
2409 impl crate::listener::DataReaderListener for ReadCnt {
2410 fn on_data_available(&self, _r: crate::InstanceHandle) {
2411 self.0.fetch_add(1, Ordering::Relaxed);
2412 }
2413 fn on_subscription_matched(
2414 &self,
2415 _r: crate::InstanceHandle,
2416 _s: crate::status::SubscriptionMatchedStatus,
2417 ) {
2418 self.1.fetch_add(1, Ordering::Relaxed);
2419 }
2420 }
2421 let s = Subscriber::new(SubscriberQos::default(), None);
2422 let r = s
2423 .create_datareader::<RawBytes>(&mk_topic(), DataReaderQos::default())
2424 .unwrap();
2425 let rc = Arc::new(ReadCnt(AtomicU32::new(0), AtomicU32::new(0)));
2426 r.set_listener(Some(rc.clone()), crate::psm_constants::status::ANY);
2427 r.notify_data_arrived();
2428 assert_eq!(rc.0.load(Ordering::Relaxed), 1);
2429 assert_eq!(rc.1.load(Ordering::Relaxed), 0);
2431 }
2432
2433 #[test]
2436 fn subscriber_begin_end_access_roundtrip() {
2437 let s = Subscriber::new(SubscriberQos::default(), None);
2438 assert!(!s.is_access_open());
2439 s.begin_access();
2440 assert!(s.is_access_open());
2441 s.end_access().unwrap();
2442 assert!(!s.is_access_open());
2443 }
2444
2445 #[test]
2446 fn subscriber_end_access_without_begin_returns_precondition_not_met() {
2447 let s = Subscriber::new(SubscriberQos::default(), None);
2449 let res = s.end_access();
2450 assert!(matches!(
2451 res,
2452 Err(crate::error::DdsError::PreconditionNotMet { .. })
2453 ));
2454 }
2455
2456 #[test]
2457 fn subscriber_begin_access_is_nestable() {
2458 let s = Subscriber::new(SubscriberQos::default(), None);
2461 s.begin_access();
2462 s.begin_access();
2463 assert!(s.is_access_open());
2464 s.end_access().unwrap();
2465 assert!(s.is_access_open());
2467 s.end_access().unwrap();
2468 assert!(!s.is_access_open());
2470 }
2471
2472 #[test]
2473 fn subscriber_too_many_ends_after_balanced_returns_error() {
2474 let s = Subscriber::new(SubscriberQos::default(), None);
2477 s.begin_access();
2478 s.end_access().unwrap();
2479 let res = s.end_access();
2480 assert!(matches!(
2481 res,
2482 Err(crate::error::DdsError::PreconditionNotMet { .. })
2483 ));
2484 }
2485}