1 // SPDX-License-Identifier: GPL-2.0+
2 /*
3  * Copyright 2025 Linaro Limited
4  *
5  * Unit test for uthread
6  */
7 
8 #include <stdbool.h>
9 #include <test/lib.h>
10 #include <test/ut.h>
11 #include <uthread.h>
12 
13 static int count;
14 
15 /* A thread entry point */
worker(void * arg)16 static void worker(void *arg)
17 {
18 	int loops = (int)(unsigned long)arg;
19 	int i;
20 
21 	for (i = 0; i < loops; i++) {
22 		count++;
23 		uthread_schedule();
24 	}
25 }
26 
27 /*
28  * uthread() - testing the uthread API
29  *
30  * This function creates two threads with the same entry point. The first one
31  * receives 5 as an argument, the second one receives 10. The number indicates
32  * the number of time the worker thread should loop on uthread_schedule()
33  * before returning. The workers increment a global counter each time they loop.
34  * As a result the main thread knows how many times it should call
35  * uthread_schedule() to let the two threads proceed, and it also knows which
36  * value the counter should have at any moment.
37  */
uthread(struct unit_test_state * uts)38 static int uthread(struct unit_test_state *uts)
39 {
40 	int i;
41 	int id1, id2;
42 
43 	count = 0;
44 	id1 = uthread_grp_new_id();
45 	ut_assert(id1 != 0);
46 	id2 = uthread_grp_new_id();
47 	ut_assert(id2 != 0);
48 	ut_assert(id1 != id2);
49 	ut_assertok(uthread_create(NULL, worker, (void *)5, 0, id1));
50 	ut_assertok(uthread_create(NULL, worker, (void *)10, 0, 0));
51 	/*
52 	 * The first call is expected to schedule the first worker, which will
53 	 * schedule the second one, which will schedule back to the main thread
54 	 * (here). Therefore count should be 2.
55 	 */
56 	ut_assert(uthread_schedule());
57 	ut_asserteq(2, count);
58 	ut_assert(!uthread_grp_done(id1));
59 	/* Four more calls should bring the count to 10 */
60 	for (i = 0; i < 4; i++) {
61 		ut_assert(!uthread_grp_done(id1));
62 		ut_assert(uthread_schedule());
63 	}
64 	ut_asserteq(10, count);
65 	/* This one allows the first worker to exit */
66 	ut_assert(uthread_schedule());
67 	/* At this point there should be no runnable thread in group 'id1' */
68 	ut_assert(uthread_grp_done(id1));
69 	/* Five more calls for the second worker to finish incrementing  */
70 	for (i = 0; i < 5; i++)
71 		ut_assert(uthread_schedule());
72 	ut_asserteq(15, count);
73 	/* Plus one call to let the second worker return from its entry point */
74 	ut_assert(uthread_schedule());
75 	/* Now both tasks should be done, schedule should return false */
76 	ut_assert(!uthread_schedule());
77 
78 	return 0;
79 }
80 LIB_TEST(uthread, 0);
81 
82 struct mw_args {
83 	struct unit_test_state *uts;
84 	struct uthread_mutex *m;
85 	int flag;
86 };
87 
88 static int mutex_worker_ret;
89 
_mutex_worker(struct mw_args * args)90 static int _mutex_worker(struct mw_args *args)
91 {
92 	struct unit_test_state *uts = args->uts;
93 
94 	ut_asserteq(-EBUSY, uthread_mutex_trylock(args->m));
95 	ut_assertok(uthread_mutex_lock(args->m));
96 	args->flag = 1;
97 	ut_assertok(uthread_mutex_unlock(args->m));
98 
99 	return 0;
100 }
101 
mutex_worker(void * arg)102 static void mutex_worker(void *arg)
103 {
104 	mutex_worker_ret = _mutex_worker((struct mw_args *)arg);
105 }
106 
107 /*
108  * thread_mutex() - testing uthread mutex operations
109  *
110  */
uthread_mutex(struct unit_test_state * uts)111 static int uthread_mutex(struct unit_test_state *uts)
112 {
113 	struct uthread_mutex m = UTHREAD_MUTEX_INITIALIZER;
114 	struct mw_args args = { .uts = uts, .m = &m, .flag = 0 };
115 	int id;
116 	int i;
117 
118 	id = uthread_grp_new_id();
119 	ut_assert(id != 0);
120 	/* Take the mutex */
121 	ut_assertok(uthread_mutex_lock(&m));
122 	/* Start a thread */
123 	ut_assertok(uthread_create(NULL, mutex_worker, (void *)&args, 0,
124 				   id));
125 	/* Let the thread run for a bit */
126 	for (i = 0; i < 100; i++)
127 		ut_assert(uthread_schedule());
128 	/* Thread should not have set the flag due to the mutex */
129 	ut_asserteq(0, args.flag);
130 	/* Release the mutex */
131 	ut_assertok(uthread_mutex_unlock(&m));
132 	/* Schedule the thread until it is done */
133 	while (uthread_schedule())
134 		;
135 	/* Now the flag should be set */
136 	ut_asserteq(1, args.flag);
137 	/* And the mutex should be available */
138 	ut_assertok(uthread_mutex_trylock(&m));
139 	ut_assertok(uthread_mutex_unlock(&m));
140 
141 	/* Of course no error are expected from the thread routine */
142 	ut_assertok(mutex_worker_ret);
143 
144 	return 0;
145 }
146 LIB_TEST(uthread_mutex, 0);
147