ascii-chat 0.8.38
Real-time terminal-based video chat with ASCII art conversion
Loading...
Searching...
No Matches
thread_pool.c
Go to the documentation of this file.
1
6#include <ascii-chat/thread_pool.h>
7#include <ascii-chat/common.h>
8#include <ascii-chat/log/logging.h>
9#include <ascii-chat/platform/thread.h>
10#include <string.h>
11
12#ifndef _WIN32
13#include <signal.h>
14#include <pthread.h>
15#endif
16
17thread_pool_t *thread_pool_create(const char *pool_name) {
18 thread_pool_t *pool = SAFE_MALLOC(sizeof(thread_pool_t), thread_pool_t *);
19 if (!pool) {
20 SET_ERRNO(ERROR_MEMORY, "Failed to allocate thread pool");
21 return NULL;
22 }
23
24 memset(pool, 0, sizeof(*pool));
25
26 // Copy pool name (truncate if necessary)
27 if (pool_name) {
28 SAFE_STRNCPY(pool->name, pool_name, sizeof(pool->name));
29 } else {
30 SAFE_STRNCPY(pool->name, "unnamed", sizeof(pool->name));
31 }
32
33 // Initialize linked list
34 pool->threads = NULL;
35 pool->thread_count = 0;
36
37 // Initialize mutex
38 if (mutex_init(&pool->threads_mutex) != 0) {
39 SAFE_FREE(pool);
40 SET_ERRNO(ERROR_THREAD, "Failed to initialize thread pool mutex");
41 return NULL;
42 }
43
44 log_debug("Thread pool '%s' created", pool->name);
45 return pool;
46}
47
48void thread_pool_destroy(thread_pool_t *pool) {
49 if (!pool) {
50 return;
51 }
52
53 log_debug("Destroying thread pool '%s' (thread_count=%zu)", pool->name, pool->thread_count);
54
55 // Stop all threads first (if not already stopped)
56 if (pool->thread_count > 0) {
57 log_debug("Thread pool '%s' has %zu threads, stopping them first", pool->name, pool->thread_count);
59 }
60
61 // Destroy mutex
62 mutex_destroy(&pool->threads_mutex);
63
64 // Free pool
65 SAFE_FREE(pool);
66
67 log_debug("Thread pool destroyed");
68}
69
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) {
72 if (!pool) {
73 return SET_ERRNO(ERROR_INVALID_PARAM, "pool is NULL");
74 }
75
76 if (!thread_func) {
77 return SET_ERRNO(ERROR_INVALID_PARAM, "thread_func is NULL");
78 }
79
80 // Allocate thread entry
81 thread_pool_entry_t *entry = SAFE_MALLOC(sizeof(thread_pool_entry_t), thread_pool_entry_t *);
82 if (!entry) {
83 return SET_ERRNO(ERROR_MEMORY, "Failed to allocate thread pool entry");
84 }
85
86 memset(entry, 0, sizeof(*entry));
87 entry->stop_id = stop_id;
88 entry->thread_func = thread_func;
89 entry->thread_arg = thread_arg;
90 entry->next = NULL;
91
92 // Copy thread name (truncate if necessary)
93 if (thread_name) {
94 SAFE_STRNCPY(entry->name, thread_name, sizeof(entry->name));
95 } else {
96 // Auto-generate name
97 SAFE_SNPRINTF(entry->name, sizeof(entry->name), "%s_worker_%d", pool->name, stop_id);
98 }
99
100 // Create thread
101 if (asciichat_thread_create(&entry->thread, thread_func, thread_arg) != 0) {
102 // Save name before freeing entry
103 char thread_name_copy[64];
104 SAFE_STRNCPY(thread_name_copy, entry->name, sizeof(thread_name_copy));
105 SAFE_FREE(entry);
106 return SET_ERRNO(ERROR_THREAD, "Failed to create thread '%s' in pool '%s'", thread_name_copy, pool->name);
107 }
108
109 // Add to thread list (sorted by stop_id)
110 mutex_lock(&pool->threads_mutex);
111
112 if (!pool->threads || pool->threads->stop_id > stop_id) {
113 // Insert at head
114 entry->next = pool->threads;
115 pool->threads = entry;
116 } else {
117 // Find insertion point (maintain sorted order by stop_id)
118 thread_pool_entry_t *prev = pool->threads;
119 while (prev->next && prev->next->stop_id <= stop_id) {
120 prev = prev->next;
121 }
122 entry->next = prev->next;
123 prev->next = entry;
124 }
125
126 pool->thread_count++;
127 mutex_unlock(&pool->threads_mutex);
128
129 log_debug("Spawned thread '%s' (stop_id=%d) in pool '%s' (total_threads=%zu)", entry->name, stop_id, pool->name,
130 pool->thread_count);
131
132 return ASCIICHAT_OK;
133}
134
135asciichat_error_t thread_pool_stop_all(thread_pool_t *pool) {
136 if (!pool) {
137 return SET_ERRNO(ERROR_INVALID_PARAM, "pool is NULL");
138 }
139
140 mutex_lock(&pool->threads_mutex);
141
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);
145 return ASCIICHAT_OK;
146 }
147
148 log_debug("Stopping %zu threads in pool '%s' in stop_id order", pool->thread_count, pool->name);
149
150 // Threads are already sorted by stop_id (ascending), so just iterate and join
151 thread_pool_entry_t *entry = pool->threads;
152 while (entry) {
153 log_debug("Joining thread '%s' (stop_id=%d) in pool '%s'", entry->name, entry->stop_id, pool->name);
154
155 // Join thread (wait for it to exit)
156 if (asciichat_thread_join(&entry->thread, NULL) != 0) {
157 log_warn("Failed to join thread '%s' in pool '%s'", entry->name, pool->name);
158 }
159
160 thread_pool_entry_t *next = entry->next;
161 SAFE_FREE(entry);
162 entry = next;
163 }
164
165 // Clear list
166 pool->threads = NULL;
167 pool->thread_count = 0;
168
169 mutex_unlock(&pool->threads_mutex);
170
171 log_debug("All threads stopped in pool '%s'", pool->name);
172 return ASCIICHAT_OK;
173}
174
175size_t thread_pool_get_count(const thread_pool_t *pool) {
176 if (!pool) {
177 return 0;
178 }
179
180 // Note: We're not locking here for performance
181 // thread_count is a simple size_t which should be atomic on most platforms
182 // If strict thread safety is required, use mutex_lock/unlock
183 return pool->thread_count;
184}
185
186bool thread_pool_has_threads(const thread_pool_t *pool) {
187 return thread_pool_get_count(pool) > 0;
188}
189
190asciichat_error_t thread_pool_interrupt_all(thread_pool_t *pool, int sig) {
191 if (!pool) {
192 return SET_ERRNO(ERROR_INVALID_PARAM, "pool is NULL");
193 }
194
195#ifndef _WIN32
196 // Only available on POSIX systems (Unix/Linux/macOS)
197 mutex_lock(&pool->threads_mutex);
198
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);
202 return ASCIICHAT_OK;
203 }
204
205 log_debug("Sending signal %d to %zu threads in pool '%s'", sig, pool->thread_count, pool->name);
206
207 // Iterate through all threads and send signal
208 thread_pool_entry_t *entry = pool->threads;
209 int sent_count = 0;
210 int failed_count = 0;
211
212 while (entry) {
213 // pthread_kill returns 0 on success, non-zero error code on failure
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);
216 failed_count++;
217 } else {
218 sent_count++;
219 }
220 entry = entry->next;
221 }
222
223 mutex_unlock(&pool->threads_mutex);
224
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);
227
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);
230 }
231
232 return ASCIICHAT_OK;
233#else
234 // Windows: no pthread_kill equivalent needed - socket_shutdown() is sufficient
235 (void)sig; // Unused on Windows
236 log_debug("thread_pool_interrupt_all: no-op on Windows (socket shutdown is sufficient)");
237 return ASCIICHAT_OK;
238#endif
239}
void thread_pool_destroy(thread_pool_t *pool)
Definition thread_pool.c:48
asciichat_error_t thread_pool_interrupt_all(thread_pool_t *pool, int sig)
thread_pool_t * thread_pool_create(const char *pool_name)
Definition thread_pool.c:17
size_t thread_pool_get_count(const thread_pool_t *pool)
bool thread_pool_has_threads(const thread_pool_t *pool)
asciichat_error_t thread_pool_spawn(thread_pool_t *pool, void *(*thread_func)(void *), void *thread_arg, int stop_id, const char *thread_name)
Definition thread_pool.c:70
asciichat_error_t thread_pool_stop_all(thread_pool_t *pool)
int mutex_init(mutex_t *mutex)
Definition threading.c:16
int asciichat_thread_create(asciichat_thread_t *thread, void *(*start_routine)(void *), void *arg)
Definition threading.c:42
int asciichat_thread_join(asciichat_thread_t *thread, void **retval)
Definition threading.c:46
int mutex_destroy(mutex_t *mutex)
Definition threading.c:21