Use * const u8 instead of * const c_void

The way cxx bridge works, it doesn't recognize any types from another module as
being shared cxx bridge types with generations native to both C++ and Rust,
meaning every module that was going to use function pointers would have to
define its own `c_void` type (because cxx bridge doesn't recognize any of
libc::c_void, std::ffi::c_void, or autocxx::c_void).

FFI on other platforms has long used the equivalent of `uint8_t *` as an
alternative to `void *` for code where `void` was not available or was
undesirable for some reason. We can join the club - this way we can always use
`* {const|mut} u8` in our rust code and `uint8_t *` in our C++ code to pass
around parameters or values over the C abi.
This commit is contained in:
Mahmoud Al-Qudsi 2023-02-18 12:52:58 -06:00
parent 4f6fe0999e
commit aaf2d1c19d
4 changed files with 34 additions and 21 deletions

View File

@ -3,7 +3,7 @@ use std::sync::atomic::{AtomicU64, Ordering};
use std::sync::{Arc, Mutex};
use std::time::{Duration, Instant};
use self::fd_monitor::{c_void, new_fd_event_signaller, FdEventSignaller, ItemWakeReason};
use self::fd_monitor::{new_fd_event_signaller, FdEventSignaller, ItemWakeReason};
use crate::fd_readable_set::FdReadableSet;
use crate::fds::AutoCloseFd;
use crate::ffi::void_ptr;
@ -25,11 +25,6 @@ mod fd_monitor {
Poke,
}
// Defines and exports a type shared between C++ and rust
struct c_void {
_unused: u8,
}
unsafe extern "C++" {
include!("fds.h");
@ -59,8 +54,8 @@ mod fd_monitor {
fn new_fd_monitor_item_ffi(
fd: i32,
timeout_usecs: u64,
callback: *const c_void,
param: *const c_void,
callback: *const u8,
param: *const u8,
) -> Box<FdMonitorItem>;
}
@ -76,8 +71,8 @@ mod fd_monitor {
&mut self,
fd: i32,
timeout_usecs: u64,
callback: *const c_void,
param: *const c_void,
callback: *const u8,
param: *const u8,
) -> u64;
#[cxx_name = "poke_item"]
@ -221,13 +216,13 @@ impl FdMonitorItem {
}
}
fn set_callback_ffi(&mut self, callback: *const c_void, param: *const c_void) {
fn set_callback_ffi(&mut self, callback: *const u8, param: *const u8) {
// Safety: we are just marshalling our function pointers with identical definitions on both
// sides of the ffi bridge as void pointers to keep cxx bridge happy. Whether we invoke the
// raw function as a void pointer or as a typed fn that helps us keep track of what we're
// doing is unsafe in all cases, so might as well make the best of it.
let callback = unsafe { std::mem::transmute(callback) };
self.callback = FdMonitorCallback::Ffi(callback, void_ptr(param as _));
self.callback = FdMonitorCallback::Ffi(callback, param.into());
}
}
@ -240,8 +235,8 @@ fn new_fd_monitor_ffi() -> Box<FdMonitor> {
fn new_fd_monitor_item_ffi(
fd: RawFd,
timeout_usecs: u64,
callback: *const c_void,
param: *const c_void,
callback: *const u8,
param: *const u8,
) -> Box<FdMonitorItem> {
// Safety: we are just marshalling our function pointers with identical definitions on both
// sides of the ffi bridge as void pointers to keep cxx bridge happy. Whether we invoke the
@ -250,7 +245,7 @@ fn new_fd_monitor_item_ffi(
let callback = unsafe { std::mem::transmute(callback) };
let mut item = FdMonitorItem::new();
item.fd.reset(fd);
item.callback = FdMonitorCallback::Ffi(callback, void_ptr(param as _));
item.callback = FdMonitorCallback::Ffi(callback, param.into());
if timeout_usecs != FdReadableSet::kNoTimeout {
item.timeout = Some(Duration::from_micros(timeout_usecs));
}
@ -354,8 +349,8 @@ impl FdMonitor {
&mut self,
fd: RawFd,
timeout_usecs: u64,
callback: *const c_void,
param: *const c_void,
callback: *const u8,
param: *const u8,
) -> u64 {
// Safety: we are just marshalling our function pointers with identical definitions on both
// sides of the ffi bridge as void pointers to keep cxx bridge happy. Whether we invoke the
@ -364,7 +359,7 @@ impl FdMonitor {
let callback = unsafe { std::mem::transmute(callback) };
let mut item = FdMonitorItem::new();
item.fd.reset(fd);
item.callback = FdMonitorCallback::Ffi(callback, void_ptr(param as _));
item.callback = FdMonitorCallback::Ffi(callback, param.into());
if timeout_usecs != FdReadableSet::kNoTimeout {
item.timeout = Some(Duration::from_micros(timeout_usecs));
}

View File

@ -151,3 +151,21 @@ impl core::fmt::Debug for void_ptr {
unsafe impl Send for void_ptr {}
unsafe impl Sync for void_ptr {}
impl core::convert::From<*const core::ffi::c_void> for void_ptr {
fn from(value: *const core::ffi::c_void) -> Self {
Self(value as *const _)
}
}
impl core::convert::From<*const u8> for void_ptr {
fn from(value: *const u8) -> Self {
Self(value as *const _)
}
}
impl core::convert::From<*const autocxx::c_void> for void_ptr {
fn from(value: *const autocxx::c_void) -> Self {
Self(value as *const _)
}
}

View File

@ -835,7 +835,7 @@ static void test_fd_monitor() {
}
}
static void trampoline(autoclose_fd_t2 &fd, item_wake_reason_t reason, c_void *param) {
static void trampoline(autoclose_fd_t2 &fd, item_wake_reason_t reason, uint8_t *param) {
auto &instance = *(item_maker_t*)(param);
instance.callback(fd, reason);
}
@ -843,7 +843,7 @@ static void test_fd_monitor() {
explicit item_maker_t(uint64_t timeout_usec) {
auto pipes = make_autoclose_pipes().acquire();
writer = std::move(pipes.write);
item = std::make_unique<rust::Box<fd_monitor_item_t>>(make_fd_monitor_item_t(pipes.read.acquire(), timeout_usec, (c_void *)item_maker_t::trampoline, (c_void*)this));
item = std::make_unique<rust::Box<fd_monitor_item_t>>(make_fd_monitor_item_t(pipes.read.acquire(), timeout_usec, (uint8_t *)item_maker_t::trampoline, (uint8_t*)this));
}
// Write 42 bytes to our write end.

View File

@ -121,7 +121,7 @@ void io_buffer_t::begin_filling(autoclose_fd_t fd) {
args->promise = std::move(promise);
item_id_ =
fd_monitor().add_item(fd.acquire(), kNoTimeout, (::c_void *)item_callback_trampoline, (::c_void *)args);
fd_monitor().add_item(fd.acquire(), kNoTimeout, (uint8_t *)item_callback_trampoline, (uint8_t *)args);
}
/// This is a hack to work around the difficulties in passing a capturing lambda across FFI