I'm building a system that calls into C libraries. One of the libraries handles device initialisation. It returns a pointer, and cannot be called more than once. Hence the use of lazy_static.
I have two questions, one is about assuring how to constrain construction across the lifetime of a system, and another about the contracts that come with unsafe impl Send/Sync for $raw_ptr
Here is a code snippet of what I'm dealing with
// ../rustlibs/serial/src/lib.rs
#![no_std]
#[derive(Copy, Clone)]
#[repr(C)]
struct SerialPtr(*const core::ffi::c_void);
extern "C" {
fn serial_send(c_library_struct: *const core::ffi::c_void, ...) -> usize;
fn serial_init() -> SerialPtr;
fn serial_register_handler(serial: SerialPtr, ...);
}
unsafe impl core::marker::Send for Serial {}
unsafe impl core::marker::Sync for Serial {}
pub struct Serial {
ptr: SerialPtr
}
// essentially, SERIAL must be a singleton for the entire system due to serial_init call count.
lazy_static! {
static ref SERIAL: Serial = unsafe {
let res = Serial {
ptr: serial_init(), // CRITICAL: must be called once, and once ONLY for entire system uptime
};
serial_register_handler(res.ptr, Some(default_handler));
res
};
}
impl Serial {
pub fn send(msg: &[u8]) -> usize {
unsafe { serial_send(SERIAL.ptr, ...) }
}
...
}
Here is the process flow of my system:
root-process start in C code-base (is always the first process after power-up, and last before power-down)
system initialisation from C code-base (not including serial_init)
spin up child process from C code-base(immediately sends request to root-process use of serial device. Blocks until root-process replys to request)
root-process calls into Rust code-base
root-proc recieves request from child
part of processing this uses Serial::send
For context of how I currently use it from the static library that C calls:
// ../rustlibs/root_service/src/lib.rs
#![no_std]
#[crate_type = "static_lib"]
use serial::*;
#[no_mangle]
fn entry_point() -> ! {
loop {
match blocking_recv(ipc_source, msg) {
// Derefs into SERIAL, thus init'ing the device on first match, replying with the result
SERIAL_SEND => reply(ipc_source, Serial::send(msg)),
...
}
}
}
Regarding my question of limiting serial_init() invocation to once-only system wide: If I make serial a static lib, will that mean there will be a fresh serial_init() invoked for each executable that links it? Does the same apply if I make it an rlib? If I make it dylib, what would that do to duplicate SERIALs within system memory?
Regarding my question of marking Serial with Send + Sync, what can I do with respect to ensuring that it's safe?
I'm building a system that calls into C libraries. One of the libraries handles device initialisation. It returns a pointer, and cannot be called more than once. Hence the use of lazy_static.
I have two questions, one is about assuring how to constrain construction across the lifetime of a system, and another about the contracts that come with
unsafe impl Send/Sync for $raw_ptr
Here is a code snippet of what I'm dealing with
Here is the process flow of my system:
Serial::send
For context of how I currently use it from the static library that C calls:
Regarding my question of limiting
serial_init()
invocation to once-only system wide: If I makeserial
a static lib, will that mean there will be a freshserial_init()
invoked for each executable that links it? Does the same apply if I make it anrlib
? If I make itdylib
, what would that do to duplicate SERIALs within system memory?Regarding my question of marking
Serial
withSend + Sync
, what can I do with respect to ensuring that it's safe?