Rust无法给回调新增参数时如何访问含非Send类型的全局状态
解决方案
单线程场景快速实现
使用thread_local!定义线程局部静态变量,线程局部存储不需要满足Send/Sync约束,完全适配当前单线程运行的场景,不需要修改FFI结构体的安全标记:
use std::cell::RefCell; use std::collections::HashMap; use std::time::Instant; use lib::bar; struct Struct2{ foo: bar, word: String, } struct GlobalState{ running: bool, now: Instant, map: HashMap<String, Struct2>, hook_id: usize, current_id: String, } // 线程局部静态变量,仅初始化线程可访问 thread_local! { static GLOBAL_STATE: RefCell<GlobalState> = RefCell::new(GlobalState::init()); } impl GlobalState{ fn init() -> Self{ let hook_id = unsafe { set_ext_hook(HOOK_ID, system_hook) }; Self { running: false, now: Instant::now(), map: Default::default(), hook_id, current_id: Default::default(), } } } unsafe extern "system" fn system_hook(ext_param1:usize, ext_param2: usize) -> isize { GLOBAL_STATE.with(|state| { let mut state = state.borrow_mut(); // 直接读写全局状态即可 state.running = true; }); 0 }
多线程兼容实现
如果后续需要新增GUI线程跨线程访问状态,可以手动为包含非安全标记FFI结构体的GlobalState实现Send/Sync,只需确认在加锁访问的场景下lib::bar不会产生数据竞争:
use once_cell::sync::Lazy; use std::sync::Mutex; use std::collections::HashMap; use std::time::Instant; use lib::bar; struct Struct2{ foo: bar, word: String, } struct GlobalState{ running: bool, now: Instant, map: HashMap<String, Struct2>, hook_id: usize, current_id: String, } // 手动标记线程安全,需自行确认lib::bar加锁访问无并发问题 unsafe impl Send for GlobalState {} unsafe impl Sync for GlobalState {} static GLOBAL_STATE: Lazy<Mutex<GlobalState>> = Lazy::new(|| { Mutex::new(GlobalState::init()) }); impl GlobalState{ fn init() -> Self{ let hook_id = unsafe { set_ext_hook(HOOK_ID, system_hook) }; Self { running: false, now: Instant::now(), map: Default::default(), hook_id, current_id: Default::default(), } } } unsafe extern "system" fn system_hook(ext_param1:usize, ext_param2: usize) -> isize { let mut state = GLOBAL_STATE.lock().unwrap(); // 读写全局状态 state.running = true; 0 }
注意事项
- 线程局部变量只能在初始化它的线程中访问,跨线程调用会触发panic,适合单线程快速验证逻辑使用
- 手动实现
Send/Sync属于unsafe操作,必须确认lib::bar的内部实现不会在加锁并发访问时产生数据竞争,常见的FFI导出无内部共享状态的类型都满足该要求 - 全局状态必须通过内部可变性容器包裹:单线程用
RefCell,多线程用Mutex/RwLock,否则无法执行修改操作
内容的提问来源于stack exchange,提问作者Ahmad AlHallak
相关产品推荐
相关产品推荐

