refactor(fileio): move compression teardown order to Rust
Keep fileio's compression-resource teardown order in the Rust policy layer: dictionary, write pool, read pool, then compression context. The C adapter keeps cRess_t and each private resource layout local while exposing only one callback projection to Rust, preserving the existing cleanup behavior. The Rust entry point also treats a null state as a no-op and verifies the projection layout at compile time. Test Plan: - ulimit -v 41943040; CARGO_BUILD_JOBS=1 cargo test --manifest-path rust/cli/Cargo.toml free_c_resources --lib - ulimit -v 41943040; make -j1 - ulimit -v 41943040; CARGO_BUILD_JOBS=1 cargo test --manifest-path rust/cli/Cargo.toml --all-targets - ulimit -v 41943040; CARGO_BUILD_JOBS=1 cargo clippy --manifest-path rust/cli/Cargo.toml --all-targets -- -D warnings
This commit is contained in:
+53
-4
@@ -1820,12 +1820,61 @@ static cRess_t FIO_createCResources(FIO_prefs_t* const prefs,
|
||||
return ress;
|
||||
}
|
||||
|
||||
typedef void (*FIO_rust_freeCResourcesCallback_f)(void* context);
|
||||
typedef struct {
|
||||
void* callbackContext;
|
||||
FIO_rust_freeCResourcesCallback_f freeDict;
|
||||
FIO_rust_freeCResourcesCallback_f freeWritePool;
|
||||
FIO_rust_freeCResourcesCallback_f freeReadPool;
|
||||
FIO_rust_freeCResourcesCallback_f freeCctx;
|
||||
} FIO_rust_freeCResourcesState;
|
||||
typedef char FIO_rust_free_c_resources_state_layout[
|
||||
(offsetof(FIO_rust_freeCResourcesState, callbackContext) == 0
|
||||
&& offsetof(FIO_rust_freeCResourcesState, freeDict) == sizeof(void*)
|
||||
&& offsetof(FIO_rust_freeCResourcesState, freeWritePool)
|
||||
== 2 * sizeof(void*)
|
||||
&& offsetof(FIO_rust_freeCResourcesState, freeReadPool)
|
||||
== 3 * sizeof(void*)
|
||||
&& offsetof(FIO_rust_freeCResourcesState, freeCctx)
|
||||
== 4 * sizeof(void*)
|
||||
&& sizeof(FIO_rust_freeCResourcesState) == 5 * sizeof(void*))
|
||||
? 1 : -1];
|
||||
void FIO_rust_freeCResources(const FIO_rust_freeCResourcesState* state);
|
||||
|
||||
static void FIO_rust_freeCResources_dict(void* context)
|
||||
{
|
||||
cRess_t* const ress = (cRess_t*)context;
|
||||
FIO_freeDict(&(ress->dict));
|
||||
}
|
||||
|
||||
static void FIO_rust_freeCResources_writePool(void* context)
|
||||
{
|
||||
cRess_t* const ress = (cRess_t*)context;
|
||||
AIO_WritePool_free(ress->writeCtx);
|
||||
}
|
||||
|
||||
static void FIO_rust_freeCResources_readPool(void* context)
|
||||
{
|
||||
cRess_t* const ress = (cRess_t*)context;
|
||||
AIO_ReadPool_free(ress->readCtx);
|
||||
}
|
||||
|
||||
static void FIO_rust_freeCResources_cctx(void* context)
|
||||
{
|
||||
cRess_t* const ress = (cRess_t*)context;
|
||||
ZSTD_freeCStream(ress->cctx); /* never fails */
|
||||
}
|
||||
|
||||
static void FIO_freeCResources(cRess_t* const ress)
|
||||
{
|
||||
FIO_freeDict(&(ress->dict));
|
||||
AIO_WritePool_free(ress->writeCtx);
|
||||
AIO_ReadPool_free(ress->readCtx);
|
||||
ZSTD_freeCStream(ress->cctx); /* never fails */
|
||||
FIO_rust_freeCResourcesState const state = {
|
||||
ress,
|
||||
FIO_rust_freeCResources_dict,
|
||||
FIO_rust_freeCResources_writePool,
|
||||
FIO_rust_freeCResources_readPool,
|
||||
FIO_rust_freeCResources_cctx
|
||||
};
|
||||
FIO_rust_freeCResources(&state);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
|
||||
use std::ffi::{c_void, CStr};
|
||||
use std::io::{self, Write};
|
||||
use std::mem::size_of;
|
||||
use std::mem::{offset_of, size_of};
|
||||
use std::os::raw::{c_char, c_int, c_uint};
|
||||
use std::ptr;
|
||||
|
||||
@@ -103,6 +103,50 @@ pub struct FIO_listFileInfoProjection_t {
|
||||
nbFiles: u32,
|
||||
}
|
||||
|
||||
type FIO_freeCResourcesCallbackFn = unsafe extern "C" fn(*mut c_void);
|
||||
|
||||
/// C-owned compression-resource teardown projection.
|
||||
///
|
||||
/// Rust owns the stable cleanup order while C keeps `cRess_t`, dictionary,
|
||||
/// asynchronous-pool, and stream layouts private behind callbacks.
|
||||
#[repr(C)]
|
||||
pub struct FIO_rust_freeCResourcesState {
|
||||
callback_context: *mut c_void,
|
||||
free_dict: FIO_freeCResourcesCallbackFn,
|
||||
free_write_pool: FIO_freeCResourcesCallbackFn,
|
||||
free_read_pool: FIO_freeCResourcesCallbackFn,
|
||||
free_cctx: FIO_freeCResourcesCallbackFn,
|
||||
}
|
||||
|
||||
const _: () = {
|
||||
assert!(offset_of!(FIO_rust_freeCResourcesState, callback_context) == 0);
|
||||
assert!(offset_of!(FIO_rust_freeCResourcesState, free_dict) == size_of::<usize>());
|
||||
assert!(
|
||||
offset_of!(FIO_rust_freeCResourcesState, free_write_pool) == 2 * size_of::<usize>()
|
||||
);
|
||||
assert!(
|
||||
offset_of!(FIO_rust_freeCResourcesState, free_read_pool) == 3 * size_of::<usize>()
|
||||
);
|
||||
assert!(offset_of!(FIO_rust_freeCResourcesState, free_cctx) == 4 * size_of::<usize>());
|
||||
assert!(size_of::<FIO_rust_freeCResourcesState>() == 5 * size_of::<usize>());
|
||||
};
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn FIO_rust_freeCResources(
|
||||
state: *const FIO_rust_freeCResourcesState,
|
||||
) {
|
||||
if state.is_null() {
|
||||
return;
|
||||
}
|
||||
let state = unsafe { &*state };
|
||||
unsafe {
|
||||
(state.free_dict)(state.callback_context);
|
||||
(state.free_write_pool)(state.callback_context);
|
||||
(state.free_read_pool)(state.callback_context);
|
||||
(state.free_cctx)(state.callback_context);
|
||||
}
|
||||
}
|
||||
|
||||
pub type FIO_listMultipleFilesIsStdinFn = unsafe extern "C" fn(*mut c_void, *const c_char) -> c_int;
|
||||
pub type FIO_listMultipleFilesDisplayFn = unsafe extern "C" fn(*mut c_void);
|
||||
pub type FIO_listMultipleFilesListFileFn = unsafe extern "C" fn(
|
||||
@@ -1869,6 +1913,50 @@ mod tests {
|
||||
use std::fs;
|
||||
use std::mem::{align_of, offset_of, size_of};
|
||||
|
||||
#[derive(Default)]
|
||||
struct FreeCResourcesTestState {
|
||||
events: Vec<&'static str>,
|
||||
}
|
||||
|
||||
unsafe extern "C" fn free_c_resources_dict_test(context: *mut c_void) {
|
||||
let state = unsafe { &mut *context.cast::<FreeCResourcesTestState>() };
|
||||
state.events.push("dict");
|
||||
}
|
||||
|
||||
unsafe extern "C" fn free_c_resources_write_pool_test(context: *mut c_void) {
|
||||
let state = unsafe { &mut *context.cast::<FreeCResourcesTestState>() };
|
||||
state.events.push("write");
|
||||
}
|
||||
|
||||
unsafe extern "C" fn free_c_resources_read_pool_test(context: *mut c_void) {
|
||||
let state = unsafe { &mut *context.cast::<FreeCResourcesTestState>() };
|
||||
state.events.push("read");
|
||||
}
|
||||
|
||||
unsafe extern "C" fn free_c_resources_cctx_test(context: *mut c_void) {
|
||||
let state = unsafe { &mut *context.cast::<FreeCResourcesTestState>() };
|
||||
state.events.push("cctx");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn free_c_resources_preserves_teardown_order_and_null_policy() {
|
||||
let mut context = FreeCResourcesTestState::default();
|
||||
let state = FIO_rust_freeCResourcesState {
|
||||
callback_context: &mut context as *mut FreeCResourcesTestState as *mut c_void,
|
||||
free_dict: free_c_resources_dict_test,
|
||||
free_write_pool: free_c_resources_write_pool_test,
|
||||
free_read_pool: free_c_resources_read_pool_test,
|
||||
free_cctx: free_c_resources_cctx_test,
|
||||
};
|
||||
|
||||
unsafe {
|
||||
FIO_rust_freeCResources(ptr::null());
|
||||
FIO_rust_freeCResources(&state);
|
||||
}
|
||||
|
||||
assert_eq!(context.events, ["dict", "write", "read", "cctx"]);
|
||||
}
|
||||
|
||||
fn temporary_file_path(name: &str) -> std::path::PathBuf {
|
||||
std::env::temp_dir().join(format!("zstd-fileio-prefs-{}-{name}", std::process::id()))
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user