Skip to main content

visa_rs/
handler.rs

1//!
2//! Defines [`Callback`] trait used in [`Instrument::install_handler`](crate::Instrument::install_handler),
3//! which returns a [`Handler`] to manage lifetime of data passed
4//!
5//!
6//!
7
8use std::{
9    ptr::NonNull,
10    sync::mpsc::{Receiver, Sender},
11};
12use visa_sys as vs;
13
14use crate::{
15    enums::event,
16    session::{AsRawSs, BorrowedSs, FromRawSs},
17    Instrument, Result, SUCCESS,
18};
19
20/// Defines the ability for being passed to [`Instrument::install_handler`](crate::Instrument::install_handler)
21///
22/// `Send` is required on both the callback and its output because visa invokes handlers
23/// on its own thread and the output is delivered to the installing thread through a
24/// channel -- neither crosses that boundary safely otherwise.
25pub trait Callback: Send {
26    type Output: Send;
27    fn call(&mut self, instr: &Instrument, event: &event::Event) -> Self::Output;
28}
29
30impl<F, Out> Callback for F
31where
32    F: FnMut(&Instrument, &event::Event) -> Out + Send,
33    Out: Send,
34{
35    type Output = Out;
36    fn call(&mut self, instr: &Instrument, event: &event::Event) -> Self::Output {
37        self(instr, event)
38    }
39}
40
41struct CallbackPack<F: Callback> {
42    sender: Sender<F::Output>,
43    core: F,
44}
45
46impl<F: Callback> CallbackPack<F> {
47    fn from_callback(f: F) -> (Self, Receiver<F::Output>) {
48        let (sender, receiver) = std::sync::mpsc::channel();
49        (Self { sender, core: f }, receiver)
50    }
51    fn call(&mut self, instr: &Instrument, event: &event::Event) -> vs::ViStatus {
52        //Normally, an application should always return VI_SUCCESS from all callback handlers. If a specific handler does not want other handlers to be invoked for the given event for the given session, it should return VI_SUCCESS_NCHAIN. No return value from a handler on one session will affect callbacks on other sessions. Future versions of VISA (or specific implementations of VISA) may take actions based on other return values, so a user should return VI_SUCCESS from handlers unless there is a specific reason to do otherwise.
53        self.sender
54            .send(self.core.call(instr, event))
55            .expect("receiver side should be valid");
56        SUCCESS
57    }
58}
59
60struct CallbackWrapper<F: Callback> {
61    f: NonNull<CallbackPack<F>>,
62    //? not sure if reproduce from F would get the same fn pointer, so better hold it
63    hold: unsafe extern "system" fn(
64        vs::ViSession,
65        vs::ViEventType,
66        vs::ViEvent,
67        *mut std::ffi::c_void,
68    ) -> vs::ViStatus,
69}
70fn split_pack<C: Callback>(
71    pack: CallbackPack<C>,
72) -> (
73    std::ptr::NonNull<CallbackPack<C>>,
74    unsafe extern "system" fn(
75        vs::ViSession,
76        vs::ViEventType,
77        vs::ViEvent,
78        *mut std::ffi::c_void,
79    ) -> vs::ViStatus,
80) {
81    use std::ffi::c_void;
82    let data = Box::into_raw(Box::new(pack));
83    unsafe extern "system" fn trampoline<T: Callback>(
84        instr: vs::ViSession,
85        event_type: vs::ViEventType,
86        event: vs::ViEvent,
87        user_data: *mut c_void,
88    ) -> vs::ViStatus {
89        // Unwinding out of an `extern "system"` fn aborts the process, so a panicking user
90        // callback is contained here. `ManuallyDrop` keeps a panic from running `Drop` on
91        // the borrowed session or on an event context visa frees itself.
92        let ret = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
93            let pack: &mut CallbackPack<T> = &mut *(user_data as *mut CallbackPack<T>);
94            let instr = std::mem::ManuallyDrop::new(Instrument::from_raw_ss(instr));
95            let event = std::mem::ManuallyDrop::new(event::Event::new(event, event_type));
96            pack.call(&instr, &event)
97        }));
98        match ret {
99            Ok(ret) => ret,
100            Err(_) => {
101                log::error!("panic in visa event handler, ignored");
102                SUCCESS
103            }
104        }
105    }
106
107    (
108        NonNull::new(data).expect("impossible to pass in a null ptr"),
109        trampoline::<C>,
110    )
111}
112impl<F: Callback> CallbackWrapper<F> {
113    pub(crate) fn new(f: F) -> (Self, Receiver<F::Output>) {
114        let (pack, receiver) = CallbackPack::from_callback(f);
115        let (data, fun) = split_pack(pack);
116        (Self { f: data, hold: fun }, receiver)
117    }
118}
119
120/// Lifetime manager for [`Callback`], will uninstall the callback when dropped.
121///
122/// Internally hold a [`Receiver`] (accessed by [`Self::receiver`]) to receive output of callback from visa.
123pub struct Handler<'b, F: Callback> {
124    instr: BorrowedSs<'b>,
125    rec: Receiver<F::Output>,
126    event_kind: event::EventKind,
127    callback: CallbackWrapper<F>,
128}
129
130impl<'b, F: Callback> Handler<'b, F> {
131    pub(crate) fn new(
132        instr: BorrowedSs<'b>,
133        event_kind: event::EventKind,
134        callback: F,
135    ) -> Result<Self> {
136        let (callback, rec) = CallbackWrapper::new(callback);
137        super::wrap_raw_error_in_unsafe!(vs::viInstallHandler(
138            instr.as_raw_ss(),
139            event_kind as _,
140            Some(callback.hold),
141            callback.f.as_ptr() as _
142        ))?;
143        Ok(Self {
144            instr,
145            rec,
146            event_kind,
147            callback,
148        })
149    }
150}
151
152impl<'b, F: Callback> Drop for Handler<'b, F> {
153    fn drop(&mut self) {
154        unsafe {
155            vs::viUninstallHandler(
156                self.instr.as_raw_ss(),
157                self.event_kind as _,
158                Some(self.callback.hold),
159                self.callback.f.as_ptr() as _,
160            );
161            drop(Box::from_raw(self.callback.f.as_ptr()));
162        }
163    }
164}
165
166impl<'b, F: Callback> Handler<'b, F> {
167    pub fn uninstall(self) {}
168}
169
170impl<'b, F: Callback> AsRef<Receiver<F::Output>> for Handler<'b, F> {
171    fn as_ref(&self) -> &Receiver<F::Output> {
172        &self.rec
173    }
174}
175
176impl<'b, F: Callback> Handler<'b, F> {
177    pub fn receiver(&self) -> &Receiver<F::Output> {
178        self.as_ref()
179    }
180}