rizin/librz/util/thread_pool.c
2026-01-09 21:26:26 +08:00

197 lines
No EOL
5.2 KiB
C

// SPDX-FileCopyrightText: 2022 deroad <wargio@libero.it>
// SPDX-License-Identifier: LGPL-3.0-only
#include <rz_th.h>
#include "thread.h"
#include <rz_userconf.h>
/**
* \brief RzThreadPool is a structure which handles n-threads threads
*
* This structure provides methods to handle multiple threads, like they were one.
*/
struct rz_th_pool_t {
size_t size;
RzThread **threads;
};
/**
* \brief Returns the number of available physical cores of the host machine
*
* \return The number of available physical cores (always >= 1)
*/
RZ_API RzThreadNCores rz_th_physical_core_number() {
#ifdef __WINDOWS__
SYSTEM_INFO sysinfo;
GetSystemInfo(&sysinfo);
return (RzThreadNCores)sysinfo.dwNumberOfProcessors;
#elif __APPLE__ || __FreeBSD__ || __OpenBSD__ || __DragonFly__ || __NetBSD__
int os_status = 0;
int mib[4];
unsigned long n_cpus = 1;
size_t n_cpus_length = sizeof(n_cpus);
/* set the mib for hw.ncpu */
mib[0] = CTL_HW;
#if __NetBSD__
mib[1] = HW_NCPUONLINE;
#elif __OpenBSD__ || __FreeBSD__ || __DragonFly__
mib[1] = HW_NCPU;
#else
mib[1] = HW_AVAILCPU;
#endif
os_status = sysctl(mib, 2, &n_cpus, &n_cpus_length, NULL, 0);
if (os_status != 0) {
#if __OpenBSD__ || __FreeBSD__
n_cpus = 1;
#else
// HW_AVAILCPU does not exist.
mib[1] = HW_NCPU;
os_status = sysctl(mib, 2, &n_cpus, &n_cpus_length, NULL, 0);
if (os_status != 0) {
n_cpus = 1;
}
#endif
}
// this is needed because the upper bits are set on bsd platforms
n_cpus &= UT32_MAX;
return (RzThreadNCores)n_cpus;
#elif __HAIKU__
system_info info;
get_system_info(&info);
return (RzThreadNCores)info.cpu_count;
#else
return (RzThreadNCores)sysconf(_SC_NPROCESSORS_ONLN);
#endif
}
/**
* \brief Returns the maximum number of threads available unless it exceeds N_THREAD_LIMIT.
* When set to 0, it will be the max number of cores.
*
* \param[in] max_threads The maximum number of threads to request
*
* \return The max number of threads requested
*/
RZ_API RzThreadNCores rz_th_max_threads(RzThreadNCores requested) {
const size_t n_thread_limit = N_THREAD_LIMIT;
RzThreadNCores n_cores = rz_th_physical_core_number();
if (requested <= RZ_THREAD_N_CORES_ALL_AVAILABLE) {
return n_cores;
} else if (n_thread_limit < (size_t)requested) {
RZ_LOG_WARN("The number of requested threads is higher than the thread limit (%" PFMTSZu ").\n", n_thread_limit);
return n_thread_limit;
}
return requested;
}
/**
* \brief returns a new RzThreadPool structure with a pool of thread
*
* Returns a new RzThreadPool structure with a pool of thread limited
* by either the physical core number count or by the value specified
* by the user (if set to 0, it will be the max physical cores number)
*
* \param max_threads The maximum number of threads needed in the pool
* \return RzThreadPool The RzThreadPool structure
*/
RZ_API RZ_OWN RzThreadPool *rz_th_pool_new(RzThreadNCores max_threads) {
RzThreadPool *pool = RZ_NEW0(RzThreadPool);
if (!pool) {
return NULL;
}
pool->size = (size_t)rz_th_max_threads(max_threads);
pool->threads = RZ_NEWS0(RzThread *, pool->size);
if (!pool->threads) {
free(pool);
return NULL;
}
return pool;
}
/**
* \brief Kills (and frees) the threads and frees the RzThreadPool struct
*
* \param RzThreadPool *The thread pool to free
*/
RZ_API void rz_th_pool_free(RZ_NULLABLE RzThreadPool *pool) {
if (!pool) {
return;
}
for (ut32 i = 0; i < pool->size; ++i) {
if (pool->threads[i]) {
rz_th_free(pool->threads[i]);
pool->threads[i] = NULL;
}
}
free(pool->threads);
free(pool);
}
/**
* \brief Adds a thread to the thread pool
*
* \param RzThreadPool The thread pool where to add the thread
* \param RzThread The thread to add to the pool
* \return true if a slot is found, false otherwise
*/
RZ_API bool rz_th_pool_add_thread(RZ_NONNULL RzThreadPool *pool, RZ_NONNULL RzThread *thread) {
rz_return_val_if_fail(pool && thread, false);
for (ut32 i = 0; i < pool->size; ++i) {
if (!pool->threads[i]) {
RZ_LOG_DEBUG("thread: thread %u added\n", i);
pool->threads[i] = thread;
return true;
}
}
return false;
}
/**
* \brief Returns the n-th thread in the thread pool.
*
* \param pool The thread pool to use
* \param index The index of the thread to get
*
* \return Returns the pointer of the n-th thread in the thread pool.
*/
RZ_API RZ_BORROW RzThread *rz_th_pool_get_thread(RZ_NONNULL RzThreadPool *pool, size_t index) {
rz_return_val_if_fail(pool && index < pool->size, NULL);
return pool->threads[index];
}
/**
* \brief Waits the end of all the threads in the thread pool
*
* \param RzThreadPool The thread pool to wait for
*
* \return true if managed to wait all threads, otherwise false
*/
RZ_API bool rz_th_pool_wait(RZ_NONNULL RzThreadPool *pool) {
rz_return_val_if_fail(pool, false);
bool has_exited = true;
for (ut32 i = 0; i < pool->size; ++i) {
if (pool->threads[i]) {
RZ_LOG_DEBUG("thread: waiting for thread %u\n", i);
has_exited = has_exited && rz_th_wait(pool->threads[i]);
}
}
return has_exited;
}
/**
* \brief Returns the thread pool size
*
* \param pool The RzThreadPool to use
*
* \return The size of the thread pool (always >= 1).
*/
RZ_API size_t rz_th_pool_size(RZ_NONNULL RzThreadPool *pool) {
rz_return_val_if_fail(pool, 1);
return pool->size;
}