refactor(mt): move resize policy to Rust

Move MT resize callback ordering and short-circuit failure policy into Rust.
C retains the private factory, jobs table, buffer pool, CCtx pool, sequence pool,
and parameter storage behind narrow callbacks, including the original ignored
worker-parameter setter result.

Test Plan:
- ulimit -v 41943040; CARGO_BUILD_JOBS=1 cargo +nightly fmt --manifest-path rust/Cargo.toml -- --check
- ulimit -v 41943040; CARGO_BUILD_JOBS=1 cargo clippy --manifest-path rust/Cargo.toml --all-targets -- -D warnings
- ulimit -v 41943040; CARGO_BUILD_JOBS=1 cargo test --manifest-path rust/Cargo.toml --all-targets (784 passed)
- ulimit -v 41943040; CARGO_BUILD_JOBS=1 cargo clippy --manifest-path rust/cli/Cargo.toml --all-targets -- -D warnings
- ulimit -v 41943040; CARGO_BUILD_JOBS=1 cargo test --manifest-path rust/cli/Cargo.toml --all-targets (179 passed)
- ulimit -v 41943040; make -j1
- ulimit -v 41943040; make -j1 -C tests test (all tests completed successfully)
This commit is contained in:
2026-07-20 07:08:36 +02:00
parent 2b2f9df5de
commit dc3c9c6c11
2 changed files with 263 additions and 10 deletions
+195
View File
@@ -611,6 +611,86 @@ pub unsafe extern "C" fn ZSTDMT_rust_createCCtx(
context
}
pub type ZSTDMT_resizeStepFn = unsafe extern "C" fn(*mut c_void, c_uint) -> usize;
pub type ZSTDMT_resizeResourceFn = unsafe extern "C" fn(*mut c_void, c_uint) -> *mut c_void;
/// Scalar inputs for MT pool resizing. Rust owns the resize order and
/// short-circuit policy; C retains every private pool and context mutation
/// behind these callbacks.
#[repr(C)]
#[derive(Clone, Copy)]
pub struct ZSTDMT_RustResizeProjection {
callback_context: *mut c_void,
nb_workers: c_uint,
}
const _: () = {
assert!(offset_of!(ZSTDMT_RustResizeProjection, callback_context) == 0);
assert!(offset_of!(ZSTDMT_RustResizeProjection, nb_workers) == size_of::<usize>());
assert!(
size_of::<ZSTDMT_RustResizeProjection>() == if size_of::<usize>() == 8 { 16 } else { 8 }
);
};
/// Resize MT resources in the original order: worker factory, jobs table,
/// buffer pool, CCtx pool, sequence pool, then the parameter publication.
#[no_mangle]
pub unsafe extern "C" fn ZSTDMT_rust_resize(
projection: *const ZSTDMT_RustResizeProjection,
resize_factory: Option<ZSTDMT_resizeStepFn>,
expand_jobs: Option<ZSTDMT_resizeStepFn>,
expand_buffer_pool: Option<ZSTDMT_resizeResourceFn>,
expand_cctx_pool: Option<ZSTDMT_resizeResourceFn>,
expand_seq_pool: Option<ZSTDMT_resizeResourceFn>,
set_nb_workers: Option<ZSTDMT_resizeStepFn>,
) -> usize {
let Some(projection) = (unsafe { projection.as_ref() }).copied() else {
return ERROR(ZstdErrorCode::Generic);
};
let (
Some(resize_factory),
Some(expand_jobs),
Some(expand_buffer_pool),
Some(expand_cctx_pool),
Some(expand_seq_pool),
Some(set_nb_workers),
) = (
resize_factory,
expand_jobs,
expand_buffer_pool,
expand_cctx_pool,
expand_seq_pool,
set_nb_workers,
)
else {
return ERROR(ZstdErrorCode::Generic);
};
if projection.callback_context.is_null() {
return ERROR(ZstdErrorCode::Generic);
}
let context = projection.callback_context;
let workers = projection.nb_workers;
let error = unsafe { resize_factory(context, workers) };
if ERR_isError(error) {
return error;
}
let error = unsafe { expand_jobs(context, workers) };
if ERR_isError(error) {
return error;
}
if unsafe { expand_buffer_pool(context, workers) }.is_null() {
return ERROR(ZstdErrorCode::MemoryAllocation);
}
if unsafe { expand_cctx_pool(context, workers) }.is_null() {
return ERROR(ZstdErrorCode::MemoryAllocation);
}
if unsafe { expand_seq_pool(context, workers) }.is_null() {
return ERROR(ZstdErrorCode::MemoryAllocation);
}
unsafe { set_nb_workers(context, workers) }
}
type ZSTDMT_waitForLdmLockFn = unsafe extern "C" fn(*mut c_void);
type ZSTDMT_waitForLdmOverlapFn = unsafe extern "C" fn(*mut c_void, *mut c_void, usize) -> c_int;
type ZSTDMT_waitForLdmWaitFn = unsafe extern "C" fn(*mut c_void);
@@ -4178,6 +4258,121 @@ mod tests {
}
}
#[derive(Default)]
struct ResizeTestContext {
events: Vec<&'static str>,
resource_calls: usize,
fail: Option<&'static str>,
}
fn record_resize_event(context: *mut c_void, event: &'static str) -> bool {
let state = unsafe { &mut *context.cast::<ResizeTestContext>() };
state.events.push(event);
state.fail == Some(event)
}
unsafe extern "C" fn resize_test_step(context: *mut c_void, _workers: c_uint) -> usize {
let state = unsafe { &*context.cast::<ResizeTestContext>() };
let event = match state.events.len() {
0 => "factory",
_ => "jobs",
};
if record_resize_event(context, event) {
ERROR(ZstdErrorCode::MemoryAllocation)
} else {
0
}
}
unsafe extern "C" fn resize_test_resource(
context: *mut c_void,
_workers: c_uint,
) -> *mut c_void {
let state = unsafe { &mut *context.cast::<ResizeTestContext>() };
let event = match state.resource_calls {
0 => "buffer-pool",
1 => "cctx-pool",
_ => "seq-pool",
};
state.resource_calls += 1;
if record_resize_event(context, event) {
ptr::null_mut()
} else {
ptr::dangling_mut::<c_void>()
}
}
unsafe extern "C" fn resize_test_set_workers(context: *mut c_void, _workers: c_uint) -> usize {
assert!(!record_resize_event(context, "set-workers"));
0
}
fn resize_test_projection(
context: &mut ResizeTestContext,
workers: c_uint,
) -> ZSTDMT_RustResizeProjection {
ZSTDMT_RustResizeProjection {
callback_context: context as *mut ResizeTestContext as *mut c_void,
nb_workers: workers,
}
}
#[test]
fn resize_preserves_resource_order_and_publishes_workers_last() {
let mut context = ResizeTestContext::default();
let projection = resize_test_projection(&mut context, 6);
let result = unsafe {
ZSTDMT_rust_resize(
&projection,
Some(resize_test_step),
Some(resize_test_step),
Some(resize_test_resource),
Some(resize_test_resource),
Some(resize_test_resource),
Some(resize_test_set_workers),
)
};
assert_eq!(result, 0);
assert_eq!(
context.events,
[
"factory",
"jobs",
"buffer-pool",
"cctx-pool",
"seq-pool",
"set-workers",
]
);
}
#[test]
fn resize_stops_after_resource_failure_without_publishing_workers() {
let mut context = ResizeTestContext {
fail: Some("cctx-pool"),
..ResizeTestContext::default()
};
let projection = resize_test_projection(&mut context, 3);
let result = unsafe {
ZSTDMT_rust_resize(
&projection,
Some(resize_test_step),
Some(resize_test_step),
Some(resize_test_resource),
Some(resize_test_resource),
Some(resize_test_resource),
Some(resize_test_set_workers),
)
};
assert_eq!(result, ERROR(ZstdErrorCode::MemoryAllocation));
assert_eq!(
context.events,
["factory", "jobs", "buffer-pool", "cctx-pool"]
);
}
fn record_free_cctx_event(context: *mut c_void, event: &'static str) {
unsafe {
(*context.cast::<FreeCCtxTestContext>()).events.push(event);