On Tue, Nov 26, 2024 at 6:48 PM Paolo Bonzini <pbonz...@redhat.com> wrote: > Callbacks > ''''''''' > > This is the least mature of the "urgent" changes, and perhaps the more > important to have a good design for. PL011 has callbacks for character > devices and memory regions, but other usecases include timers, bottom > halves and interrupt sinks (aka GPIO inputs).
Ok, I have an idea that should make it possible to write something like this: pub struct Timer { t: *mut QEMUTimer, } impl Timer { // FnCall is QEMU-specific and contains all the magic. unsafe extern "C" fn rust_timer_cb< 'a, T, F: FnCall<(&'a T,)>>(opaque: *mut c_void) { // The argument to F::call is a tuple, otherwise this is fairly expected F::call((unsafe { &*(opaque.cast::<T>()) },)) } #[inline] fn new_ns<'a, T, F: FnCall<(&'a T,)>>( clk: QEMUClockType, _cb: F, opaque: &'a T, ) -> Timer { let cb: unsafe extern "C" fn(*mut c_void) = rust_timer_cb::<'a, T, F>; Timer { t: unsafe { bindings::timer_new_ns(clk, cb, opaque) }, } } } and then the caller can simply write self.timer = Timer::new_ns(QEMU_CLOCK_VIRTUAL, Self::timer_update, self); The idea is that the compiler will generate a different version of rust_timer_cb for every value of the second argument (which is unused, except inasmuch as it helps the compiler know what "F::call" means). In the code above "unsafe" is only needed for the unavoidable casts and FFI calls, while the device has no unsafe at all. I haven't written the timer bindings but the basic idea is like the above. It can be expanded once Zhao posts his HPET conversion. It may also be possible to use macros to make things a little less verbose, but I'm tending to err on the side of explicitness for now. I'm also not sure about the applicability of this trick to character devices and memory regions, since those have multiple callbacks and/or callbacks in structs rather than function parameters. However, for timers this is a good option I think. Paolo