Hi,
I'm trying to write an rlib that will provide a nice interface to a fairly old chunk of C code. The way this code is structured is making it rather difficult and I could use some advice. Apologies about all the words and code up front.
The C code I'm looking to wrap minifies down to something like this:
// problem.c
int a = 0;
extern void foo(int);
extern void bar(int);
void quux(void) {
++a;
foo(a);
// 20 years of optimized C code here that does stuff
bar(a);
}
foo
and bar
will be rust functions I will provide and link in the resulting rlib. I cannot control parameters to foo
or bar
. Additionally they're on a hot path and very performance sensitive.
I am interested in consumers of my library to be able to provide a callbacks which will execute on foo
and bar
, effectively muxing foo
and bar
. E.g
// quux.rs
extern "C" {
fn quux();
}
static mut FOO_HOOKS = Vec<Box<FnMut(u32)>> = Vec::new();
pub unsafe fn hook_foo<T: FnMut(u32) + 'static>(h : T) {
FOO_HOOKS.push(Box::new(h));
}
#[no_mangle]
extern "C" foo(a: u32) {
unsafe { FOO_HOOKS.iter_mut().for_each(|x| x(a)) }
}
pub unsafe fn do_quux() {
quux()
}
// consumer.rs
use quux::{hook_foo, hook_bar, do_quux};
fn main() {
unsafe {
hook_foo(|a| {
println!("{}", a);
});
do_quux();
}
}
I understand this is wildly race-y, however the C code is not threaded and will call foo
and bar
synchronously meaning this code currently should be no worse than the corresponding C code I would have written.
My issue comes when I want to have the hooks share mutable state, essentially something like this rust pseudocode:
// my-wishlist.rs
let mut baz = Vec::new();
unsafe {
hook_foo(|a| {
baz.push(a);
});
hook_bar(|a| {
baz.push(a);
});
do_quux();
}
This (again assuming do_quux
is only called once at any given time and nothing else reads or writes baz
) should be safe as foo
and bar
cannot be executed concurrently. Obviously it wont compile for a few reasons.
My main question is how to best implement something roughly similar to this, given the constraints that we can't modify the C or introduce unneeded perf hits. I don't think there is a good solution, given the way the C code is designed, so I'm mostly looking for whatever's least bad. Right now I'm just passing it through with raw pointers, but would welcome any improvements!
Hopefully I haven't missed anything obvious, and thanks for taking the time to read all this!