stateful_callback/
stateful_callback.rs1use std::collections::BTreeSet;
17use std::sync::atomic::{AtomicUsize, Ordering};
18use std::sync::{Arc, Mutex};
19use std::time::Duration;
20
21use opendaq::{Channel, Instance, Procedure, StreamReader};
22
23fn main() -> opendaq::Result<()> {
24 let instance = Instance::new()?;
25 let device = instance.add_device("daqref://device0")?.expect("device");
26 let channel = instance
27 .find_component("Dev/RefDev0/IO/AI/RefCh0")?
28 .expect("reference channel not found")
29 .cast::<Channel>()?;
30 device.set_property_value("GlobalSampleRate", 1000)?;
31 let signal = &channel.signals()?[0];
32
33 let reader = StreamReader::<f64>::new(signal)?;
34
35 let notifications = Arc::new(AtomicUsize::new(0));
40 let caller_threads = Arc::new(Mutex::new(BTreeSet::<String>::new()));
41
42 let notifications_cb = Arc::clone(¬ifications);
45 let caller_threads_cb = Arc::clone(&caller_threads);
46 let on_ready = Procedure::from_fn(move |_args| {
47 notifications_cb.fetch_add(1, Ordering::Relaxed);
48 let thread = format!("{:?}", std::thread::current().id());
49 caller_threads_cb.lock().unwrap().insert(thread);
50 Ok(())
51 })?;
52 reader.set_on_data_available(&on_ready)?;
53
54 let mut total_samples = 0usize;
58 let mut running_sum = 0.0f64;
59 for _ in 0..10 {
60 let samples = reader.read(1000, 200)?;
61 total_samples += samples.sample_count();
62 running_sum += samples.iter().sum::<f64>();
63 std::thread::sleep(Duration::from_millis(50));
64 }
65
66 let fired = notifications.load(Ordering::Relaxed);
68 let caller_threads = caller_threads.lock().unwrap();
69 let main_thread = format!("{:?}", std::thread::current().id());
70 let mean = if total_samples > 0 {
71 running_sum / total_samples as f64
72 } else {
73 0.0
74 };
75
76 println!("Read {total_samples} samples (mean {mean:.4}).");
77 println!("The data-ready callback fired {fired} time(s) while we read.");
78 println!("Main thread: {main_thread}");
79 println!("Callback ran on: {caller_threads:?}");
80 if caller_threads.iter().any(|t| *t != main_thread) {
81 println!(
82 "The callback ran on an openDAQ scheduler thread -- which is why its \
83 closure must be Send + Sync and its state shared through Arc/atomics."
84 );
85 } else {
86 println!(
87 "openDAQ happened to notify us on the calling thread this run, but it is \
88 free to use its own worker threads, so the Send + Sync / shared-state \
89 contract still applies."
90 );
91 }
92
93 Ok(())
94}