refactor(mt): move context construction policy to Rust
Move MT context validation, worker clamping, resource-construction order, and partial-failure cleanup policy into Rust. Keep the private context layout, allocator-owned resources, pools, jobs table, and synchronization primitives in C behind callbacks. 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 (782 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:
+155
-39
@@ -642,6 +642,46 @@ typedef char ZSTDMT_rust_free_cctx_state_layout[
|
||||
&& offsetof(ZSTDMT_RustFreeCCtxState, freeMTctx) == 12 * sizeof(void*)
|
||||
&& sizeof(ZSTDMT_RustFreeCCtxState) == 13 * sizeof(void*)) ? 1 : -1];
|
||||
size_t ZSTDMT_rust_freeCCtx(const ZSTDMT_RustFreeCCtxState* state);
|
||||
|
||||
typedef struct {
|
||||
void* callbackContext;
|
||||
unsigned requestedNbWorkers;
|
||||
unsigned maxNbWorkers;
|
||||
size_t contextSize;
|
||||
ZSTD_customMem customMem;
|
||||
} ZSTDMT_RustCreateCCtxProjection;
|
||||
typedef char ZSTDMT_rust_create_cctx_projection_layout[
|
||||
(offsetof(ZSTDMT_RustCreateCCtxProjection, callbackContext) == 0
|
||||
&& offsetof(ZSTDMT_RustCreateCCtxProjection, requestedNbWorkers)
|
||||
== sizeof(void*)
|
||||
&& offsetof(ZSTDMT_RustCreateCCtxProjection, maxNbWorkers)
|
||||
== sizeof(void*) + sizeof(unsigned)
|
||||
&& offsetof(ZSTDMT_RustCreateCCtxProjection, contextSize)
|
||||
== (sizeof(void*) == 8 ? 16 : 12)
|
||||
&& offsetof(ZSTDMT_RustCreateCCtxProjection, customMem)
|
||||
== (sizeof(void*) == 8 ? 24 : 16)
|
||||
&& sizeof(ZSTDMT_RustCreateCCtxProjection)
|
||||
== (sizeof(void*) == 8 ? 48 : 28)) ? 1 : -1];
|
||||
typedef void* (*ZSTDMT_createCCtxAllocateFn)(void* opaque, size_t size);
|
||||
typedef size_t (*ZSTDMT_createCCtxSetWorkersFn)(void* opaque, unsigned nbWorkers);
|
||||
typedef void (*ZSTDMT_createCCtxSetInitialStateFn)(void* opaque);
|
||||
typedef void* (*ZSTDMT_createCCtxFactoryFn)(void* opaque, unsigned nbWorkers);
|
||||
typedef void* (*ZSTDMT_createCCtxJobsFn)(void* opaque, unsigned* nbJobs);
|
||||
typedef void* (*ZSTDMT_createCCtxResourceFn)(void* opaque, unsigned nbWorkers);
|
||||
typedef int (*ZSTDMT_createCCtxSerialInitFn)(void* opaque);
|
||||
typedef void (*ZSTDMT_createCCtxFreeFn)(void* opaque);
|
||||
void* ZSTDMT_rust_createCCtx(
|
||||
const ZSTDMT_RustCreateCCtxProjection* projection,
|
||||
ZSTDMT_createCCtxAllocateFn allocate,
|
||||
ZSTDMT_createCCtxSetWorkersFn setWorkers,
|
||||
ZSTDMT_createCCtxSetInitialStateFn setInitialState,
|
||||
ZSTDMT_createCCtxFactoryFn createFactory,
|
||||
ZSTDMT_createCCtxJobsFn createJobs,
|
||||
ZSTDMT_createCCtxResourceFn createBufferPool,
|
||||
ZSTDMT_createCCtxResourceFn createCCtxPool,
|
||||
ZSTDMT_createCCtxResourceFn createSeqPool,
|
||||
ZSTDMT_createCCtxSerialInitFn initSerial,
|
||||
ZSTDMT_createCCtxFreeFn freeContext);
|
||||
size_t ZSTDMT_rust_initCStream(
|
||||
const ZSTDMT_RustInitCStreamProjection* projection, void* opaque,
|
||||
ZSTDMT_initResizeFn resize, ZSTDMT_initDrainFn drain,
|
||||
@@ -1700,8 +1740,6 @@ typedef struct {
|
||||
*/
|
||||
} RoundBuff_t;
|
||||
|
||||
static const RoundBuff_t kNullRoundBuff = {NULL, 0, 0};
|
||||
|
||||
#define RSYNC_LENGTH 32
|
||||
/* Don't create chunks smaller than the zstd block size.
|
||||
* This stops us from regressing compression ratio too much,
|
||||
@@ -1831,46 +1869,124 @@ static size_t ZSTDMT_CCtxParam_setNbWorkers(ZSTD_CCtx_params* params, unsigned n
|
||||
return ZSTD_CCtxParams_setParameter(params, ZSTD_c_nbWorkers, (int)nbWorkers);
|
||||
}
|
||||
|
||||
typedef struct {
|
||||
ZSTDMT_CCtx* mtctx;
|
||||
ZSTD_customMem cMem;
|
||||
ZSTD_threadPool* pool;
|
||||
} ZSTDMT_rust_createCCtx_context;
|
||||
|
||||
static void* ZSTDMT_rust_createCCtx_allocate(void* opaque, size_t size)
|
||||
{
|
||||
ZSTDMT_rust_createCCtx_context* const context =
|
||||
(ZSTDMT_rust_createCCtx_context*)opaque;
|
||||
context->mtctx = (ZSTDMT_CCtx*)ZSTD_customCalloc(size, context->cMem);
|
||||
return context->mtctx;
|
||||
}
|
||||
|
||||
static size_t ZSTDMT_rust_createCCtx_setWorkers(void* opaque, unsigned nbWorkers)
|
||||
{
|
||||
ZSTDMT_rust_createCCtx_context* const context =
|
||||
(ZSTDMT_rust_createCCtx_context*)opaque;
|
||||
return ZSTDMT_CCtxParam_setNbWorkers(&context->mtctx->params, nbWorkers);
|
||||
}
|
||||
|
||||
static void ZSTDMT_rust_createCCtx_setInitialState(void* opaque)
|
||||
{
|
||||
ZSTDMT_rust_createCCtx_context* const context =
|
||||
(ZSTDMT_rust_createCCtx_context*)opaque;
|
||||
context->mtctx->cMem = context->cMem;
|
||||
context->mtctx->allJobsCompleted = 1;
|
||||
}
|
||||
|
||||
static void* ZSTDMT_rust_createCCtx_factory(void* opaque, unsigned nbWorkers)
|
||||
{
|
||||
ZSTDMT_rust_createCCtx_context* const context =
|
||||
(ZSTDMT_rust_createCCtx_context*)opaque;
|
||||
if (context->pool != NULL) {
|
||||
context->mtctx->factory = context->pool;
|
||||
context->mtctx->providedFactory = 1;
|
||||
} else {
|
||||
context->mtctx->factory = POOL_create_advanced(nbWorkers, 0, context->cMem);
|
||||
context->mtctx->providedFactory = 0;
|
||||
}
|
||||
return context->mtctx->factory;
|
||||
}
|
||||
|
||||
static void* ZSTDMT_rust_createCCtx_jobs(void* opaque, unsigned* nbJobs)
|
||||
{
|
||||
ZSTDMT_rust_createCCtx_context* const context =
|
||||
(ZSTDMT_rust_createCCtx_context*)opaque;
|
||||
context->mtctx->jobs = ZSTDMT_createJobsTable((U32*)nbJobs, context->cMem);
|
||||
if (context->mtctx->jobs != NULL)
|
||||
context->mtctx->jobIDMask = *nbJobs - 1;
|
||||
return context->mtctx->jobs;
|
||||
}
|
||||
|
||||
static void* ZSTDMT_rust_createCCtx_bufferPool(void* opaque, unsigned nbWorkers)
|
||||
{
|
||||
ZSTDMT_rust_createCCtx_context* const context =
|
||||
(ZSTDMT_rust_createCCtx_context*)opaque;
|
||||
context->mtctx->bufPool = ZSTDMT_createBufferPool(
|
||||
BUF_POOL_MAX_NB_BUFFERS(nbWorkers), context->cMem);
|
||||
return context->mtctx->bufPool;
|
||||
}
|
||||
|
||||
static void* ZSTDMT_rust_createCCtx_cctxPool(void* opaque, unsigned nbWorkers)
|
||||
{
|
||||
ZSTDMT_rust_createCCtx_context* const context =
|
||||
(ZSTDMT_rust_createCCtx_context*)opaque;
|
||||
context->mtctx->cctxPool = ZSTDMT_createCCtxPool(nbWorkers, context->cMem);
|
||||
return context->mtctx->cctxPool;
|
||||
}
|
||||
|
||||
static void* ZSTDMT_rust_createCCtx_seqPool(void* opaque, unsigned nbWorkers)
|
||||
{
|
||||
ZSTDMT_rust_createCCtx_context* const context =
|
||||
(ZSTDMT_rust_createCCtx_context*)opaque;
|
||||
context->mtctx->seqPool = ZSTDMT_createSeqPool(nbWorkers, context->cMem);
|
||||
return context->mtctx->seqPool;
|
||||
}
|
||||
|
||||
static int ZSTDMT_rust_createCCtx_initSerial(void* opaque)
|
||||
{
|
||||
ZSTDMT_rust_createCCtx_context* const context =
|
||||
(ZSTDMT_rust_createCCtx_context*)opaque;
|
||||
return ZSTDMT_serialState_init(&context->mtctx->serial);
|
||||
}
|
||||
|
||||
static void ZSTDMT_rust_createCCtx_free(void* opaque)
|
||||
{
|
||||
ZSTDMT_rust_createCCtx_context* const context =
|
||||
(ZSTDMT_rust_createCCtx_context*)opaque;
|
||||
if (context->mtctx != NULL) {
|
||||
ZSTDMT_freeCCtx(context->mtctx);
|
||||
context->mtctx = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
MEM_STATIC ZSTDMT_CCtx* ZSTDMT_createCCtx_advanced_internal(unsigned nbWorkers, ZSTD_customMem cMem, ZSTD_threadPool* pool)
|
||||
{
|
||||
ZSTDMT_CCtx* mtctx;
|
||||
U32 nbJobs = nbWorkers + 2;
|
||||
int initError;
|
||||
ZSTDMT_rust_createCCtx_context context = { NULL, cMem, pool };
|
||||
ZSTDMT_RustCreateCCtxProjection const projection = {
|
||||
&context,
|
||||
nbWorkers,
|
||||
ZSTDMT_NBWORKERS_MAX,
|
||||
sizeof(ZSTDMT_CCtx),
|
||||
cMem
|
||||
};
|
||||
DEBUGLOG(3, "ZSTDMT_createCCtx_advanced (nbWorkers = %u)", nbWorkers);
|
||||
|
||||
if (nbWorkers < 1) return NULL;
|
||||
nbWorkers = MIN(nbWorkers , ZSTDMT_NBWORKERS_MAX);
|
||||
if ((cMem.customAlloc!=NULL) ^ (cMem.customFree!=NULL))
|
||||
/* invalid custom allocator */
|
||||
return NULL;
|
||||
|
||||
mtctx = (ZSTDMT_CCtx*) ZSTD_customCalloc(sizeof(ZSTDMT_CCtx), cMem);
|
||||
if (!mtctx) return NULL;
|
||||
ZSTDMT_CCtxParam_setNbWorkers(&mtctx->params, nbWorkers);
|
||||
mtctx->cMem = cMem;
|
||||
mtctx->allJobsCompleted = 1;
|
||||
if (pool != NULL) {
|
||||
mtctx->factory = pool;
|
||||
mtctx->providedFactory = 1;
|
||||
}
|
||||
else {
|
||||
mtctx->factory = POOL_create_advanced(nbWorkers, 0, cMem);
|
||||
mtctx->providedFactory = 0;
|
||||
}
|
||||
mtctx->jobs = ZSTDMT_createJobsTable(&nbJobs, cMem);
|
||||
assert(nbJobs > 0); assert((nbJobs & (nbJobs - 1)) == 0); /* ensure nbJobs is a power of 2 */
|
||||
mtctx->jobIDMask = nbJobs - 1;
|
||||
mtctx->bufPool = ZSTDMT_createBufferPool(BUF_POOL_MAX_NB_BUFFERS(nbWorkers), cMem);
|
||||
mtctx->cctxPool = ZSTDMT_createCCtxPool(nbWorkers, cMem);
|
||||
mtctx->seqPool = ZSTDMT_createSeqPool(nbWorkers, cMem);
|
||||
initError = ZSTDMT_serialState_init(&mtctx->serial);
|
||||
mtctx->roundBuff = kNullRoundBuff;
|
||||
if (!mtctx->factory | !mtctx->jobs | !mtctx->bufPool | !mtctx->cctxPool | !mtctx->seqPool | initError) {
|
||||
ZSTDMT_freeCCtx(mtctx);
|
||||
return NULL;
|
||||
}
|
||||
DEBUGLOG(3, "mt_cctx created, for %u threads", nbWorkers);
|
||||
return mtctx;
|
||||
return (ZSTDMT_CCtx*)ZSTDMT_rust_createCCtx(
|
||||
&projection,
|
||||
ZSTDMT_rust_createCCtx_allocate,
|
||||
ZSTDMT_rust_createCCtx_setWorkers,
|
||||
ZSTDMT_rust_createCCtx_setInitialState,
|
||||
ZSTDMT_rust_createCCtx_factory,
|
||||
ZSTDMT_rust_createCCtx_jobs,
|
||||
ZSTDMT_rust_createCCtx_bufferPool,
|
||||
ZSTDMT_rust_createCCtx_cctxPool,
|
||||
ZSTDMT_rust_createCCtx_seqPool,
|
||||
ZSTDMT_rust_createCCtx_initSerial,
|
||||
ZSTDMT_rust_createCCtx_free);
|
||||
}
|
||||
|
||||
ZSTDMT_CCtx* ZSTDMT_createCCtx_advanced(unsigned nbWorkers, ZSTD_customMem cMem, ZSTD_threadPool* pool)
|
||||
|
||||
@@ -468,6 +468,149 @@ const _: () = {
|
||||
assert!(size_of::<ZSTDMT_RustFreeCCtxState>() == size_of::<[usize; 13]>());
|
||||
};
|
||||
|
||||
pub type ZSTDMT_createCCtxAllocateFn = unsafe extern "C" fn(*mut c_void, usize) -> *mut c_void;
|
||||
pub type ZSTDMT_createCCtxSetWorkersFn = unsafe extern "C" fn(*mut c_void, c_uint) -> usize;
|
||||
pub type ZSTDMT_createCCtxSetInitialStateFn = unsafe extern "C" fn(*mut c_void);
|
||||
pub type ZSTDMT_createCCtxFactoryFn = unsafe extern "C" fn(*mut c_void, c_uint) -> *mut c_void;
|
||||
pub type ZSTDMT_createCCtxJobsFn = unsafe extern "C" fn(*mut c_void, *mut c_uint) -> *mut c_void;
|
||||
pub type ZSTDMT_createCCtxResourceFn = unsafe extern "C" fn(*mut c_void, c_uint) -> *mut c_void;
|
||||
pub type ZSTDMT_createCCtxSerialInitFn = unsafe extern "C" fn(*mut c_void) -> c_int;
|
||||
pub type ZSTDMT_createCCtxFreeFn = unsafe extern "C" fn(*mut c_void);
|
||||
|
||||
/// Scalar inputs and callbacks for MT context construction. Rust owns the
|
||||
/// validation, worker clamp, and resource-construction order; C retains the
|
||||
/// private context layout, allocator calls, pools, and synchronization.
|
||||
#[repr(C)]
|
||||
#[derive(Clone, Copy)]
|
||||
pub struct ZSTDMT_RustCreateCCtxProjection {
|
||||
callback_context: *mut c_void,
|
||||
requested_nb_workers: c_uint,
|
||||
max_nb_workers: c_uint,
|
||||
context_size: usize,
|
||||
custom_mem: ZstdCustomMem,
|
||||
}
|
||||
|
||||
const _: () = {
|
||||
assert!(offset_of!(ZSTDMT_RustCreateCCtxProjection, callback_context) == 0);
|
||||
assert!(
|
||||
offset_of!(ZSTDMT_RustCreateCCtxProjection, requested_nb_workers) == size_of::<usize>()
|
||||
);
|
||||
assert!(
|
||||
offset_of!(ZSTDMT_RustCreateCCtxProjection, max_nb_workers)
|
||||
== size_of::<usize>() + size_of::<c_uint>()
|
||||
);
|
||||
assert!(
|
||||
offset_of!(ZSTDMT_RustCreateCCtxProjection, context_size)
|
||||
== if size_of::<usize>() == 8 { 16 } else { 12 }
|
||||
);
|
||||
assert!(
|
||||
offset_of!(ZSTDMT_RustCreateCCtxProjection, custom_mem)
|
||||
== if size_of::<usize>() == 8 { 24 } else { 16 }
|
||||
);
|
||||
assert!(
|
||||
size_of::<ZSTDMT_RustCreateCCtxProjection>()
|
||||
== if size_of::<usize>() == 8 { 48 } else { 28 }
|
||||
);
|
||||
};
|
||||
|
||||
fn custom_mem_is_valid(custom_mem: ZstdCustomMem) -> bool {
|
||||
custom_mem.customAlloc.is_some() == custom_mem.customFree.is_some()
|
||||
}
|
||||
|
||||
/// Construct an MT context through the original C resource callbacks.
|
||||
/// Callbacks are all invoked through the allocation and cleanup sequence even
|
||||
/// after an intermediate resource returns NULL, matching the C constructor's
|
||||
/// cleanup contract for partially initialized contexts.
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn ZSTDMT_rust_createCCtx(
|
||||
projection: *const ZSTDMT_RustCreateCCtxProjection,
|
||||
allocate: Option<ZSTDMT_createCCtxAllocateFn>,
|
||||
set_workers: Option<ZSTDMT_createCCtxSetWorkersFn>,
|
||||
set_initial_state: Option<ZSTDMT_createCCtxSetInitialStateFn>,
|
||||
create_factory: Option<ZSTDMT_createCCtxFactoryFn>,
|
||||
create_jobs: Option<ZSTDMT_createCCtxJobsFn>,
|
||||
create_buffer_pool: Option<ZSTDMT_createCCtxResourceFn>,
|
||||
create_cctx_pool: Option<ZSTDMT_createCCtxResourceFn>,
|
||||
create_seq_pool: Option<ZSTDMT_createCCtxResourceFn>,
|
||||
init_serial: Option<ZSTDMT_createCCtxSerialInitFn>,
|
||||
free_context: Option<ZSTDMT_createCCtxFreeFn>,
|
||||
) -> *mut c_void {
|
||||
let Some(projection) = (unsafe { projection.as_ref() }).copied() else {
|
||||
return ptr::null_mut();
|
||||
};
|
||||
let (
|
||||
Some(allocate),
|
||||
Some(set_workers),
|
||||
Some(set_initial_state),
|
||||
Some(create_factory),
|
||||
Some(create_jobs),
|
||||
Some(create_buffer_pool),
|
||||
Some(create_cctx_pool),
|
||||
Some(create_seq_pool),
|
||||
Some(init_serial),
|
||||
Some(free_context),
|
||||
) = (
|
||||
allocate,
|
||||
set_workers,
|
||||
set_initial_state,
|
||||
create_factory,
|
||||
create_jobs,
|
||||
create_buffer_pool,
|
||||
create_cctx_pool,
|
||||
create_seq_pool,
|
||||
init_serial,
|
||||
free_context,
|
||||
)
|
||||
else {
|
||||
return ptr::null_mut();
|
||||
};
|
||||
|
||||
if projection.callback_context.is_null()
|
||||
|| projection.requested_nb_workers == 0
|
||||
|| projection.max_nb_workers == 0
|
||||
|| projection.context_size == 0
|
||||
|| !custom_mem_is_valid(projection.custom_mem)
|
||||
{
|
||||
return ptr::null_mut();
|
||||
}
|
||||
|
||||
let nb_workers = projection
|
||||
.requested_nb_workers
|
||||
.min(projection.max_nb_workers);
|
||||
let mut nb_jobs = projection.requested_nb_workers.wrapping_add(2);
|
||||
let context = unsafe { allocate(projection.callback_context, projection.context_size) };
|
||||
if context.is_null() {
|
||||
return ptr::null_mut();
|
||||
}
|
||||
|
||||
// The C constructor ignores this setter's result for a valid worker
|
||||
// count; preserve that exact policy before publishing the custom memory.
|
||||
unsafe {
|
||||
let _ = set_workers(projection.callback_context, nb_workers);
|
||||
set_initial_state(projection.callback_context);
|
||||
}
|
||||
|
||||
let factory = unsafe { create_factory(projection.callback_context, nb_workers) };
|
||||
let jobs = unsafe { create_jobs(projection.callback_context, &mut nb_jobs) };
|
||||
let buffer_pool = unsafe { create_buffer_pool(projection.callback_context, nb_workers) };
|
||||
let cctx_pool = unsafe { create_cctx_pool(projection.callback_context, nb_workers) };
|
||||
let seq_pool = unsafe { create_seq_pool(projection.callback_context, nb_workers) };
|
||||
let serial_error = unsafe { init_serial(projection.callback_context) };
|
||||
|
||||
if factory.is_null()
|
||||
|| jobs.is_null()
|
||||
|| buffer_pool.is_null()
|
||||
|| cctx_pool.is_null()
|
||||
|| seq_pool.is_null()
|
||||
|| serial_error != 0
|
||||
{
|
||||
unsafe { free_context(projection.callback_context) };
|
||||
return ptr::null_mut();
|
||||
}
|
||||
|
||||
context
|
||||
}
|
||||
|
||||
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);
|
||||
@@ -3932,6 +4075,109 @@ mod tests {
|
||||
events: Vec<&'static str>,
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
struct CreateCCtxTestContext {
|
||||
events: Vec<&'static str>,
|
||||
workers: c_uint,
|
||||
jobs: c_uint,
|
||||
factory_ok: bool,
|
||||
resource_calls: usize,
|
||||
}
|
||||
|
||||
fn record_create_cctx_event(context: *mut c_void, event: &'static str) {
|
||||
unsafe {
|
||||
(*context.cast::<CreateCCtxTestContext>())
|
||||
.events
|
||||
.push(event);
|
||||
}
|
||||
}
|
||||
|
||||
unsafe extern "C" fn create_cctx_test_allocate(
|
||||
context: *mut c_void,
|
||||
_size: usize,
|
||||
) -> *mut c_void {
|
||||
record_create_cctx_event(context, "allocate");
|
||||
ptr::dangling_mut::<c_void>()
|
||||
}
|
||||
|
||||
unsafe extern "C" fn create_cctx_test_set_workers(
|
||||
context: *mut c_void,
|
||||
workers: c_uint,
|
||||
) -> usize {
|
||||
record_create_cctx_event(context, "workers");
|
||||
unsafe { (*context.cast::<CreateCCtxTestContext>()).workers = workers };
|
||||
0
|
||||
}
|
||||
|
||||
unsafe extern "C" fn create_cctx_test_initial_state(context: *mut c_void) {
|
||||
record_create_cctx_event(context, "initial-state");
|
||||
}
|
||||
|
||||
unsafe extern "C" fn create_cctx_test_factory(
|
||||
context: *mut c_void,
|
||||
_workers: c_uint,
|
||||
) -> *mut c_void {
|
||||
record_create_cctx_event(context, "factory");
|
||||
let context = unsafe { &*context.cast::<CreateCCtxTestContext>() };
|
||||
if context.factory_ok {
|
||||
ptr::dangling_mut::<c_void>()
|
||||
} else {
|
||||
ptr::null_mut()
|
||||
}
|
||||
}
|
||||
|
||||
unsafe extern "C" fn create_cctx_test_jobs(
|
||||
context: *mut c_void,
|
||||
jobs: *mut c_uint,
|
||||
) -> *mut c_void {
|
||||
record_create_cctx_event(context, "jobs");
|
||||
unsafe {
|
||||
(*context.cast::<CreateCCtxTestContext>()).jobs = 8;
|
||||
*jobs = 8;
|
||||
}
|
||||
ptr::dangling_mut::<c_void>()
|
||||
}
|
||||
|
||||
unsafe extern "C" fn create_cctx_test_resource(
|
||||
context: *mut c_void,
|
||||
_workers: c_uint,
|
||||
) -> *mut c_void {
|
||||
let event = unsafe {
|
||||
let state = &mut *context.cast::<CreateCCtxTestContext>();
|
||||
let event = match state.resource_calls {
|
||||
0 => "buffer-pool",
|
||||
1 => "cctx-pool",
|
||||
_ => "seq-pool",
|
||||
};
|
||||
state.resource_calls += 1;
|
||||
event
|
||||
};
|
||||
record_create_cctx_event(context, event);
|
||||
ptr::dangling_mut::<c_void>()
|
||||
}
|
||||
|
||||
unsafe extern "C" fn create_cctx_test_serial_init(context: *mut c_void) -> c_int {
|
||||
record_create_cctx_event(context, "serial");
|
||||
0
|
||||
}
|
||||
|
||||
unsafe extern "C" fn create_cctx_test_free(context: *mut c_void) {
|
||||
record_create_cctx_event(context, "free");
|
||||
}
|
||||
|
||||
fn create_cctx_test_projection(
|
||||
context: &mut CreateCCtxTestContext,
|
||||
custom_mem: ZstdCustomMem,
|
||||
) -> ZSTDMT_RustCreateCCtxProjection {
|
||||
ZSTDMT_RustCreateCCtxProjection {
|
||||
callback_context: context as *mut CreateCCtxTestContext as *mut c_void,
|
||||
requested_nb_workers: 99,
|
||||
max_nb_workers: 4,
|
||||
context_size: 128,
|
||||
custom_mem,
|
||||
}
|
||||
}
|
||||
|
||||
fn record_free_cctx_event(context: *mut c_void, event: &'static str) {
|
||||
unsafe {
|
||||
(*context.cast::<FreeCCtxTestContext>()).events.push(event);
|
||||
@@ -4005,6 +4251,125 @@ mod tests {
|
||||
assert_eq!(unsafe { ZSTDMT_rust_freeCCtx(ptr::null()) }, 0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn create_cctx_preserves_resource_order_and_clamps_workers() {
|
||||
let mut context = CreateCCtxTestContext {
|
||||
factory_ok: true,
|
||||
..CreateCCtxTestContext::default()
|
||||
};
|
||||
let projection = create_cctx_test_projection(&mut context, DEFAULT_MEM);
|
||||
|
||||
let result = unsafe {
|
||||
ZSTDMT_rust_createCCtx(
|
||||
&projection,
|
||||
Some(create_cctx_test_allocate),
|
||||
Some(create_cctx_test_set_workers),
|
||||
Some(create_cctx_test_initial_state),
|
||||
Some(create_cctx_test_factory),
|
||||
Some(create_cctx_test_jobs),
|
||||
Some(create_cctx_test_resource),
|
||||
Some(create_cctx_test_resource),
|
||||
Some(create_cctx_test_resource),
|
||||
Some(create_cctx_test_serial_init),
|
||||
Some(create_cctx_test_free),
|
||||
)
|
||||
};
|
||||
|
||||
assert_eq!(result, ptr::dangling_mut::<c_void>());
|
||||
assert_eq!(context.workers, 4);
|
||||
assert_eq!(context.jobs, 8);
|
||||
assert_eq!(
|
||||
context.events,
|
||||
vec![
|
||||
"allocate",
|
||||
"workers",
|
||||
"initial-state",
|
||||
"factory",
|
||||
"jobs",
|
||||
"buffer-pool",
|
||||
"cctx-pool",
|
||||
"seq-pool",
|
||||
"serial",
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn create_cctx_runs_remaining_callbacks_before_failure_cleanup() {
|
||||
let mut context = CreateCCtxTestContext::default();
|
||||
let projection = create_cctx_test_projection(&mut context, DEFAULT_MEM);
|
||||
|
||||
let result = unsafe {
|
||||
ZSTDMT_rust_createCCtx(
|
||||
&projection,
|
||||
Some(create_cctx_test_allocate),
|
||||
Some(create_cctx_test_set_workers),
|
||||
Some(create_cctx_test_initial_state),
|
||||
Some(create_cctx_test_factory),
|
||||
Some(create_cctx_test_jobs),
|
||||
Some(create_cctx_test_resource),
|
||||
Some(create_cctx_test_resource),
|
||||
Some(create_cctx_test_resource),
|
||||
Some(create_cctx_test_serial_init),
|
||||
Some(create_cctx_test_free),
|
||||
)
|
||||
};
|
||||
|
||||
assert!(result.is_null());
|
||||
assert_eq!(
|
||||
context.events,
|
||||
vec![
|
||||
"allocate",
|
||||
"workers",
|
||||
"initial-state",
|
||||
"factory",
|
||||
"jobs",
|
||||
"buffer-pool",
|
||||
"cctx-pool",
|
||||
"seq-pool",
|
||||
"serial",
|
||||
"free",
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
unsafe extern "C" fn create_cctx_test_alloc(_opaque: *mut c_void, _size: usize) -> *mut c_void {
|
||||
ptr::null_mut()
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn create_cctx_rejects_mismatched_custom_allocator() {
|
||||
let mut context = CreateCCtxTestContext {
|
||||
factory_ok: true,
|
||||
..CreateCCtxTestContext::default()
|
||||
};
|
||||
let invalid_mem = ZstdCustomMem {
|
||||
customAlloc: Some(create_cctx_test_alloc),
|
||||
customFree: None,
|
||||
opaque: ptr::null_mut(),
|
||||
};
|
||||
let projection = create_cctx_test_projection(&mut context, invalid_mem);
|
||||
|
||||
let result = unsafe {
|
||||
ZSTDMT_rust_createCCtx(
|
||||
&projection,
|
||||
Some(create_cctx_test_allocate),
|
||||
Some(create_cctx_test_set_workers),
|
||||
Some(create_cctx_test_initial_state),
|
||||
Some(create_cctx_test_factory),
|
||||
Some(create_cctx_test_jobs),
|
||||
Some(create_cctx_test_resource),
|
||||
Some(create_cctx_test_resource),
|
||||
Some(create_cctx_test_resource),
|
||||
Some(create_cctx_test_serial_init),
|
||||
Some(create_cctx_test_free),
|
||||
)
|
||||
};
|
||||
|
||||
assert!(result.is_null());
|
||||
assert!(context.events.is_empty());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn free_cctx_preserves_callback_order_and_ownership_conditions() {
|
||||
let mut context = FreeCCtxTestContext::default();
|
||||
|
||||
Reference in New Issue
Block a user