Thank you for all the answers! What brought this question is actually a little different problem in my code, I am currently trying to write a Rust wrapper for C API that is split in two part:
First, there is a list of the Rust functions that are being called from the C code, (f1, f2...)
And second, there is a list of C API functions that are being called from Rust, (api1, api2...) and they in turn can recursively call f1, f2...
The problem appears because I also need to keep some global state across all these function calls. Right now I am doing it like this:
struct Global
{
}
thread_local!
(
static GLOBAL: RefCell<Global> = RefCell::new(Global
{
});
);
fn f1()
{
GLOBAL.with(|global|
{
let mut global=global.borrow_mut();
...
});
api1();
GLOBAL.with(|global|
{
let mut global=global.borrow_mut();
...
});
api2();
GLOBAL.with(|global|
{
let mut global=global.borrow_mut();
...
});
api3();
GLOBAL.with(|global|
{
let mut global=global.borrow_mut();
...
});
...
}
In f1 I am forced to borrow GLOBAL multiple times, because calls to the api functions can, in turn, lead to the recursive call of the f1.
This is cumbersome and I think is unnecessary, because all these calls are being done in the same thread. So I thought that maybe I can get away with a function wrappers done this way:
use std::ptr;
use std::sync::atomic::{AtomicPtr, Ordering};
struct Global
{
data: i32,
}
static mut GLOBAL:Global=Global{data:0};
static GLOBAL_PTR:AtomicPtr<Global>=AtomicPtr::new(ptr::null_mut());
fn api1() // this is actually a C function
{
f1();
}
fn api1_wrapper(global: &mut Global) // wrapper around call to C function
{
let global=GLOBAL_PTR.swap(global as *mut _, Ordering::Relaxed);
assert!(global.is_null());
api1();
let global=GLOBAL_PTR.swap(ptr::null_mut(), Ordering::Relaxed);
assert!(!global.is_null());
}
fn f1_wrapped(global: &mut Global) // actual logic is here
{
global.data+=1;
if global.data < 2
{
api1_wrapper(global);
global.data+=1;
api1_wrapper(global);
}
global.data+=1;
}
fn f1() // wrapper, called from C code
{
let global=GLOBAL_PTR.swap(ptr::null_mut(), std::sync::atomic::Ordering::Relaxed);
let global=unsafe{ global.as_mut().unwrap() };
f1_wrapped(global);
let g=GLOBAL_PTR.swap(global as *mut _, Ordering::Relaxed);
assert!(g.is_null());
}
fn init() // called only once, during initialization
{
unsafe
{
let ptr=&mut GLOBAL as *mut _;
GLOBAL_PTR.store(ptr, std::sync::atomic::Ordering::Relaxed);
}
}
fn main()
{
init();
f1();
let global=GLOBAL_PTR.swap(ptr::null_mut(), std::sync::atomic::Ordering::Relaxed);
let global=unsafe{ global.as_mut().unwrap() };
println!("{}", global.data);
}
So, will this work? Or should I stick with my current solution? Or maybe there is some other way to make working with the global state in recursive functions more convenient?