一些基础组件

This commit is contained in:
2026-08-30 04:11:22 +08:00
parent 636657f454
commit 456543f6a4
7 changed files with 710 additions and 0 deletions
+325
View File
@@ -0,0 +1,325 @@
#include <c_LockQueue.h>
#if defined(PLATFORM_POSIX)
#include <sys/time.h>
#endif
/* ------------------------------------------------------------------------------------------------------------------ */
/* */
c_err_t c_LockQueue_Init(c_LockQueue_t* queue, c_size_t capacity, c_Allocator_t* allocator) {
if (!queue || capacity==0) return C_ERR_PARAM;
queue->allocator = (allocator != NULL) ? *allocator : c_DefaultAllocator;
queue->data = (void**)c_Allocator_Alloc(&queue->allocator, sizeof(void*) * capacity);
if (!queue->data) {
return C_ERR_NOMEM;
}
queue->capacity = capacity;
queue->write_idx = 0;
queue->read_idx = 0;
queue->size = 0;
queue->is_shutdown = C_FALSE;
if ((c_Mutex_Init(&queue->lock)!=C_ERR_OK) ||
(c_Cond_Init(&queue->not_full)!=C_ERR_OK) ||
(c_Cond_Init(&queue->not_empty)!=C_ERR_OK))
{
if (queue->data) {
c_Allocator_Free(&queue->allocator, queue->data);
queue->data = NULL;
}
}
return C_ERR_OK;
}
void c_LockQueue_Destroy(c_LockQueue_t* queue) {
if (!queue) return;
c_LockQueue_Shutdown(queue);
c_Mutex_Lock(&queue->lock);
c_Cond_Destroy(&queue->not_full);
c_Cond_Destroy(&queue->not_empty);
if (queue->data) {
c_Allocator_Free(&queue->allocator, queue->data);
queue->data = NULL;
}
c_Mutex_UnLock(&queue->lock);
// 销毁跨平台锁
c_Mutex_Destroy(&queue->lock);
}
void c_LockQueue_Shutdown(c_LockQueue_t* queue) {
if (!queue) return;
c_Mutex_Lock(&queue->lock);
queue->is_shutdown = C_TRUE;
// 广播唤醒所有正在阻塞的生产者和消费者,让他们通过 is_shutdown 状态感知并安全退出
c_Cond_Broadcast(&queue->not_full);
c_Cond_Broadcast(&queue->not_empty);
c_Mutex_UnLock(&queue->lock);
}
c_err_t c_LockQueue_Push(c_LockQueue_t* queue, void* data) {
if (!queue) return C_ERR_PARAM;
c_Mutex_Lock(&queue->lock);
// 1. 经典工业级设计:使用 while 循环检查条件,完美防御虚假唤醒
while (queue->size == queue->capacity && !queue->is_shutdown) {
c_Cond_Wait(&queue->not_full, &queue->lock);
}
// 2. 如果队列中途被关闭,直接拒绝写入并返回
if (queue->is_shutdown) {
c_Mutex_UnLock(&queue->lock);
return C_ERR_FAIL;
}
// 3. 循环数组插入数据
queue->data[queue->write_idx] = data;
queue->write_idx = (queue->write_idx + 1) % queue->capacity;
queue->size++;
// 4. 唤醒可能正在等待数据的消费者
c_Cond_Signal(&queue->not_empty);
c_Mutex_UnLock(&queue->lock);
return C_ERR_OK;
}
c_err_t c_LockQueue_Pop(c_LockQueue_t* queue, void** item) {
if (!queue ) return C_ERR_PARAM;
c_Mutex_Lock(&queue->lock);
// 1. 队空且未关闭时,消费者阻塞等待
while (queue->size == 0 && !queue->is_shutdown) {
c_Cond_Wait(&queue->not_empty, &queue->lock);
}
// 2. 如果队列已关闭且数据已被清空,优雅退出
if (queue->is_shutdown && queue->size == 0) {
c_Mutex_UnLock(&queue->lock);
return C_ERR_FAIL;
}
// 3. 循环数组取出数据
if (item) {
*item = queue->data[queue->read_idx];
}
queue->read_idx = (queue->read_idx + 1) % queue->capacity;
queue->size--;
// 4. 唤醒可能正在等待空间的生产者
c_Cond_Signal(&queue->not_full);
c_Mutex_UnLock(&queue->lock);
return C_ERR_OK;
}
c_size_t c_LockQueue_Size(c_LockQueue_t* queue) {
if (!queue) return 0;
c_Mutex_Lock(&queue->lock);
const c_size_t size = queue->size;
c_Mutex_UnLock(&queue->lock);
return size;
}
c_bool_t c_LockQueue_IsEmpty(c_LockQueue_t* queue) {
if (!queue) return true; // 安全檢查:無效隊列視為空
c_Mutex_Lock(&queue->lock);
const c_bool_t is_empty = (queue->size == 0);
c_Mutex_UnLock(&queue->lock);
return is_empty;
}
c_bool_t c_LockQueue_IsFull(c_LockQueue_t* queue) {
if (!queue) return false; // 安全檢查
c_Mutex_Lock(&queue->lock);
const c_bool_t is_full = (queue->size == queue->capacity);
c_Mutex_UnLock(&queue->lock);
return is_full;
}
c_bool_t c_LockQueue_TimedPop(c_LockQueue_t* queue, void** item, c_uint_t timeout_ms) {
if (!queue || !item) return C_FALSE;
c_Mutex_Lock(&queue->lock);
// 1. Calculate the absolute deadline for POSIX or track elapsed time for Windows
c_uint_t remaining_ms = timeout_ms;
#if defined(PLATFORM_POSIX)
// POSIX timedwait requires an absolute system calendar time deadline
struct timespec deadline;
struct timeval now;
gettimeofday(&now, NULL);
long long total_ns = (long long)now.tv_usec * 1000 + (long long)timeout_ms * 1000000;
deadline.tv_sec = now.tv_sec + total_ns / 1000000000LL;
deadline.tv_nsec = total_ns % 1000000000LL;
#elif defined(PLATFORM_WINDOWS)
// Windows tracks relative intervals natively via GetTickCount/GetTickCount64
ULONGLONG start_tick = GetTickCount64();
#endif
// 2. Loop to defend against Spurious Wakeups
while (queue->size == 0 && !queue->is_shutdown) {
if (remaining_ms == 0) {
// Out of time before cond wait or remaining time became zero
c_Mutex_UnLock(&queue->lock);
return C_FALSE;
}
// 3. Atomically release the lock and sleep until signaled or timed out
#if defined(PLATFORM_WINDOWS)
// SleepConditionVariableCS handles relative timeout natively
BOOL wait_success = SleepConditionVariableCS(&queue->not_empty.handle, &queue->lock.handle, remaining_ms);
if (!wait_success) {
if (GetLastError() == ERROR_TIMEOUT) {
c_Mutex_UnLock(&queue->lock);
return C_FALSE; // Dynamic Windows timeout hit
}
}
// Recalculate remaining time in case of spurious wakeups
ULONGLONG elapsed = GetTickCount64() - start_tick;
if (elapsed >= timeout_ms) {
remaining_ms = 0;
} else {
remaining_ms = timeout_ms - (c_uint_t)elapsed;
}
#elif defined(PLATFORM_POSIX)
// pthread_cond_timedwait takes the exact calculated deadline
int wait_result = pthread_cond_timedwait(&queue->not_empty.handle, &queue->lock.handle, &deadline);
if (wait_result != 0) {
// POSIX returns ETIMEDOUT (usually 110) if time limit expires
c_Mutex_UnLock(&queue->lock);
return C_FALSE;
}
// Re-verify remaining time using current clock just to be precise
gettimeofday(&now, NULL);
long long current_ms = (long long)now.tv_sec * 1000 + now.tv_usec / 1000;
long long deadline_ms = (long long)deadline.tv_sec * 1000 + deadline.tv_nsec / 1000000;
if (current_ms >= deadline_ms) {
remaining_ms = 0;
} else {
remaining_ms = (c_uint_t)(deadline_ms - current_ms);
}
#endif
}
// 4. Handle exit criteria if queue shut down during wait
if (queue->is_shutdown && queue->size == 0) {
c_Mutex_UnLock(&queue->lock);
return C_FALSE;
}
// 5. Securely pop data from circular buffer
*item = queue->data[queue->read_idx];
queue->read_idx = (queue->read_idx + 1) % queue->capacity;
queue->size--;
// 6. Signal blocked producers that space has cleared up
c_Cond_Signal(&queue->not_full);
c_Mutex_UnLock(&queue->lock);
return C_TRUE;
}
c_bool_t c_LockQueue_TimedPush(c_LockQueue_t* queue, void* item, c_uint_t timeout_ms) {
if (!queue || !item) return C_FALSE;
c_Mutex_Lock(&queue->lock);
// 1. Calculate the absolute deadline for POSIX or track elapsed time for Windows
c_uint_t remaining_ms = timeout_ms;
#if defined(PLATFORM_POSIX)
// POSIX timedwait requires an absolute wall-clock calendar deadline
struct timespec deadline;
struct timeval now;
gettimeofday(&now, NULL);
long long total_ns = (long long)now.tv_usec * 1000 + (long long)timeout_ms * 1000000;
deadline.tv_sec = now.tv_sec + total_ns / 1000000000LL;
deadline.tv_nsec = total_ns % 1000000000LL;
#elif defined(PLATFORM_WINDOWS)
// Windows tracks relative intervals natively via clock ticks
ULONGLONG start_tick = GetTickCount64();
#endif
// 2. Loop to defend against Spurious Wakeups while the queue is full
while (queue->size == queue->capacity && !queue->is_shutdown) {
if (remaining_ms == 0) {
// Out of time before cond wait or remaining time ticked down to zero
c_Mutex_UnLock(&queue->lock);
return C_FALSE;
}
// 3. Atomically release the lock and sleep until signaled or timed out
#if defined(PLATFORM_WINDOWS)
// SleepConditionVariableCS handles relative timeout natively
BOOL wait_success = SleepConditionVariableCS(&queue->not_full.handle, &queue->lock.handle, remaining_ms);
if (!wait_success) {
if (GetLastError() == ERROR_TIMEOUT) {
c_Mutex_UnLock(&queue->lock);
return C_FALSE; // Dynamic Windows timeout expired
}
}
// Recalculate remaining time in case of a spurious wakeup
ULONGLONG elapsed = GetTickCount64() - start_tick;
if (elapsed >= timeout_ms) {
remaining_ms = 0;
} else {
remaining_ms = timeout_ms - (c_uint_t)elapsed;
}
#elif defined(PLATFORM_POSIX)
// pthread_cond_timedwait takes the absolute calculated deadline
int wait_result = pthread_cond_timedwait(&queue->not_full.handle, &queue->lock.handle, &deadline);
if (wait_result != 0) {
// POSIX returns ETIMEDOUT (110) if time limit expires
c_Mutex_UnLock(&queue->lock);
return C_FALSE;
}
// Re-verify remaining time using current clock to stay precise
gettimeofday(&now, NULL);
long long current_ms = (long long)now.tv_sec * 1000 + now.tv_usec / 1000;
long long deadline_ms = (long long)deadline.tv_sec * 1000 + deadline.tv_nsec / 1000000;
if (current_ms >= deadline_ms) {
remaining_ms = 0;
} else {
remaining_ms = (c_uint_t)(deadline_ms - current_ms);
}
#endif
}
// 4. Handle exit criteria if queue shut down during wait
if (queue->is_shutdown) {
c_Mutex_UnLock(&queue->lock);
return C_FALSE;
}
// 5. Securely push data into the circular buffer
queue->data[queue->write_idx] = item;
queue->write_idx = (queue->write_idx + 1) % queue->capacity;
queue->size++;
// 6. Signal blocked consumers that data is ready
c_Cond_Signal(&queue->not_empty);
c_Mutex_UnLock(&queue->lock);
return C_TRUE;
}
+54
View File
@@ -0,0 +1,54 @@
#ifndef INCLUDED_C_LOCKQUEUE_H
#define INCLUDED_C_LOCKQUEUE_H
#ifndef INCLUDED_C_TYPES_H
#include <c_Types.h>
#endif /*INCLUDED_C_TYPES_H*/
#ifndef INCLUDED_C_MUTEX_H
#include <c_Mutex.h>
#endif /*INCLUDED_C_MUTEX_H*/
#ifndef INCLUDED_C_COND_H
#include <c_Cond.h>
#endif /*INCLUDED_C_COND_H*/
#ifndef INCLUDED_C_ALLOCATOR_H
#include <c_Allocator.h>
#endif /*INCLUDED_C_ALLOCATOR_H*/
/* ------------------------------------------------------------------------------------------------------------------ */
/* */
typedef struct {
void** data;
c_size_t capacity;
c_size_t write_idx;
c_size_t read_idx;
c_size_t size;
c_bool_t is_shutdown;
c_Mutex_t lock;
c_Cond_t not_full;
c_Cond_t not_empty;
c_Allocator_t allocator;
}c_LockQueue_t;
/* ------------------------------------------------------------------------------------------------------------------ */
/* */
c_err_t c_LockQueue_Init(c_LockQueue_t* queue, c_size_t capacity, c_Allocator_t* allocator);
void c_LockQueue_Destroy(c_LockQueue_t* queue);
void c_LockQueue_Shutdown(c_LockQueue_t* queue);
c_err_t c_LockQueue_Push(c_LockQueue_t* queue, void* data);
c_err_t c_LockQueue_Pop(c_LockQueue_t* queue, void** data);
c_bool_t c_LockQueue_TimedPop(c_LockQueue_t* queue, void** item, c_uint_t timeout_ms);
c_bool_t c_LockQueue_TimedPush(c_LockQueue_t* queue, void* item, c_uint_t timeout_ms);
c_size_t c_LockQueue_Size(c_LockQueue_t* queue);
c_bool_t c_LockQueue_IsEmpty(c_LockQueue_t* queue);
c_bool_t c_LockQueue_IsFull(c_LockQueue_t* queue);
#endif /*INCLUDED_C_LOCKQUEUE_H*/
+1
View File
@@ -0,0 +1 @@
#include <c_Thread.h>
+125
View File
@@ -0,0 +1,125 @@
#ifndef INCLUDED_C_THREAD_H
#define INCLUDED_C_THREAD_H
#ifndef INCLUDED_C_TYPES_H
#include <c_Types.h>
#endif /*INCLUDED_C_TYPES_H*/
/* ------------------------------------------------------------------------------------------------------------------ */
/* */
typedef void* c_ThreadResult_t;
#if defined(_WIN32) || defined(_WIN64)
#include <windows.h>
#include <process.h>
typedef HANDLE c_Thread_t;
typedef DWORD c_ThreadId_t; // Windows 使用 DWORD 作为线程 ID
#define C_THREAD_FUNC_RETURN_TYPE unsigned __stdcall
#define C_THREAD_FUNC_RETURN_VTYPE unsigned
#else
#include <pthread.h>
typedef pthread_t c_Thread_t;
typedef unsigned long c_ThreadId_t; // POSIX 转换为数字 ID
#define C_THREAD_FUNC_RETURN_TYPE void*
#define C_THREAD_FUNC_RETURN_VTYPE C_THREAD_FUNC_RETURN_TYPE
#endif
// Universal thread creation signature
typedef C_THREAD_FUNC_RETURN_TYPE (*c_ThreadFn_t)(void*);
C_STATIC_FORCE_INLINE
c_bool_t c_Thread_Create(c_Thread_t* thread, c_ThreadFn_t func, void* arg) {
#if defined(_WIN32) || defined(_WIN64)
// *thread = CreateThread(NULL, 0, func, arg, 0, NULL);
// return (*thread != NULL);
*thread = (HANDLE)_beginthreadex(NULL, 0, (unsigned (__stdcall *)(void*))func, arg, 0, NULL);
return (*thread != NULL);
#else
return (pthread_create(thread, NULL, func, arg) == 0);
#endif
}
C_STATIC_FORCE_INLINE
void c_Thread_Join(c_Thread_t thread) {
#if defined(_WIN32) || defined(_WIN64)
WaitForSingleObject(thread, INFINITE);
CloseHandle(thread);
#else
pthread_join(thread, NULL);
#endif
}
C_STATIC_FORCE_INLINE
void c_Thread_Sleep(unsigned int milliseconds) {
#if defined(_WIN32) || defined(_WIN64)
Sleep(milliseconds);
#else
usleep(milliseconds * 1000);
#endif
}
C_STATIC_FORCE_INLINE
c_ThreadId_t c_Thread_SelfId(void) {
#if defined(_WIN32) || defined(_WIN64)
return GetCurrentThreadId();
#else
return (c_ThreadId_t)pthread_self();
#endif
}
C_STATIC_FORCE_INLINE
c_bool_t c_Thread_Detach(c_Thread_t thread) {
#if defined(_WIN32) || defined(_WIN64)
// Windows 中,关闭线程句柄并不等于终止线程。
// 它只是减少内核对象的引用计数。线程运行结束时,内核对象会自动销毁。
// 这与 POSIX 的 detach 行为完全一致。
if (thread != NULL) {
return (CloseHandle(thread) != 0);
}
return C_FALSE;
#else
// POSIX 直接使用 pthread_detach
return (pthread_detach(thread) == 0);
#endif
}
C_STATIC_FORCE_INLINE
c_bool_t c_Thread_Equal(c_Thread_t t1, c_Thread_t t2) {
#if defined(_WIN32) || defined(_WIN64)
// Windows 下可以通过比较线程 ID 来判断是否为同一个线程
// 即使其中一个是 GetCurrentThread() 产生的伪句柄,GetThreadId 也能正确识别
return (GetThreadId(t1) == GetThreadId(t2));
#else
// POSIX 提供专用的比较函数
return (pthread_equal(t1, t2) != 0);
#endif
}
C_STATIC_FORCE_INLINE
c_bool_t c_Thread_JoinWithResult(c_Thread_t thread, c_ThreadResult_t* out_result) {
#if defined(_WIN32) || defined(_WIN64)
if (WaitForSingleObject(thread, INFINITE) == WAIT_OBJECT_0) {
DWORD exit_code = 0;
if (GetExitCodeThread(thread, &exit_code)) {
if (out_result) {
// 将 DWORD 转换为指针类型输出
*out_result = (c_ThreadResult_t)(uintptr_t)exit_code;
}
CloseHandle(thread);
return C_TRUE;
}
}
CloseHandle(thread); // 即使失败也尝试关闭句柄
return C_FALSE;
#else
// POSIX 直接在 join 时传入指针变量的地址
return (pthread_join(thread, out_result) == 0);
#endif
}
#define c_Thread_Exit(x) return (C_THREAD_FUNC_RETURN_VTYPE)(x)
#define c_Thread_Fn(fn) C_THREAD_FUNC_RETURN_TYPE fn
#endif /*INCLUDED_C_THREAD_H*/
+122
View File
@@ -0,0 +1,122 @@
#include <c_ThreadPool.h>
#include <c_Memory.h>
// Worker thread routine
static c_Thread_Fn(thread_pool_worker(void* arg)) {
c_ThreadPool_t* pool = (c_ThreadPool_t*)arg;
c_ThreadPoolTask_t* task;
while (!pool->is_shutdown) {
task = NULL;
// Use a 500ms timeout for timedpop to allow periodic shutdown condition verification
if (c_LockQueue_TimedPop(&pool->task_queue, (void**)&task, pool->check_interval_ms)!=C_ERR_OK) {
if (task) {
if (task->function) {
// Execute user payload safely
task->function(task->argument);
}
// C_FREE(task); // Free the memory allocated for the task wrapper
c_Pool_Free(&pool->task_pool, task);
}
}
}
c_Thread_Exit(0);
}
/**
* @brief Initializes a fixed-size thread pool.
*/
c_err_t c_ThreadPool_Init(c_ThreadPool_t* pool, int thread_count, int queue_capacity, int task_pool_size, int check_interval_ms) {
if (!pool || thread_count <= 0 || queue_capacity <= 0) return C_ERR_PARAM;
pool->is_shutdown = C_FALSE;
pool->thread_count = thread_count;
pool->check_interval_ms = check_interval_ms;
if (c_Pool_Init(&pool->task_pool, sizeof(c_ThreadPoolTask_t), task_pool_size)!=C_ERR_OK) {
return C_ERR_FAIL;
}
// Allocate the thread handle array
pool->threads = (c_Thread_t*)C_ALLOC(sizeof(c_Thread_t) * thread_count);
if (!pool->threads) {
return C_ERR_NOMEM;
}
// Initialize our previously constructed cross-platform thread-safe queue
c_err_t err = c_LockQueue_Init(&pool->task_queue, queue_capacity, 0);
if (err!=C_ERR_OK) {
C_FREE(pool->threads);
return err;
}
// Spawn the requested worker threads
for (int i = 0; i < thread_count; i++) {
if (!c_Thread_Create(&pool->threads[i], thread_pool_worker, pool)) {
// Rollback strategy on failures
pool->is_shutdown = C_TRUE;
c_LockQueue_Shutdown(&pool->task_queue);
for (int j = 0; j < i; j++) {
c_Thread_Join(pool->threads[j]);
}
c_LockQueue_Destroy(&pool->task_queue);
C_FREE(pool->threads);
return C_ERR_FAIL;
}
}
return C_ERR_OK;
}
/**
* @brief Submits a work payload to the pool.
*/
c_bool_t c_ThreadPool_Submit(c_ThreadPool_t* pool, void (*function)(void*), void* argument){
if (!pool || !function || pool->is_shutdown) return C_FALSE;
// c_ThreadPoolTask_t* task = (c_ThreadPoolTask_t*)C_ALLOC(sizeof(*task));
c_ThreadPoolTask_t* task = c_Pool_Alloc(&pool->task_pool);
if (!task) return C_FALSE;
task->function = function;
task->argument = argument;
// Push the task into our thread-safe buffer. If full, this blocks the calling thread
if (c_LockQueue_Push(&pool->task_queue, task)!=C_ERR_OK) {
c_Pool_Free(&pool->task_pool, task);
return C_FALSE;
}
return C_TRUE;
}
/**
* @brief Orderly terminates the thread pool, waiting for running jobs to finish.
*/
void c_ThreadPool_Destroy(c_ThreadPool_t* pool) {
if (!pool) return;
// 1. Terminate the processing loop
pool->is_shutdown = C_TRUE;
// 2. Shut down the queue to unblock workers waiting indefinitely
c_LockQueue_Shutdown(&pool->task_queue);
// 3. Join all worker threads safely
for (int i = 0; i < pool->thread_count; i++) {
c_Thread_Join(pool->threads[i]);
}
// 4. Drain any remaining unexecuted tasks to prevent memory leaks
// void* unexecuted_task = NULL;
// while (c_LockQueue_Pop(&pool->task_queue, &unexecuted_task)==C_ERR_OK) {
// C_FREE(unexecuted_task);
// }
c_Pool_DryUp(&pool->task_pool);
c_Pool_Destroy(&pool->task_pool);
// 5. Reclaim memory structures
c_LockQueue_Destroy(&pool->task_queue);
C_FREE(pool->threads);
}
+44
View File
@@ -0,0 +1,44 @@
#ifndef INCLUDED_C_THREADPOOL_H
#define INCLUDED_C_THREADPOOL_H
#ifndef INCLUDED_C_LOCKQUEUE_H
#include <c_LockQueue.h>
#endif /*INCLUDED_C_LOCKQUEUE_H*/
#ifndef INCLUDED_C_THREAD_H
#include <c_Thread.h>
#endif /*INCLUDED_C_THREAD_H*/
#ifndef INCLUDED_C_POOL_H
#include <c_Pool.h>
#endif /*INCLUDED_C_POOL_H*/
/* ------------------------------------------------------------------------------------------------------------------ */
/* */
// Struct representing a single executable unit of work
typedef struct {
void (*function)(void*); // Pointer to the user function
void* argument; // Argument passed to the function
} c_ThreadPoolTask_t;
typedef struct {
c_LockQueue_t task_queue; // Safe queue storing thread_pool_task_t pointers
c_Thread_t* threads; // Array of worker thread handles
int thread_count; // Total number of worker threads
volatile c_bool_t is_shutdown; // Shutdown flag
int check_interval_ms;
c_Pool_t task_pool;
} c_ThreadPool_t;
// Public Core API
c_err_t c_ThreadPool_Init(c_ThreadPool_t* pool, int thread_count, int queue_capacity, int task_pool_size, int check_interval_ms);
void c_ThreadPool_Destroy(c_ThreadPool_t* pool);
c_bool_t c_ThreadPool_Submit(c_ThreadPool_t* pool, void (*function)(void*), void* argument);
#endif /*INCLUDED_C_THREADPOOL_H*/
+39
View File
@@ -0,0 +1,39 @@
#include "c_ThreadPool.h"
#include <stdlib.h>
#include <stdio.h>
// Sample payload mimicking work
void compute_square(void* arg) {
int val = *(int*)arg;
printf("[Thread Pool Task] Processing square of %d = %d\n", val, val * val);
free(arg); // Free parameter memory passed during submission
}
int main(int argc, char** argv){
// Create a pool with 4 worker threads and a max capacity of 20 pending tasks
c_ThreadPool_t pool = {0};
c_ThreadPool_Init(&pool, 4, 20, 10, 500);
printf("--- Thread Pool Initialized with 4 Workers ---\n");
// Queue 10 dynamic processing computations
for (int i = 1; i <= 10; i++) {
int* num = (int*)malloc(sizeof(int));
*num = i;
if (!c_ThreadPool_Submit(&pool, compute_square, num)) {
fprintf(stderr, "[Thread Pool Task] Submit %d failed\n", i);
}
}
// Force main to simulate work before cleaning up
printf("All tasks submitted. Waiting for processing to settle...\n");
c_Thread_Sleep(2000);
printf("--- Destroying Thread Pool ---\n");
c_ThreadPool_Destroy(&pool);
printf("Thread pool destroyed cleanly.\n");
return 0;
}