ascii-chat 0.8.38
Real-time terminal-based video chat with ASCII art conversion
Loading...
Searching...
No Matches
thread_pool.c File Reference

🧵 Generic thread pool implementation for managing worker threads More...

Go to the source code of this file.

Functions

thread_pool_t * thread_pool_create (const char *pool_name)
 
void thread_pool_destroy (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)
 
asciichat_error_t thread_pool_stop_all (thread_pool_t *pool)
 
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_interrupt_all (thread_pool_t *pool, int sig)
 

Detailed Description

🧵 Generic thread pool implementation for managing worker threads

Definition in file thread_pool.c.

Function Documentation

◆ thread_pool_create()

thread_pool_t * thread_pool_create ( const char *  pool_name)

Definition at line 17 of file thread_pool.c.

17 {
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}
int mutex_init(mutex_t *mutex)
Definition threading.c:16

References mutex_init().

Referenced by acds_server_init(), server_main(), and tcp_server_add_client().

◆ thread_pool_destroy()

void thread_pool_destroy ( thread_pool_t *  pool)

Definition at line 48 of file thread_pool.c.

48 {
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}
asciichat_error_t thread_pool_stop_all(thread_pool_t *pool)
int mutex_destroy(mutex_t *mutex)
Definition threading.c:21

References mutex_destroy(), and thread_pool_stop_all().

Referenced by acds_server_shutdown(), server_main(), tcp_server_destroy(), and tcp_server_remove_client().

◆ thread_pool_get_count()

size_t thread_pool_get_count ( const thread_pool_t *  pool)

Definition at line 175 of file thread_pool.c.

175 {
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}

Referenced by tcp_server_get_thread_count(), tcp_server_spawn_thread(), and thread_pool_has_threads().

◆ thread_pool_has_threads()

bool thread_pool_has_threads ( const thread_pool_t *  pool)

Definition at line 186 of file thread_pool.c.

186 {
187 return thread_pool_get_count(pool) > 0;
188}
size_t thread_pool_get_count(const thread_pool_t *pool)

References thread_pool_get_count().

◆ thread_pool_interrupt_all()

asciichat_error_t thread_pool_interrupt_all ( thread_pool_t *  pool,
int  sig 
)

Definition at line 190 of file thread_pool.c.

190 {
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}

◆ thread_pool_spawn()

asciichat_error_t thread_pool_spawn ( thread_pool_t *  pool,
void *(*)(void *)  thread_func,
void *  thread_arg,
int  stop_id,
const char *  thread_name 
)

Definition at line 70 of file thread_pool.c.

71 {
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}
int asciichat_thread_create(asciichat_thread_t *thread, void *(*start_routine)(void *), void *arg)
Definition threading.c:42

References asciichat_thread_create().

Referenced by acds_server_init(), audio_start_thread(), capture_start_thread(), keepalive_start_thread(), protocol_start_connection(), server_main(), and tcp_server_spawn_thread().

◆ thread_pool_stop_all()

asciichat_error_t thread_pool_stop_all ( thread_pool_t *  pool)

Definition at line 135 of file thread_pool.c.

135 {
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}
int asciichat_thread_join(asciichat_thread_t *thread, void **retval)
Definition threading.c:46

References asciichat_thread_join().

Referenced by protocol_stop_connection(), tcp_server_stop_client_threads(), and thread_pool_destroy().