feat(compress): move thread-pool attachment policy into Rust

Move ZSTD_CCtx_refThreadPool init-stage validation and pool-slot assignment
behind a Rust-owned ABI bridge while keeping the opaque pool and CCtx storage
in C. Preserve the stage error and avoid mutating the pool on rejection.

Test Plan:
- cargo test --manifest-path rust/Cargo.toml --lib
- cargo clippy --manifest-path rust/Cargo.toml --all-targets -- -D warnings
- make -B -C programs -j1 zstd
- make -C tests -j1 test-zstream ZSTREAM_TESTTIME=-T1s
- focused ref_thread_pool unit tests
This commit is contained in:
2026-07-19 13:55:21 +02:00
parent 245cb36f91
commit 55f28cdd71
2 changed files with 99 additions and 4 deletions
+80
View File
@@ -1012,6 +1012,43 @@ pub unsafe extern "C" fn ZSTD_rust_setParameter(
result
}
/// Explicit projection for `ZSTD_CCtx_refThreadPool`.
///
/// Rust owns the init-stage check and assignment policy while the pool slot
/// itself remains storage owned by the C context. The pool value is opaque to
/// Rust because its private `POOL_ctx_s` layout is not part of this bridge.
#[repr(C)]
pub struct ZSTD_rust_refThreadPoolState {
pool: *mut *mut c_void,
requested_pool: *mut c_void,
stream_stage: c_int,
}
const _: () = {
assert!(offset_of!(ZSTD_rust_refThreadPoolState, pool) == 0);
assert!(offset_of!(ZSTD_rust_refThreadPoolState, requested_pool) == size_of::<usize>());
assert!(offset_of!(ZSTD_rust_refThreadPoolState, stream_stage) == 2 * size_of::<usize>());
assert!(size_of::<ZSTD_rust_refThreadPoolState>() == 3 * size_of::<usize>());
};
/// Attach an opaque thread pool through the C-owned context slot.
#[no_mangle]
pub unsafe extern "C" fn ZSTD_rust_refThreadPool(
state: *const ZSTD_rust_refThreadPoolState,
) -> usize {
if state.is_null() {
return ERROR(ZstdErrorCode::Generic);
}
let state = unsafe { &*state };
if state.stream_stage != ZSTD_CSTREAM_STAGE_INIT {
return ERROR(ZstdErrorCode::StageWrong);
}
unsafe {
*state.pool = state.requested_pool;
}
0
}
type ResetCCtxClearAllDictsFn = unsafe extern "C" fn(*mut c_void);
type ResetCCtxResetParamsFn = unsafe extern "C" fn(*mut c_void) -> usize;
@@ -10478,6 +10515,49 @@ mod tests {
assert_eq!(max_block_size_set, 0);
}
fn ref_thread_pool_test_state(
pool: &mut *mut c_void,
requested_pool: *mut c_void,
stream_stage: c_int,
) -> ZSTD_rust_refThreadPoolState {
ZSTD_rust_refThreadPoolState {
pool,
requested_pool,
stream_stage,
}
}
#[test]
fn ref_thread_pool_assigns_the_requested_pool_at_init_stage() {
let mut pool = ptr::null_mut::<c_void>();
let requested_pool = ptr::dangling_mut::<c_void>();
let state = ref_thread_pool_test_state(&mut pool, requested_pool, ZSTD_CSTREAM_STAGE_INIT);
let result = unsafe { ZSTD_rust_refThreadPool(&state) };
assert_eq!(result, 0);
assert_eq!(pool, requested_pool);
}
#[test]
fn ref_thread_pool_rejects_non_init_stage_without_mutating_the_pool() {
let original_pool = ptr::dangling_mut::<c_void>();
let mut pool = original_pool;
let state = ref_thread_pool_test_state(&mut pool, ptr::null_mut(), ZSTD_CSTREAM_STAGE_LOAD);
let result = unsafe { ZSTD_rust_refThreadPool(&state) };
assert_eq!(result, ERROR(ZstdErrorCode::StageWrong));
assert_eq!(pool, original_pool);
}
#[test]
fn ref_thread_pool_rejects_null_state() {
let result = unsafe { ZSTD_rust_refThreadPool(ptr::null()) };
assert_eq!(result, ERROR(ZstdErrorCode::Generic));
}
#[derive(Default)]
struct SetCParamsTestContext {
events: Vec<&'static str>,