use crate::entity::OwnedHandle;
use crate::{DataReader, DdsEntity, DdsResult, DdsType, WaitSet};
use cyclonedds_rust_sys::*;
const DEFAULT_BATCH: usize = 256;
const READER_COOKIE: i64 = 0;
const INTERRUPT_COOKIE: i64 = 1;
struct StreamWaitSet(WaitSet);
impl Drop for StreamWaitSet {
fn drop(&mut self) {
let _ = self.0.set_trigger(true);
}
}
unsafe fn drain<T: DdsType>(
entity: dds_entity_t,
take: bool,
max_samples: usize,
) -> DdsResult<Vec<T>> {
let mut samples: Vec<*mut std::ffi::c_void> = vec![std::ptr::null_mut(); max_samples];
let mut infos: Vec<dds_sample_info> = vec![std::mem::zeroed(); max_samples];
let n = if take {
dds_take(
entity,
samples.as_mut_ptr(),
infos.as_mut_ptr() as *mut dds_sample_info_t,
max_samples,
max_samples as u32,
)
} else {
dds_read(
entity,
samples.as_mut_ptr(),
infos.as_mut_ptr() as *mut dds_sample_info_t,
max_samples,
max_samples as u32,
)
};
if n < 0 {
return Err(crate::DdsError::from(n));
}
let n = n as usize;
let mut result = Vec::with_capacity(n);
for i in 0..n {
if infos[i].valid_data && !samples[i].is_null() {
if let Ok(data) = T::clone_out(samples[i] as *const T) {
result.push(data);
}
}
}
let _ = dds_return_loan(entity, samples.as_mut_ptr(), n as i32);
Ok(result)
}
#[cfg(feature = "async")]
impl WaitSet {
#[cfg_attr(feature = "tracing", tracing::instrument(skip(self)))]
pub async fn wait_async(&self, timeout_ns: i64) -> DdsResult<Vec<i64>> {
let owned = self.owned().clone();
tokio::task::spawn_blocking(move || {
let max_results: usize = 64;
let mut xs: Vec<dds_attach_t> = vec![0; max_results];
let n = unsafe {
dds_waitset_wait(owned.handle(), xs.as_mut_ptr(), max_results, timeout_ns)
};
drop(owned);
if n < 0 {
return Err(crate::DdsError::from(n));
}
let n = n as usize;
xs.truncate(n);
Ok(xs.into_iter().map(|x| x as i64).collect())
})
.await
.map_err(|e| crate::DdsError::Other(e.to_string()))?
}
}
#[cfg(feature = "async")]
impl<T: DdsType> DataReader<T> {
#[cfg_attr(feature = "tracing", tracing::instrument(skip(self)))]
pub async fn take_async(&self) -> DdsResult<Vec<T>> {
unsafe { drain::<T>(self.entity(), true, DEFAULT_BATCH) }
}
fn sample_stream(
&self,
take: bool,
max_samples: usize,
timeout_ns: i64,
) -> impl futures_core::Stream<Item = DdsResult<Vec<T>>> + '_ {
let entity = self.entity();
let owner = self.owned().clone();
async_stream::try_stream! {
let participant = unsafe { dds_get_participant(entity) };
let waitset = WaitSet::for_parents(participant, vec![owner])?;
waitset.attach(entity, READER_COOKIE)?;
waitset.attach(waitset.entity(), INTERRUPT_COOKIE)?;
let waitset = StreamWaitSet(waitset);
loop {
let triggered = waitset.0.wait_async(timeout_ns).await?;
if triggered.contains(&INTERRUPT_COOKIE) {
break;
}
if triggered.is_empty() {
yield Vec::new();
continue;
}
yield unsafe { drain::<T>(entity, take, max_samples) }?;
}
}
}
#[cfg_attr(feature = "tracing", tracing::instrument(skip(self)))]
pub fn read_aiter(&self) -> impl futures_core::Stream<Item = DdsResult<Vec<T>>> + '_ {
self.sample_stream(false, DEFAULT_BATCH, dds_duration_t::MAX)
}
#[cfg_attr(feature = "tracing", tracing::instrument(skip(self)))]
pub fn read_aiter_batch(
&self,
max_samples: usize,
) -> impl futures_core::Stream<Item = DdsResult<Vec<T>>> + '_ {
self.sample_stream(false, max_samples, dds_duration_t::MAX)
}
#[cfg_attr(feature = "tracing", tracing::instrument(skip(self)))]
pub fn take_aiter(&self) -> impl futures_core::Stream<Item = DdsResult<Vec<T>>> + '_ {
self.sample_stream(true, DEFAULT_BATCH, dds_duration_t::MAX)
}
pub fn take_aiter_batch(
&self,
max_samples: usize,
) -> impl futures_core::Stream<Item = DdsResult<Vec<T>>> + '_ {
self.sample_stream(true, max_samples, dds_duration_t::MAX)
}
pub fn read_aiter_timeout(
&self,
timeout_ns: i64,
) -> impl futures_core::Stream<Item = DdsResult<Vec<T>>> + '_ {
self.sample_stream(false, DEFAULT_BATCH, timeout_ns)
}
pub fn read_aiter_batch_timeout(
&self,
max_samples: usize,
timeout_ns: i64,
) -> impl futures_core::Stream<Item = DdsResult<Vec<T>>> + '_ {
self.sample_stream(false, max_samples, timeout_ns)
}
pub fn take_aiter_batch_timeout(
&self,
max_samples: usize,
timeout_ns: i64,
) -> impl futures_core::Stream<Item = DdsResult<Vec<T>>> + '_ {
self.sample_stream(true, max_samples, timeout_ns)
}
pub fn take_aiter_timeout(
&self,
timeout_ns: i64,
) -> impl futures_core::Stream<Item = DdsResult<Vec<T>>> + '_ {
self.sample_stream(true, DEFAULT_BATCH, timeout_ns)
}
}