Skip to main content

libzstd_rs_sys/lib/common/
pool.rs

1use core::ptr;
2use std::sync::{Condvar, Mutex};
3use std::thread::JoinHandle;
4
5use libc::{calloc, free, size_t};
6
7use crate::lib::zstd::{ZSTD_customMem, ZSTD_defaultCMem};
8
9pub struct POOL_ctx {
10    customMem: ZSTD_customMem,
11    threads: *mut JoinHandle<()>,
12    threadCapacity: size_t,
13    threadLimit: size_t,
14    queue: *mut POOL_job,
15    queueHead: size_t,
16    queueTail: size_t,
17    queueSize: size_t,
18    numThreadsBusy: size_t,
19    queueEmpty: core::ffi::c_int,
20    queueMutex: Mutex<()>,
21    queuePushCond: Condvar,
22    queuePopCond: Condvar,
23    shutdown: core::ffi::c_int,
24}
25
26struct SendPoolCtxPtr(*mut POOL_ctx);
27unsafe impl Send for SendPoolCtxPtr {}
28
29#[derive(Copy, Clone)]
30#[repr(C)]
31pub(crate) struct POOL_job {
32    function: POOL_function,
33    opaque: *mut core::ffi::c_void,
34}
35pub type POOL_function = Option<unsafe extern "C" fn(*mut core::ffi::c_void) -> ()>;
36pub type ZSTD_threadPool = POOL_ctx;
37#[inline]
38unsafe extern "C" fn ZSTD_customCalloc(
39    size: size_t,
40    customMem: ZSTD_customMem,
41) -> *mut core::ffi::c_void {
42    if (customMem.customAlloc).is_some() {
43        let ptr = (customMem.customAlloc).unwrap_unchecked()(customMem.opaque, size);
44        ptr::write_bytes(ptr, 0, size as libc::size_t);
45        return ptr;
46    }
47    calloc(1, size)
48}
49#[inline]
50unsafe extern "C" fn ZSTD_customFree(ptr: *mut core::ffi::c_void, customMem: ZSTD_customMem) {
51    if !ptr.is_null() {
52        if (customMem.customFree).is_some() {
53            (customMem.customFree).unwrap_unchecked()(customMem.opaque, ptr);
54        } else {
55            free(ptr);
56        }
57    }
58}
59unsafe fn POOL_thread(ctx: *mut POOL_ctx) {
60    if ctx.is_null() {
61        return;
62    }
63    loop {
64        let mut guard = (*ctx).queueMutex.lock().unwrap();
65        while (*ctx).queueEmpty != 0 || (*ctx).numThreadsBusy >= (*ctx).threadLimit {
66            if (*ctx).shutdown != 0 {
67                return;
68            }
69            guard = (*ctx).queuePopCond.wait(guard).unwrap();
70        }
71        let job = *((*ctx).queue).add((*ctx).queueHead);
72        (*ctx).queueHead = ((*ctx).queueHead).wrapping_add(1) % (*ctx).queueSize;
73        (*ctx).numThreadsBusy = ((*ctx).numThreadsBusy).wrapping_add(1);
74        (*ctx).numThreadsBusy;
75        (*ctx).queueEmpty = ((*ctx).queueHead == (*ctx).queueTail) as core::ffi::c_int;
76        (*ctx).queuePushCond.notify_one();
77        drop(guard);
78        (job.function).unwrap_unchecked()(job.opaque);
79        guard = (*ctx).queueMutex.lock().unwrap();
80        (*ctx).numThreadsBusy = ((*ctx).numThreadsBusy).wrapping_sub(1);
81        (*ctx).numThreadsBusy;
82        (*ctx).queuePushCond.notify_one();
83    }
84}
85#[cfg_attr(feature = "export-symbols", export_name = crate::prefix!(ZSTD_createThreadPool))]
86pub unsafe extern "C" fn ZSTD_createThreadPool(numThreads: size_t) -> *mut ZSTD_threadPool {
87    POOL_create(numThreads, 0)
88}
89pub unsafe fn POOL_create(numThreads: size_t, queueSize: size_t) -> *mut POOL_ctx {
90    POOL_create_advanced(numThreads, queueSize, ZSTD_defaultCMem)
91}
92pub(crate) unsafe fn POOL_create_advanced(
93    numThreads: size_t,
94    queueSize: size_t,
95    customMem: ZSTD_customMem,
96) -> *mut POOL_ctx {
97    let mut ctx = core::ptr::null_mut::<POOL_ctx>();
98    if numThreads == 0 {
99        return core::ptr::null_mut();
100    }
101    ctx = ZSTD_customCalloc(::core::mem::size_of::<POOL_ctx>(), customMem) as *mut POOL_ctx;
102    if ctx.is_null() {
103        return core::ptr::null_mut();
104    }
105    (*ctx).queueSize = queueSize.wrapping_add(1);
106    (*ctx).queue = ZSTD_customCalloc(
107        ((*ctx).queueSize).wrapping_mul(::core::mem::size_of::<POOL_job>()),
108        customMem,
109    ) as *mut POOL_job;
110    (*ctx).queueHead = 0;
111    (*ctx).queueTail = 0;
112    (*ctx).numThreadsBusy = 0;
113    (*ctx).queueEmpty = 1;
114    ptr::write(ptr::addr_of_mut!((*ctx).queueMutex), Mutex::new(()));
115    ptr::write(ptr::addr_of_mut!((*ctx).queuePushCond), Condvar::new());
116    ptr::write(ptr::addr_of_mut!((*ctx).queuePopCond), Condvar::new());
117    (*ctx).shutdown = 0;
118    (*ctx).threads = ZSTD_customCalloc(
119        numThreads.wrapping_mul(::core::mem::size_of::<JoinHandle<()>>()),
120        customMem,
121    ) as *mut JoinHandle<()>;
122    (*ctx).threadCapacity = 0;
123    (*ctx).customMem = customMem;
124    if ((*ctx).threads).is_null() || ((*ctx).queue).is_null() {
125        POOL_free(ctx);
126        return core::ptr::null_mut();
127    }
128    for i in 0..numThreads {
129        let ctx = SendPoolCtxPtr(ctx);
130        core::ptr::write(
131            ((*ctx.0).threads).add(i),
132            std::thread::spawn(|| {
133                let ctx = ctx;
134                POOL_thread(ctx.0)
135            }),
136        );
137    }
138    (*ctx).threadCapacity = numThreads;
139    (*ctx).threadLimit = numThreads;
140    ctx
141}
142unsafe fn POOL_join(ctx: *mut POOL_ctx) {
143    let guard = (*ctx).queueMutex.lock().unwrap();
144    (*ctx).shutdown = 1;
145    drop(guard);
146    (*ctx).queuePushCond.notify_all();
147    (*ctx).queuePopCond.notify_all();
148    for i in 0..(*ctx).threadCapacity {
149        core::ptr::read(((*ctx).threads).add(i)).join().unwrap();
150    }
151}
152pub unsafe fn POOL_free(ctx: *mut POOL_ctx) {
153    if ctx.is_null() {
154        return;
155    }
156    POOL_join(ctx);
157    ptr::drop_in_place(ptr::addr_of_mut!((*ctx).queueMutex));
158    ptr::drop_in_place(ptr::addr_of_mut!((*ctx).queuePushCond));
159    ptr::drop_in_place(ptr::addr_of_mut!((*ctx).queuePopCond));
160    ZSTD_customFree((*ctx).queue as *mut core::ffi::c_void, (*ctx).customMem);
161    ZSTD_customFree((*ctx).threads as *mut core::ffi::c_void, (*ctx).customMem);
162    ZSTD_customFree(ctx as *mut core::ffi::c_void, (*ctx).customMem);
163}
164pub unsafe fn POOL_joinJobs(ctx: *mut POOL_ctx) {
165    let mut guard = (*ctx).queueMutex.lock().unwrap();
166    while (*ctx).queueEmpty == 0 || (*ctx).numThreadsBusy > 0 {
167        guard = (*ctx).queuePushCond.wait(guard).unwrap();
168    }
169}
170#[cfg_attr(feature = "export-symbols", export_name = crate::prefix!(ZSTD_freeThreadPool))]
171pub unsafe extern "C" fn ZSTD_freeThreadPool(pool: *mut ZSTD_threadPool) {
172    POOL_free(pool);
173}
174pub(crate) unsafe fn POOL_sizeof(ctx: *const POOL_ctx) -> size_t {
175    if ctx.is_null() {
176        return 0;
177    }
178    (::core::mem::size_of::<POOL_ctx>())
179        .wrapping_add(((*ctx).queueSize).wrapping_mul(::core::mem::size_of::<POOL_job>()))
180        .wrapping_add(
181            ((*ctx).threadCapacity).wrapping_mul(::core::mem::size_of::<JoinHandle<()>>()),
182        )
183}
184unsafe fn POOL_resize_internal(ctx: *mut POOL_ctx, numThreads: size_t) -> core::ffi::c_int {
185    if numThreads <= (*ctx).threadCapacity {
186        if numThreads == 0 {
187            return 1;
188        }
189        (*ctx).threadLimit = numThreads;
190        return 0;
191    }
192    let threadPool = ZSTD_customCalloc(
193        numThreads.wrapping_mul(::core::mem::size_of::<JoinHandle<()>>()),
194        (*ctx).customMem,
195    ) as *mut JoinHandle<()>;
196    if threadPool.is_null() {
197        return 1;
198    }
199    libc::memcpy(
200        threadPool as *mut core::ffi::c_void,
201        (*ctx).threads as *const core::ffi::c_void,
202        ((*ctx).threadCapacity).wrapping_mul(::core::mem::size_of::<JoinHandle<()>>()),
203    );
204    ZSTD_customFree((*ctx).threads as *mut core::ffi::c_void, (*ctx).customMem);
205    (*ctx).threads = threadPool;
206    for threadId in (*ctx).threadCapacity..numThreads {
207        let ctx = SendPoolCtxPtr(ctx);
208        core::ptr::write(
209            ((*ctx.0).threads).add(threadId),
210            std::thread::spawn(|| {
211                let ctx = ctx;
212                POOL_thread(ctx.0)
213            }),
214        );
215    }
216    (*ctx).threadCapacity = numThreads;
217    (*ctx).threadLimit = numThreads;
218    0
219}
220pub(crate) unsafe fn POOL_resize(ctx: *mut POOL_ctx, numThreads: size_t) -> core::ffi::c_int {
221    let mut result: core::ffi::c_int = 0;
222    if ctx.is_null() {
223        return 1;
224    }
225    let _guard = (*ctx).queueMutex.lock().unwrap();
226    result = POOL_resize_internal(ctx, numThreads);
227    (*ctx).queuePopCond.notify_all();
228    result
229}
230unsafe fn isQueueFull(ctx: *const POOL_ctx) -> core::ffi::c_int {
231    if (*ctx).queueSize > 1 {
232        ((*ctx).queueHead == ((*ctx).queueTail).wrapping_add(1) % (*ctx).queueSize)
233            as core::ffi::c_int
234    } else {
235        ((*ctx).numThreadsBusy == (*ctx).threadLimit || (*ctx).queueEmpty == 0) as core::ffi::c_int
236    }
237}
238unsafe fn POOL_add_internal(
239    ctx: *mut POOL_ctx,
240    function: POOL_function,
241    opaque: *mut core::ffi::c_void,
242) {
243    let mut job = POOL_job {
244        function: None,
245        opaque: core::ptr::null_mut::<core::ffi::c_void>(),
246    };
247    job.function = function;
248    job.opaque = opaque;
249    if (*ctx).shutdown != 0 {
250        return;
251    }
252    (*ctx).queueEmpty = 0;
253    *((*ctx).queue).add((*ctx).queueTail) = job;
254    (*ctx).queueTail = ((*ctx).queueTail).wrapping_add(1) % (*ctx).queueSize;
255    (*ctx).queuePopCond.notify_one();
256}
257pub unsafe fn POOL_add(
258    ctx: *mut POOL_ctx,
259    function: POOL_function,
260    opaque: *mut core::ffi::c_void,
261) {
262    let mut guard = (*ctx).queueMutex.lock().unwrap();
263    while isQueueFull(ctx) != 0 && (*ctx).shutdown == 0 {
264        guard = (*ctx).queuePushCond.wait(guard).unwrap();
265    }
266    POOL_add_internal(ctx, function, opaque);
267}
268pub(crate) unsafe fn POOL_tryAdd(
269    ctx: *mut POOL_ctx,
270    function: POOL_function,
271    opaque: *mut core::ffi::c_void,
272) -> core::ffi::c_int {
273    let _guard = (*ctx).queueMutex.lock().unwrap();
274    if isQueueFull(ctx) != 0 {
275        return 0;
276    }
277    POOL_add_internal(ctx, function, opaque);
278    1
279}