tests: thread_apis: add k_thread_join() tests
Exercise various scenarios from thread and ISR context. Signed-off-by: Andrew Boie <andrew.p.boie@intel.com>
This commit is contained in:
parent
322816eada
commit
dc7ac32a94
2 changed files with 178 additions and 4 deletions
|
@ -7,3 +7,4 @@ CONFIG_HEAP_MEM_POOL_SIZE=256
|
||||||
CONFIG_SCHED_CPU_MASK=y
|
CONFIG_SCHED_CPU_MASK=y
|
||||||
CONFIG_TEST_USERSPACE=y
|
CONFIG_TEST_USERSPACE=y
|
||||||
CONFIG_MP_NUM_CPUS=1
|
CONFIG_MP_NUM_CPUS=1
|
||||||
|
CONFIG_IRQ_OFFLOAD=y
|
||||||
|
|
|
@ -271,11 +271,181 @@ void test_user_mode(void)
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
struct k_thread join_thread;
|
||||||
|
K_THREAD_STACK_DEFINE(join_stack, STACK_SIZE);
|
||||||
|
|
||||||
|
struct k_thread control_thread;
|
||||||
|
K_THREAD_STACK_DEFINE(control_stack, STACK_SIZE);
|
||||||
|
|
||||||
|
enum control_method {
|
||||||
|
TIMEOUT,
|
||||||
|
NO_WAIT,
|
||||||
|
SELF_ABORT,
|
||||||
|
OTHER_ABORT,
|
||||||
|
ALREADY_EXIT,
|
||||||
|
ISR_ALREADY_EXIT,
|
||||||
|
ISR_RUNNING
|
||||||
|
};
|
||||||
|
|
||||||
|
void join_entry(void *p1, void *p2, void *p3)
|
||||||
|
{
|
||||||
|
enum control_method m = (enum control_method)p1;
|
||||||
|
|
||||||
|
switch (m) {
|
||||||
|
case TIMEOUT:
|
||||||
|
case NO_WAIT:
|
||||||
|
case OTHER_ABORT:
|
||||||
|
case ISR_RUNNING:
|
||||||
|
printk("join_thread: sleeping forever\n");
|
||||||
|
k_sleep(K_FOREVER);
|
||||||
|
break;
|
||||||
|
case SELF_ABORT:
|
||||||
|
case ALREADY_EXIT:
|
||||||
|
case ISR_ALREADY_EXIT:
|
||||||
|
printk("join_thread: self-exiting\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void control_entry(void *p1, void *p2, void *p3)
|
||||||
|
{
|
||||||
|
printk("control_thread: killing join thread\n");
|
||||||
|
k_thread_abort(&join_thread);
|
||||||
|
}
|
||||||
|
|
||||||
|
void do_join_from_isr(void *arg)
|
||||||
|
{
|
||||||
|
int *ret = arg;
|
||||||
|
|
||||||
|
printk("isr: joining join_thread\n");
|
||||||
|
*ret = k_thread_join(&join_thread, K_NO_WAIT);
|
||||||
|
printk("isr: k_thread_join() returned with %d\n", *ret);
|
||||||
|
}
|
||||||
|
|
||||||
|
int join_scenario(enum control_method m)
|
||||||
|
{
|
||||||
|
s32_t timeout = K_FOREVER;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
printk("ztest_thread: method %d, create join_thread\n", m);
|
||||||
|
k_thread_create(&join_thread, join_stack, STACK_SIZE, join_entry,
|
||||||
|
(void *)m, NULL, NULL, K_PRIO_PREEMPT(1),
|
||||||
|
K_USER | K_INHERIT_PERMS, K_NO_WAIT);
|
||||||
|
|
||||||
|
switch (m) {
|
||||||
|
case ALREADY_EXIT:
|
||||||
|
case ISR_ALREADY_EXIT:
|
||||||
|
/* Let join_thread run first */
|
||||||
|
k_sleep(50);
|
||||||
|
break;
|
||||||
|
case OTHER_ABORT:
|
||||||
|
printk("ztest_thread: create control_thread\n");
|
||||||
|
k_thread_create(&control_thread, control_stack, STACK_SIZE,
|
||||||
|
control_entry, NULL, NULL, NULL,
|
||||||
|
K_PRIO_PREEMPT(2),
|
||||||
|
K_USER | K_INHERIT_PERMS, K_NO_WAIT);
|
||||||
|
break;
|
||||||
|
case TIMEOUT:
|
||||||
|
timeout = 50;
|
||||||
|
break;
|
||||||
|
case NO_WAIT:
|
||||||
|
timeout = K_NO_WAIT;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m == ISR_ALREADY_EXIT || m == ISR_RUNNING) {
|
||||||
|
irq_offload(do_join_from_isr, &ret);
|
||||||
|
} else {
|
||||||
|
printk("ztest_thread: joining join_thread\n");
|
||||||
|
ret = k_thread_join(&join_thread, timeout);
|
||||||
|
printk("ztest_thread: k_thread_join() returned with %d\n", ret);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ret != 0) {
|
||||||
|
k_thread_abort(&join_thread);
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
void test_thread_join(void)
|
||||||
|
{
|
||||||
|
#ifdef CONFIG_USERSPACE
|
||||||
|
/* scenario: thread never started */
|
||||||
|
zassert_equal(k_thread_join(&join_thread, K_FOREVER), 0,
|
||||||
|
"failed case thread never started");
|
||||||
|
#endif
|
||||||
|
zassert_equal(join_scenario(TIMEOUT), -EAGAIN, "failed timeout case");
|
||||||
|
zassert_equal(join_scenario(NO_WAIT), -EBUSY, "failed no-wait case");
|
||||||
|
zassert_equal(join_scenario(SELF_ABORT), 0, "failed self-abort case");
|
||||||
|
zassert_equal(join_scenario(OTHER_ABORT), 0, "failed other-abort case");
|
||||||
|
zassert_equal(join_scenario(ALREADY_EXIT), 0,
|
||||||
|
"failed already exit case");
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void test_thread_join_isr(void)
|
||||||
|
{
|
||||||
|
zassert_equal(join_scenario(ISR_RUNNING), -EBUSY, "failed isr running");
|
||||||
|
zassert_equal(join_scenario(ISR_ALREADY_EXIT), 0, "failed isr exited");
|
||||||
|
}
|
||||||
|
|
||||||
|
struct k_thread deadlock1_thread;
|
||||||
|
K_THREAD_STACK_DEFINE(deadlock1_stack, STACK_SIZE);
|
||||||
|
|
||||||
|
struct k_thread deadlock2_thread;
|
||||||
|
K_THREAD_STACK_DEFINE(deadlock2_stack, STACK_SIZE);
|
||||||
|
|
||||||
|
void deadlock1_entry(void *p1, void *p2, void *p3)
|
||||||
|
{
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
k_sleep(500);
|
||||||
|
|
||||||
|
ret = k_thread_join(&deadlock2_thread, K_FOREVER);
|
||||||
|
zassert_equal(ret, -EDEADLK, "failed mutual join case");
|
||||||
|
}
|
||||||
|
|
||||||
|
void deadlock2_entry(void *p1, void *p2, void *p3)
|
||||||
|
{
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
/* deadlock1_thread is active but currently sleeping */
|
||||||
|
ret = k_thread_join(&deadlock1_thread, K_FOREVER);
|
||||||
|
|
||||||
|
zassert_equal(ret, 0, "couldn't join deadlock2_thread");
|
||||||
|
}
|
||||||
|
|
||||||
|
void test_thread_join_deadlock(void)
|
||||||
|
{
|
||||||
|
/* Deadlock scenarios */
|
||||||
|
zassert_equal(k_thread_join(k_current_get(), K_FOREVER), -EDEADLK,
|
||||||
|
"failed self-deadlock case");
|
||||||
|
|
||||||
|
k_thread_create(&deadlock1_thread, deadlock1_stack, STACK_SIZE,
|
||||||
|
deadlock1_entry, NULL, NULL, NULL,
|
||||||
|
K_PRIO_PREEMPT(1), K_USER | K_INHERIT_PERMS, K_NO_WAIT);
|
||||||
|
k_thread_create(&deadlock2_thread, deadlock2_stack, STACK_SIZE,
|
||||||
|
deadlock2_entry, NULL, NULL, NULL,
|
||||||
|
K_PRIO_PREEMPT(1), K_USER | K_INHERIT_PERMS, K_NO_WAIT);
|
||||||
|
|
||||||
|
zassert_equal(k_thread_join(&deadlock1_thread, K_FOREVER), 0,
|
||||||
|
"couldn't join deadlock1_thread");
|
||||||
|
zassert_equal(k_thread_join(&deadlock2_thread, K_FOREVER), 0,
|
||||||
|
"couldn't join deadlock2_thread");
|
||||||
|
}
|
||||||
|
|
||||||
void test_main(void)
|
void test_main(void)
|
||||||
{
|
{
|
||||||
k_thread_access_grant(k_current_get(), &tdata, tstack);
|
k_thread_access_grant(k_current_get(), &tdata, tstack,
|
||||||
k_thread_access_grant(k_current_get(), &tdata_custom, tstack_custom);
|
&tdata_custom, tstack_custom,
|
||||||
k_thread_access_grant(k_current_get(), &tdata_name, tstack_name);
|
&tdata_name, tstack_name,
|
||||||
|
&join_thread, join_stack,
|
||||||
|
&control_thread, control_stack,
|
||||||
|
&deadlock1_thread, deadlock1_stack,
|
||||||
|
&deadlock2_thread, deadlock2_stack);
|
||||||
main_prio = k_thread_priority_get(k_current_get());
|
main_prio = k_thread_priority_get(k_current_get());
|
||||||
#ifdef CONFIG_USERSPACE
|
#ifdef CONFIG_USERSPACE
|
||||||
strncpy(unreadable_string, "unreadable string",
|
strncpy(unreadable_string, "unreadable string",
|
||||||
|
@ -307,7 +477,10 @@ void test_main(void)
|
||||||
ztest_unit_test(test_user_mode),
|
ztest_unit_test(test_user_mode),
|
||||||
ztest_1cpu_unit_test(test_threads_cpu_mask),
|
ztest_1cpu_unit_test(test_threads_cpu_mask),
|
||||||
ztest_unit_test(test_threads_suspend_timeout),
|
ztest_unit_test(test_threads_suspend_timeout),
|
||||||
ztest_unit_test(test_threads_suspend)
|
ztest_unit_test(test_threads_suspend),
|
||||||
|
ztest_user_unit_test(test_thread_join),
|
||||||
|
ztest_unit_test(test_thread_join_isr),
|
||||||
|
ztest_user_unit_test(test_thread_join_deadlock)
|
||||||
);
|
);
|
||||||
|
|
||||||
ztest_run_test_suite(threads_lifecycle);
|
ztest_run_test_suite(threads_lifecycle);
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue