18 thread_pool_t *pool = SAFE_MALLOC(
sizeof(thread_pool_t), thread_pool_t *);
20 SET_ERRNO(ERROR_MEMORY,
"Failed to allocate thread pool");
24 memset(pool, 0,
sizeof(*pool));
28 SAFE_STRNCPY(pool->name, pool_name,
sizeof(pool->name));
30 SAFE_STRNCPY(pool->name,
"unnamed",
sizeof(pool->name));
35 pool->thread_count = 0;
40 SET_ERRNO(ERROR_THREAD,
"Failed to initialize thread pool mutex");
44 log_debug(
"Thread pool '%s' created", pool->name);
70asciichat_error_t
thread_pool_spawn(thread_pool_t *pool,
void *(*thread_func)(
void *),
void *thread_arg,
int stop_id,
71 const char *thread_name) {
73 return SET_ERRNO(ERROR_INVALID_PARAM,
"pool is NULL");
77 return SET_ERRNO(ERROR_INVALID_PARAM,
"thread_func is NULL");
81 thread_pool_entry_t *entry = SAFE_MALLOC(
sizeof(thread_pool_entry_t), thread_pool_entry_t *);
83 return SET_ERRNO(ERROR_MEMORY,
"Failed to allocate thread pool entry");
86 memset(entry, 0,
sizeof(*entry));
87 entry->stop_id = stop_id;
88 entry->thread_func = thread_func;
89 entry->thread_arg = thread_arg;
94 SAFE_STRNCPY(entry->name, thread_name,
sizeof(entry->name));
97 SAFE_SNPRINTF(entry->name,
sizeof(entry->name),
"%s_worker_%d", pool->name, stop_id);
103 char thread_name_copy[64];
104 SAFE_STRNCPY(thread_name_copy, entry->name,
sizeof(thread_name_copy));
106 return SET_ERRNO(ERROR_THREAD,
"Failed to create thread '%s' in pool '%s'", thread_name_copy, pool->name);
110 mutex_lock(&pool->threads_mutex);
112 if (!pool->threads || pool->threads->stop_id > stop_id) {
114 entry->next = pool->threads;
115 pool->threads = entry;
118 thread_pool_entry_t *prev = pool->threads;
119 while (prev->next && prev->next->stop_id <= stop_id) {
122 entry->next = prev->next;
126 pool->thread_count++;
127 mutex_unlock(&pool->threads_mutex);
129 log_debug(
"Spawned thread '%s' (stop_id=%d) in pool '%s' (total_threads=%zu)", entry->name, stop_id, pool->name,
137 return SET_ERRNO(ERROR_INVALID_PARAM,
"pool is NULL");
140 mutex_lock(&pool->threads_mutex);
142 if (pool->thread_count == 0) {
143 mutex_unlock(&pool->threads_mutex);
144 log_debug(
"Thread pool '%s' has no threads to stop", pool->name);
148 log_debug(
"Stopping %zu threads in pool '%s' in stop_id order", pool->thread_count, pool->name);
151 thread_pool_entry_t *entry = pool->threads;
153 log_debug(
"Joining thread '%s' (stop_id=%d) in pool '%s'", entry->name, entry->stop_id, pool->name);
157 log_warn(
"Failed to join thread '%s' in pool '%s'", entry->name, pool->name);
160 thread_pool_entry_t *next = entry->next;
166 pool->threads = NULL;
167 pool->thread_count = 0;
169 mutex_unlock(&pool->threads_mutex);
171 log_debug(
"All threads stopped in pool '%s'", pool->name);
192 return SET_ERRNO(ERROR_INVALID_PARAM,
"pool is NULL");
197 mutex_lock(&pool->threads_mutex);
199 if (pool->thread_count == 0) {
200 mutex_unlock(&pool->threads_mutex);
201 log_debug(
"Thread pool '%s' has no threads to interrupt", pool->name);
205 log_debug(
"Sending signal %d to %zu threads in pool '%s'", sig, pool->thread_count, pool->name);
208 thread_pool_entry_t *entry = pool->threads;
210 int failed_count = 0;
214 if (pthread_kill(entry->thread, sig) != 0) {
215 log_warn(
"Failed to send signal %d to thread '%s' in pool '%s'", sig, entry->name, pool->name);
223 mutex_unlock(&pool->threads_mutex);
225 log_debug(
"Sent signal %d to %d/%zu threads in pool '%s' (%d failures)", sig, sent_count, pool->thread_count,
226 pool->name, failed_count);
228 if (failed_count > 0 && sent_count == 0) {
229 return SET_ERRNO(ERROR_THREAD,
"Failed to send signal to any threads in pool '%s'", pool->name);
236 log_debug(
"thread_pool_interrupt_all: no-op on Windows (socket shutdown is sufficient)");
asciichat_error_t thread_pool_spawn(thread_pool_t *pool, void *(*thread_func)(void *), void *thread_arg, int stop_id, const char *thread_name)