1 /*****************************************************************************
2 * thread.c: Test for thread API
3 *****************************************************************************
4 * Copyright (C) 2020 Marvin Scholz
6 * This program is free software; you can redistribute it and/or modify it
7 * under the terms of the GNU Lesser General Public License as published by
8 * the Free Software Foundation; either version 2.1 of the License, or
9 * (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public License
17 * along with this program; if not, write to the Free Software Foundation,
18 * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
19 *****************************************************************************/
31 #include <vlc_common.h>
33 static int thread_data_magic
= 0x1234;
34 static int thread_return_magic
= 0x4321;
36 // Join thread and verify return
37 #define TEST_THREAD_JOIN(th, eret) \
44 // Cancels thread, joins and verifies return
45 #define TEST_THREAD_CANCEL_JOIN(th) \
48 TEST_THREAD_JOIN(th, VLC_THREAD_CANCELED); \
51 // Join thread and verify magic return value
52 #define TEST_THREAD_JOIN_MAGIC(th) \
53 TEST_THREAD_JOIN(th, &thread_return_magic)
56 #define TEST_THREAD_CLONE(th, f, data) \
57 assert(vlc_clone(th, f, data, VLC_THREAD_PRIORITY_LOW) == 0)
59 // Spawn thread with magic data
60 #define TEST_THREAD_CLONE_MAGIC(th, f) \
61 TEST_THREAD_CLONE(th, f, &thread_data_magic)
64 // Structure for the condition tests
67 int state
; // Count of threads starting, or -1 when threads may exit
69 vlc_cond_t cond_started
; // Signalled when a thread started
70 vlc_cond_t cond_end
; // Waited on by thread before terminating
73 static void cond_data_init(struct cond_data
*data
, int count
)
78 vlc_mutex_init(&data
->mutex
);
79 vlc_cond_init(&data
->cond_started
);
80 vlc_cond_init(&data
->cond_end
);
85 * Test simple thread creation
87 static void *thread_func_noop(void *data
)
89 assert(data
== &thread_data_magic
);
90 return &thread_return_magic
;
93 static void test__thread_create()
96 TEST_THREAD_CLONE_MAGIC(&th
, thread_func_noop
);
97 TEST_THREAD_JOIN_MAGIC(th
);
102 * Test simple thread cancelation
104 static void *thread_func_loop(void *data
)
106 assert(data
== &thread_data_magic
);
110 return &thread_return_magic
;
113 static void test__thread_cancelation()
116 TEST_THREAD_CLONE_MAGIC(&th
, thread_func_loop
);
117 TEST_THREAD_CANCEL_JOIN(th
);
122 * Test thread cancellation with cleanup
124 static void thread_func_cleanup(void *data
)
126 int *cleanup_state
= data
;
127 assert(*cleanup_state
== 0);
131 static void *thread_func_loop_cleanup(void *data
)
133 int *cleanup_state
= data
;
134 assert(*cleanup_state
== 0);
136 vlc_cleanup_push(thread_func_cleanup
, data
);
142 return &thread_return_magic
;
145 static void test__thread_cancelation_with_cleanup()
148 int th_cleanup_state
= 0;
150 TEST_THREAD_CLONE(&th
, thread_func_loop_cleanup
, &th_cleanup_state
);
151 TEST_THREAD_CANCEL_JOIN(th
);
152 assert(th_cleanup_state
== 1);
157 * - Test broadcasting a condition, waking several threads
158 * - Test waiting on condition
160 static void *thread_func_cond(void *ptr
)
162 struct cond_data
*data
= ptr
;
164 // Decrement state and signal start of thread
165 vlc_mutex_lock(&data
->mutex
);
167 vlc_cond_signal(&data
->cond_started
);
168 vlc_mutex_unlock(&data
->mutex
);
170 // Wait until termination is allowed
171 vlc_mutex_lock(&data
->mutex
);
172 mutex_cleanup_push(&data
->mutex
);
173 while(data
->state
!= -1) {
174 vlc_cond_wait(&data
->cond_end
, &data
->mutex
);
177 vlc_mutex_unlock(&data
->mutex
);
179 return &thread_return_magic
;
182 static void test__cond_broadcast()
184 struct cond_data data
;
185 vlc_thread_t threads
[20];
187 cond_data_init(&data
, ARRAY_SIZE(threads
));
190 for (size_t i
= 0; i
< ARRAY_SIZE(threads
); ++i
) {
191 TEST_THREAD_CLONE(&threads
[i
], thread_func_cond
, &data
);
194 // Wait for all threads to start
195 vlc_mutex_lock(&data
.mutex
);
196 while(data
.state
> 0) {
197 vlc_cond_wait(&data
.cond_started
, &data
.mutex
);
199 vlc_mutex_unlock(&data
.mutex
);
201 // Broadcast threads-may-end condition to all threads
202 vlc_mutex_lock(&data
.mutex
);
204 vlc_cond_broadcast(&data
.cond_end
);
205 vlc_mutex_unlock(&data
.mutex
);
207 // Wait for all threads to exit
208 for (size_t i
= 0; i
< ARRAY_SIZE(threads
); ++i
) {
209 TEST_THREAD_JOIN_MAGIC(threads
[i
]);
213 static void test__cond_wait()
215 struct cond_data data
;
216 cond_data_init(&data
, 1);
219 TEST_THREAD_CLONE(&th
, thread_func_cond
, &data
);
221 // Wait for thread to start
222 vlc_mutex_lock(&data
.mutex
);
223 while(data
.state
!= 0) {
224 vlc_cond_wait(&data
.cond_started
, &data
.mutex
);
226 vlc_mutex_unlock(&data
.mutex
);
228 // Signal threads-may-end condition to thread
229 vlc_mutex_lock(&data
.mutex
);
231 vlc_cond_signal(&data
.cond_end
);
232 vlc_mutex_unlock(&data
.mutex
);
234 // Wait for thread to exit
235 TEST_THREAD_JOIN_MAGIC(th
);
240 * Test waiting on condition and let it timeout
242 static void *thread_func_cond_timeout(void *ptr
)
244 struct cond_data
*data
= ptr
;
246 // Decrement state and signal start of thread
247 vlc_mutex_lock(&data
->mutex
);
249 vlc_cond_signal(&data
->cond_started
);
250 vlc_mutex_unlock(&data
->mutex
);
252 vlc_mutex_lock(&data
->mutex
);
253 mutex_cleanup_push(&data
->mutex
);
255 // Wait until termination is allowed (which is never signalled)
256 vlc_tick_t now
= vlc_tick_now();
257 vlc_tick_t deadline
= now
+ VLC_TICK_FROM_MS(25);
258 int ret
= vlc_cond_timedwait(&data
->cond_end
, &data
->mutex
, deadline
);
259 assert(ret
== ETIMEDOUT
);
261 vlc_tick_t elapsed
= vlc_tick_now() - now
;
262 assert(elapsed
>= VLC_TICK_FROM_MS(25));
265 vlc_mutex_unlock(&data
->mutex
);
267 return &thread_return_magic
;
270 static void test__cond_wait_timeout()
272 struct cond_data data
;
273 cond_data_init(&data
, 1);
276 TEST_THREAD_CLONE(&th
, thread_func_cond_timeout
, &data
);
278 // Wait for thread to start
279 vlc_mutex_lock(&data
.mutex
);
280 while(data
.state
!= 0) {
281 vlc_cond_wait(&data
.cond_started
, &data
.mutex
);
283 vlc_mutex_unlock(&data
.mutex
);
285 // Never signal that the thread may end, on purpose
286 // as it should timeout.
288 // Wait for thread exit
289 TEST_THREAD_JOIN_MAGIC(th
);
294 * Test cancelling vlc_tick_sleep
296 static void *thread_func_tick_sleep(void *data
)
298 assert(data
== &thread_data_magic
);
299 vlc_tick_sleep(VLC_TICK_FROM_SEC(10));
300 return &thread_return_magic
;
303 static void test__vlc_tick_sleep_cancelation()
306 TEST_THREAD_CLONE_MAGIC(&th
, thread_func_tick_sleep
);
307 TEST_THREAD_CANCEL_JOIN(th
);
312 * Test sleeping for delay with vlc_tick_sleep
314 static void test__vlc_tick_sleep()
316 vlc_tick_t now
= vlc_tick_now();
318 vlc_tick_sleep(VLC_TICK_FROM_MS(25));
320 vlc_tick_t elapsed
= vlc_tick_now() - now
;
321 assert(elapsed
>= VLC_TICK_FROM_MS(25));
326 * Main function executing all tests above
332 test__thread_create();
333 test__thread_cancelation();
334 test__thread_cancelation_with_cleanup();
336 test__cond_wait_timeout();
337 test__cond_broadcast();
338 test__vlc_tick_sleep_cancelation();
339 test__vlc_tick_sleep();