GCC Code Coverage Report


Directory: src/gate/
File: src/gate/threadpools.c
Date: 2026-10-19 17:06:30
Exec Total Coverage
Lines: 118 139 84.9%
Functions: 11 11 100.0%
Branches: 41 68 60.3%

Line Branch Exec Source
1 /* GATE PROJECT LICENSE:
2 +----------------------------------------------------------------------------+
3 | Copyright (c) 2018-2026, Stefan Meislinger <sm@opengate.at> |
4 | All rights reserved. |
5 | |
6 | Redistribution and use in source and binary forms, with or without |
7 | modification, are permitted provided that the following conditions are met:|
8 | |
9 | 1. Redistributions of source code must retain the above copyright notice, |
10 | this list of conditions and the following disclaimer. |
11 | 2. Redistributions in binary form must reproduce the above copyright |
12 | notice, this list of conditions and the following disclaimer in the |
13 | documentation and/or other materials provided with the distribution. |
14 | |
15 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"|
16 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
17 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
18 | ARE DISCLAIMED.IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE |
19 | LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR |
20 | CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF |
21 | SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS |
22 | INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN |
23 | CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) |
24 | ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF |
25 | THE POSSIBILITY OF SUCH DAMAGE. |
26 +----------------------------------------------------------------------------+
27 */
28 #include "gate/threadpools.h"
29 #include "gate/synchronization.h"
30 #include "gate/arrays.h"
31 #include "gate/results.h"
32
33 struct gate_threadpool_class
34 {
35 gate_mutex_t lock;
36 gate_synccondition_t cond;
37 gate_atomic_int_t state;
38 gate_uint32_t max_pending_tasks;
39 gate_uint32_t max_threads;
40 gate_arraylist_t tasks;
41 gate_thread_t threads[1];
42 };
43
44
45 #define GATE_THREADPOOL_STATE_OFFLINE 0
46 #define GATE_THREADPOOL_STATE_STARTING 1
47 #define GATE_THREADPOOL_STATE_ONLINE 2
48 #define GATE_THREADPOOL_STATE_STOPPING 3
49
50 1 static gate_bool_t is_starting(gate_threadpool_t pool)
51 {
52 1 return gate_atomic_int_get(&pool->state) == GATE_THREADPOOL_STATE_STARTING;
53 }
54
55 7 static gate_bool_t is_online(gate_threadpool_t pool)
56 {
57 7 return gate_atomic_int_get(&pool->state) == GATE_THREADPOOL_STATE_ONLINE;
58 }
59
60 1 static gate_result_t gate_threadpool_code(void* param)
61 {
62 1 gate_result_t ret = GATE_RESULT_OK;
63 1 gate_threadpool_t pool = (gate_threadpool_t)param;
64 void* ptr_entry;
65
66 {
67 1 ret = gate_mutex_acquire(&pool->lock);
68
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 if (GATE_FAILED(ret))
69 {
70 ✗ return ret;
71 }
72
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
1 while (is_starting(pool))
73 {
74 ✗ gate_synccondition_timed_wait(&pool->cond, &pool->lock, 200);
75 }
76 1 gate_mutex_release(&pool->lock);
77 }
78
79
2/2
✓ Branch 1 taken 2 times.
✓ Branch 2 taken 1 times.
3 while (is_online(pool))
80 {
81 2 gate_runnable_t* task = NULL;
82
83 {
84 2 ret = gate_mutex_acquire(&pool->lock);
85
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
2 if (GATE_FAILED(ret))
86 {
87 ✗ break;
88 }
89
90 do
91 {
92
4/4
✓ Branch 1 taken 2 times.
✓ Branch 2 taken 1 times.
✓ Branch 4 taken 1 times.
✓ Branch 5 taken 1 times.
3 while ((gate_arraylist_length(pool->tasks) == 0) && is_online(pool))
93 {
94 1 gate_synccondition_timed_wait(&pool->cond, &pool->lock, 1000);
95 }
96
97
2/2
✓ Branch 1 taken 1 times.
✓ Branch 2 taken 1 times.
2 if (!is_online(pool))
98 {
99 1 break;
100 }
101
102
1/2
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
1 if (gate_arraylist_length(pool->tasks) > 0)
103 {
104 1 ptr_entry = gate_arraylist_get(pool->tasks, 0);
105 }
106 else
107 {
108 ✗ ptr_entry = NULL;
109 }
110
111
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
1 if (ptr_entry != NULL)
112 {
113 1 task = *(gate_runnable_t**)ptr_entry;
114
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
1 if (task != NULL)
115 {
116 1 gate_object_retain(task);
117 }
118 1 gate_arraylist_remove(pool->tasks, 0, 1);
119 }
120 } while (0);
121 2 gate_mutex_release(&pool->lock);
122 }
123
124
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 1 times.
2 if (task != NULL)
125 {
126 1 gate_runnable_run(task);
127 1 gate_object_release(task);
128 }
129 }
130
131 1 return ret;
132 }
133
134 2 gate_result_t gate_threadpool_create(gate_threadpool_t* ptr_pool, gate_uint32_t thread_count, gate_uint32_t max_tasks)
135 {
136 2 gate_result_t ret = GATE_RESULT_FAILED;
137 2 gate_threadpool_t pool = NULL;
138 do
139 {
140
1/2
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
2 if (thread_count == 0)
141 {
142 2 thread_count = 1;
143 }
144
1/2
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
2 if (max_tasks == 0)
145 {
146 2 max_tasks = 1;
147 }
148
149 2 pool = gate_mem_alloc(sizeof(struct gate_threadpool_class) + sizeof(gate_thread_t) * thread_count);
150
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
2 if (pool == NULL) break;
151 2 gate_mem_clear(pool, sizeof(struct gate_threadpool_class) + sizeof(gate_thread_t) * thread_count);
152
153 2 gate_atomic_int_set(&pool->state, GATE_THREADPOOL_STATE_OFFLINE);
154
155 2 ret = gate_mutex_create(&pool->lock);
156
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
2 if (GATE_FAILED(ret))
157 {
158 ✗ gate_mem_dealloc(pool);
159 ✗ pool = NULL;
160 ✗ break;
161 }
162
163 2 ret = gate_synccondition_create(&pool->cond);
164
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
2 GATE_BREAK_IF_FAILED(ret);
165
166 2 pool->tasks = gate_arraylist_create(sizeof(gate_runnable_t*), NULL, 16, &gate_object_ptr_copyctor, &gate_object_ptr_dtor);
167
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 2 times.
2 if (gate_arraylist_is_null(pool->tasks))
168 {
169 ✗ break;
170 }
171
172 2 pool->max_threads = thread_count;
173 2 pool->max_pending_tasks = max_tasks;
174
175 2 *ptr_pool = pool;
176 2 pool = NULL;
177 2 ret = GATE_RESULT_OK;
178 } while (0);
179
180
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
2 if (pool != NULL)
181 {
182 ✗ gate_arraylist_release(&pool->tasks);
183 ✗ gate_synccondition_destroy(&pool->cond);
184 ✗ gate_mutex_destroy(&pool->lock);
185 }
186
187 2 return ret;
188 }
189
190 2 gate_result_t gate_threadpool_destroy(gate_threadpool_t pool)
191 {
192 2 gate_result_t ret = GATE_RESULT_OK;
193 gate_result_t result;
194 2 gate_threadpool_stop(pool);
195
196 2 gate_arraylist_release(&pool->tasks);
197
198 2 result = gate_synccondition_destroy(&pool->cond);
199
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
2 if (GATE_FAILED(result)) ret = result;
200
201 2 result = gate_mutex_destroy(&pool->lock);
202
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
2 if (GATE_FAILED(result)) ret = result;
203
204 2 gate_mem_dealloc(pool);
205
206 2 return ret;
207 }
208
209 1 gate_result_t gate_threadpool_start(gate_threadpool_t pool)
210 {
211 1 gate_result_t ret = GATE_RESULT_FAILED;
212 1 gate_size_t ndx = 0;
213
214
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
1 if (GATE_THREADPOOL_STATE_OFFLINE != gate_atomic_int_xchg_if(
215 &pool->state, GATE_THREADPOOL_STATE_OFFLINE, GATE_THREADPOOL_STATE_STARTING))
216 {
217 ✗ ret = GATE_RESULT_INVALIDSTATE;
218 }
219 else
220 {
221
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 1 times.
2 for (ndx = 0; ndx != pool->max_threads; ++ndx)
222 {
223 1 ret = gate_thread_start_code(&gate_threadpool_code, pool, &pool->threads[ndx], NULL);
224
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 if (GATE_FAILED(ret))
225 {
226 ✗ gate_atomic_int_set(&pool->state, GATE_THREADPOOL_STATE_STOPPING);
227 ✗ gate_synccondition_signal_all(&pool->cond);
228 ✗ while (ndx-- != 0)
229 {
230 ✗ gate_thread_join(&pool->threads[ndx], NULL);
231 }
232 ✗ break;
233 }
234 }
235
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
1 if (GATE_SUCCEEDED(ret))
236 {
237 1 gate_atomic_int_set(&pool->state, GATE_THREADPOOL_STATE_ONLINE);
238 1 gate_synccondition_signal_all(&pool->cond);
239 }
240 else
241 {
242 ✗ gate_atomic_int_set(&pool->state, GATE_THREADPOOL_STATE_OFFLINE);
243 }
244 }
245 1 return ret;
246 }
247
248 3 gate_result_t gate_threadpool_stop(gate_threadpool_t pool)
249 {
250 3 gate_result_t ret = GATE_RESULT_FAILED;
251 gate_size_t ndx;
252
253
2/2
✓ Branch 1 taken 2 times.
✓ Branch 2 taken 1 times.
3 if (GATE_THREADPOOL_STATE_ONLINE != gate_atomic_int_xchg_if(
254 &pool->state, GATE_THREADPOOL_STATE_ONLINE, GATE_THREADPOOL_STATE_STOPPING))
255 {
256 2 ret = GATE_RESULT_INVALIDSTATE;
257 }
258 else
259 {
260 1 gate_synccondition_signal_all(&pool->cond);
261
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 1 times.
2 for (ndx = 0; ndx != pool->max_threads; ++ndx)
262 {
263 1 gate_thread_join(&pool->threads[ndx], NULL);
264 }
265 1 gate_atomic_int_set(&pool->state, GATE_THREADPOOL_STATE_OFFLINE);
266 1 ret = GATE_RESULT_OK;
267 }
268 3 return ret;
269 }
270 2 gate_result_t gate_threadpool_add_task(gate_threadpool_t pool, gate_runnable_t* task)
271 {
272 2 gate_result_t ret = GATE_RESULT_FAILED;
273 do
274 {
275 2 ret = gate_mutex_acquire(&pool->lock);
276
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
2 GATE_BREAK_IF_FAILED(ret);
277
278
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 2 times.
2 if (gate_arraylist_length(pool->tasks) >= pool->max_pending_tasks)
279 {
280 ✗ ret = GATE_RESULT_OUTOFBOUNDS;
281 }
282
1/2
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
2 else if (NULL != gate_arraylist_add(pool->tasks, &task))
283 {
284 2 ret = GATE_RESULT_OK;
285 }
286 else
287 {
288 ✗ ret = GATE_RESULT_FAILED;
289 }
290 2 gate_mutex_release(&pool->lock);
291 2 gate_synccondition_signal_all(&pool->cond);
292 } while (0);
293 2 return ret;
294 }
295
296 1 static gate_bool_t gate_threadpool_remove_task_condition(void const* item, void* param)
297 {
298 1 gate_runnable_t* const* src = (gate_runnable_t* const*)item;
299 1 gate_runnable_t* dst = (gate_runnable_t*)param;
300 1 return *src == dst;
301 }
302
303 1 gate_result_t gate_threadpool_remove_task(gate_threadpool_t pool, gate_runnable_t* task)
304 {
305 1 gate_result_t ret = GATE_RESULT_FAILED;
306 gate_size_t count1, count2;
307 do
308 {
309 1 ret = gate_mutex_acquire(&pool->lock);
310
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 GATE_BREAK_IF_FAILED(ret);
311 1 count1 = gate_arraylist_length(pool->tasks);
312 1 ret = gate_arraylist_remove_if(pool->tasks, gate_threadpool_remove_task_condition, task);
313
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
1 if (GATE_SUCCEEDED(ret))
314 {
315 1 count2 = gate_arraylist_length(pool->tasks);
316
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 if (count2 == count1)
317 {
318 /* nothing was removed, signal "unchanged" OK-state */
319 ✗ ret = GATE_RESULT_OK_UNCHANGED;
320 }
321 }
322 1 gate_mutex_release(&pool->lock);
323 } while (0);
324 1 return ret;
325 }
326 4 gate_size_t gate_threadpool_pending_tasks(gate_threadpool_t pool)
327 {
328 4 gate_size_t ret = 0;
329 4 gate_result_t result = gate_mutex_acquire(&pool->lock);
330
1/2
✓ Branch 0 taken 4 times.
✗ Branch 1 not taken.
4 if (GATE_SUCCEEDED(result))
331 {
332 4 ret = gate_arraylist_length(pool->tasks);
333 4 gate_mutex_release(&pool->lock);
334 }
335 4 return ret;
336 }
337