mirror of
https://source.denx.de/u-boot/u-boot.git
synced 2025-08-14 03:06:59 +02:00
Add a test for uthread mutexes. Signed-off-by: Jerome Forissier <jerome.forissier@linaro.org>
147 lines
3.8 KiB
C
147 lines
3.8 KiB
C
// SPDX-License-Identifier: GPL-2.0+
|
|
/*
|
|
* Copyright 2025 Linaro Limited
|
|
*
|
|
* Unit test for uthread
|
|
*/
|
|
|
|
#include <stdbool.h>
|
|
#include <test/lib.h>
|
|
#include <test/ut.h>
|
|
#include <uthread.h>
|
|
|
|
static int count;
|
|
|
|
/* A thread entry point */
|
|
static void worker(void *arg)
|
|
{
|
|
int loops = (int)(unsigned long)arg;
|
|
int i;
|
|
|
|
for (i = 0; i < loops; i++) {
|
|
count++;
|
|
uthread_schedule();
|
|
}
|
|
}
|
|
|
|
/*
|
|
* uthread() - testing the uthread API
|
|
*
|
|
* This function creates two threads with the same entry point. The first one
|
|
* receives 5 as an argument, the second one receives 10. The number indicates
|
|
* the number of time the worker thread should loop on uthread_schedule()
|
|
* before returning. The workers increment a global counter each time they loop.
|
|
* As a result the main thread knows how many times it should call
|
|
* uthread_schedule() to let the two threads proceed, and it also knows which
|
|
* value the counter should have at any moment.
|
|
*/
|
|
static int uthread(struct unit_test_state *uts)
|
|
{
|
|
int i;
|
|
int id1, id2;
|
|
|
|
count = 0;
|
|
id1 = uthread_grp_new_id();
|
|
ut_assert(id1 != 0);
|
|
id2 = uthread_grp_new_id();
|
|
ut_assert(id2 != 0);
|
|
ut_assert(id1 != id2);
|
|
ut_assertok(uthread_create(NULL, worker, (void *)5, 0, id1));
|
|
ut_assertok(uthread_create(NULL, worker, (void *)10, 0, 0));
|
|
/*
|
|
* The first call is expected to schedule the first worker, which will
|
|
* schedule the second one, which will schedule back to the main thread
|
|
* (here). Therefore count should be 2.
|
|
*/
|
|
ut_assert(uthread_schedule());
|
|
ut_asserteq(2, count);
|
|
ut_assert(!uthread_grp_done(id1));
|
|
/* Four more calls should bring the count to 10 */
|
|
for (i = 0; i < 4; i++) {
|
|
ut_assert(!uthread_grp_done(id1));
|
|
ut_assert(uthread_schedule());
|
|
}
|
|
ut_asserteq(10, count);
|
|
/* This one allows the first worker to exit */
|
|
ut_assert(uthread_schedule());
|
|
/* At this point there should be no runnable thread in group 'id1' */
|
|
ut_assert(uthread_grp_done(id1));
|
|
/* Five more calls for the second worker to finish incrementing */
|
|
for (i = 0; i < 5; i++)
|
|
ut_assert(uthread_schedule());
|
|
ut_asserteq(15, count);
|
|
/* Plus one call to let the second worker return from its entry point */
|
|
ut_assert(uthread_schedule());
|
|
/* Now both tasks should be done, schedule should return false */
|
|
ut_assert(!uthread_schedule());
|
|
|
|
return 0;
|
|
}
|
|
LIB_TEST(uthread, 0);
|
|
|
|
struct mw_args {
|
|
struct unit_test_state *uts;
|
|
struct uthread_mutex *m;
|
|
int flag;
|
|
};
|
|
|
|
static int mutex_worker_ret;
|
|
|
|
static int _mutex_worker(struct mw_args *args)
|
|
{
|
|
struct unit_test_state *uts = args->uts;
|
|
|
|
ut_asserteq(-EBUSY, uthread_mutex_trylock(args->m));
|
|
ut_assertok(uthread_mutex_lock(args->m));
|
|
args->flag = 1;
|
|
ut_assertok(uthread_mutex_unlock(args->m));
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void mutex_worker(void *arg)
|
|
{
|
|
mutex_worker_ret = _mutex_worker((struct mw_args *)arg);
|
|
}
|
|
|
|
/*
|
|
* thread_mutex() - testing uthread mutex operations
|
|
*
|
|
*/
|
|
static int uthread_mutex(struct unit_test_state *uts)
|
|
{
|
|
struct uthread_mutex m = UTHREAD_MUTEX_INITIALIZER;
|
|
struct mw_args args = { .uts = uts, .m = &m, .flag = 0 };
|
|
int id;
|
|
int i;
|
|
|
|
id = uthread_grp_new_id();
|
|
ut_assert(id != 0);
|
|
/* Take the mutex */
|
|
ut_assertok(uthread_mutex_lock(&m));
|
|
/* Start a thread */
|
|
ut_assertok(uthread_create(NULL, mutex_worker, (void *)&args, 0,
|
|
id));
|
|
/* Let the thread run for a bit */
|
|
for (i = 0; i < 100; i++)
|
|
ut_assert(uthread_schedule());
|
|
/* Thread should not have set the flag due to the mutex */
|
|
ut_asserteq(0, args.flag);
|
|
/* Release the mutex */
|
|
ut_assertok(uthread_mutex_unlock(&m));
|
|
/* Schedule the thread until it is done */
|
|
while (uthread_schedule())
|
|
;
|
|
/* Now the flag should be set */
|
|
ut_asserteq(1, args.flag);
|
|
/* And the mutex should be available */
|
|
ut_assertok(uthread_mutex_trylock(&m));
|
|
ut_assertok(uthread_mutex_unlock(&m));
|
|
|
|
/* Of course no error are expected from the thread routine */
|
|
ut_assertok(mutex_worker_ret);
|
|
|
|
return 0;
|
|
}
|
|
LIB_TEST(uthread_mutex, 0);
|