diff --git a/tests/kernel/threads_scheduling/schedule_api/Makefile b/tests/kernel/threads_scheduling/schedule_api/Makefile new file mode 100644 index 00000000000..4de50f93d4d --- /dev/null +++ b/tests/kernel/threads_scheduling/schedule_api/Makefile @@ -0,0 +1,4 @@ +BOARD ?= qemu_x86 +CONF_FILE = prj.conf + +include ${ZEPHYR_BASE}/Makefile.inc diff --git a/tests/kernel/threads_scheduling/schedule_api/prj.conf b/tests/kernel/threads_scheduling/schedule_api/prj.conf new file mode 100644 index 00000000000..9467c292689 --- /dev/null +++ b/tests/kernel/threads_scheduling/schedule_api/prj.conf @@ -0,0 +1 @@ +CONFIG_ZTEST=y diff --git a/tests/kernel/threads_scheduling/schedule_api/src/Makefile b/tests/kernel/threads_scheduling/schedule_api/src/Makefile new file mode 100644 index 00000000000..e5c8615302a --- /dev/null +++ b/tests/kernel/threads_scheduling/schedule_api/src/Makefile @@ -0,0 +1,3 @@ +include $(ZEPHYR_BASE)/tests/Makefile.test + +obj-y = main.o test_sched_priority.o test_sched_timeslice_and_lock.o diff --git a/tests/kernel/threads_scheduling/schedule_api/src/main.c b/tests/kernel/threads_scheduling/schedule_api/src/main.c new file mode 100644 index 00000000000..f42770cd277 --- /dev/null +++ b/tests/kernel/threads_scheduling/schedule_api/src/main.c @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2016 Intel Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @addtogroup t_kernel_threads + * @{ + * @defgroup t_threads_scheduling test_threads_scheduling + * @brief TestPurpose: verify threads scheduling based on priority, time + * slice and lock/unlock + * @} + */ + +#include "test_sched.h" + +/*test case main entry*/ +void test_main(void *p1, void *p2, void *p3) +{ + ztest_test_suite(test_threads_scheduling, + ztest_unit_test(test_priority_cooperative), + ztest_unit_test(test_priority_preemptible), + ztest_unit_test(test_yield_cooperative), + ztest_unit_test(test_sleep_cooperative), + ztest_unit_test(test_sleep_wakeup_preemptible), + ztest_unit_test(test_time_slicing_preemptible), + ztest_unit_test(test_time_slicing_disable_preemptible), + ztest_unit_test(test_lock_preemptible), + ztest_unit_test(test_unlock_preemptible) + ); + ztest_run_test_suite(test_threads_scheduling); +} diff --git a/tests/kernel/threads_scheduling/schedule_api/src/test_sched.h b/tests/kernel/threads_scheduling/schedule_api/src/test_sched.h new file mode 100644 index 00000000000..f9f7480f196 --- /dev/null +++ b/tests/kernel/threads_scheduling/schedule_api/src/test_sched.h @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2016 Intel Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __TEST_SCHED_H__ +#define __TEST_SCHED_H__ + +#include +#include + +#define STACK_SIZE 256 + +struct thread_data { + k_tid_t tid; + int priority; + int executed; +}; + +void test_priority_cooperative(void); +void test_priority_preemptible(void); +void test_yield_cooperative(void); +void test_sleep_cooperative(void); +void test_sleep_wakeup_preemptible(void); +void test_time_slicing_preemptible(void); +void test_time_slicing_disable_preemptible(void); +void test_lock_preemptible(void); +void test_unlock_preemptible(void); + +#endif /* __TEST_SCHED_H__ */ diff --git a/tests/kernel/threads_scheduling/schedule_api/src/test_sched_priority.c b/tests/kernel/threads_scheduling/schedule_api/src/test_sched_priority.c new file mode 100644 index 00000000000..0388c7f1c6a --- /dev/null +++ b/tests/kernel/threads_scheduling/schedule_api/src/test_sched_priority.c @@ -0,0 +1,90 @@ +/* + * Copyright (c) 2016 Intel Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @addtogroup t_threads_scheduling + * @{ + * @defgroup t_threads_priority test_threads_priority + * @brief TestPurpose: verify threads scheduling priority + * @} + */ + +#include "test_sched.h" + +static char __noinit __stack tstack[STACK_SIZE]; +static int last_prio; + +static void thread_entry(void *p1, void *p2, void *p3) +{ + last_prio = k_thread_priority_get(k_current_get()); +} + +/*test cases*/ +void test_priority_cooperative(void) +{ + int old_prio = k_thread_priority_get(k_current_get()); + + /* set current thread to a negative priority */ + last_prio = -1; + k_thread_priority_set(k_current_get(), last_prio); + + /* spawn thread with higher priority */ + int spawn_prio = last_prio - 1; + + k_tid_t tid = k_thread_spawn(tstack, STACK_SIZE, + thread_entry, NULL, NULL, NULL, + spawn_prio, 0, 0); + /* checkpoint: current thread shouldn't preempted by higher thread */ + assert_true(last_prio == k_thread_priority_get(k_current_get()), NULL); + k_sleep(100); + /* checkpoint: spawned thread get executed */ + assert_true(last_prio == spawn_prio, NULL); + k_thread_abort(tid); + + /* restore environment */ + k_thread_priority_set(k_current_get(), old_prio); +} + +void test_priority_preemptible(void) +{ + int old_prio = k_thread_priority_get(k_current_get()); + + /* set current thread to a non-negative priority */ + last_prio = 2; + k_thread_priority_set(k_current_get(), last_prio); + + int spawn_prio = last_prio - 1; + + k_tid_t tid = k_thread_spawn(tstack, STACK_SIZE, + thread_entry, NULL, NULL, NULL, + spawn_prio, 0, 0); + /* checkpoint: thread is preempted by higher thread */ + assert_true(last_prio == spawn_prio, NULL); + + k_sleep(100); + k_thread_abort(tid); + + spawn_prio = last_prio + 1; + tid = k_thread_spawn(tstack, STACK_SIZE, + thread_entry, NULL, NULL, NULL, + spawn_prio, 0, 0); + /* checkpoint: thread is not preempted by lower thread */ + assert_false(last_prio == spawn_prio, NULL); + k_thread_abort(tid); + + /* restore environment */ + k_thread_priority_set(k_current_get(), old_prio); +} diff --git a/tests/kernel/threads_scheduling/schedule_api/src/test_sched_timeslice_and_lock.c b/tests/kernel/threads_scheduling/schedule_api/src/test_sched_timeslice_and_lock.c new file mode 100644 index 00000000000..ec1f8e20c81 --- /dev/null +++ b/tests/kernel/threads_scheduling/schedule_api/src/test_sched_timeslice_and_lock.c @@ -0,0 +1,235 @@ +/* + * Copyright (c) 2016 Intel Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @addtogroup t_threads_scheduling + * @{ + * @defgroup t_sched_timeslice_and_lock test_sched_timeslice_and_lock + * @brief TestPurpose: verify sched time slice and lock/unlock + * @} + */ + +#include "test_sched.h" +#define THREADS_NUM 3 +static char __noinit __stack tstack[THREADS_NUM][STACK_SIZE]; + +static struct thread_data tdata[THREADS_NUM]; +static int old_prio, init_prio; + +static void thread_entry(void *p1, void *p2, void *p3) +{ + int sleep_ms = (int)p2; + + if (sleep_ms > 0) { + k_sleep(sleep_ms); + } + + int tnum = (int)p1; + + tdata[tnum].executed = 1; +} + +static void setup_threads(void) +{ + old_prio = k_thread_priority_get(k_current_get()); + for (int i = 0; i < THREADS_NUM; i++) { + if (i == 0) { + /* spawn thread with higher priority */ + tdata[i].priority = init_prio - 1; + } else if (i == 1) { + /* spawn thread with same priority */ + tdata[i].priority = init_prio; + } else { + /* spawn thread with lower priority */ + tdata[i].priority = init_prio + 1; + } + tdata[i].executed = 0; + } + k_thread_priority_set(k_current_get(), init_prio); +} + +static void spawn_threads(int sleep_sec) +{ + for (int i = 0; i < THREADS_NUM; i++) { + tdata[i].tid = k_thread_spawn(tstack[i], STACK_SIZE, + thread_entry, (void *)i, (void *)sleep_sec, NULL, + tdata[i].priority, 0, 0); + } +} + +static void teardown_threads(void) +{ + for (int i = 0; i < THREADS_NUM; i++) { + k_thread_abort(tdata[i].tid); + } + k_thread_priority_set(k_current_get(), old_prio); +} + +/*test cases*/ +void test_yield_cooperative(void) +{ + + /* set current thread to a cooperative priority */ + init_prio = -1; + setup_threads(); + + spawn_threads(0); + /* checkpoint: only higher priority thread get executed when yield */ + k_yield(); + assert_true(tdata[0].executed == 1, NULL); + assert_true(tdata[1].executed == 1, NULL); + for (int i = 2; i < THREADS_NUM; i++) { + assert_true(tdata[i].executed == 0, NULL); + } + /* restore environment */ + teardown_threads(); +} + +void test_sleep_cooperative(void) +{ + /* set current thread to a cooperative priority */ + init_prio = -1; + setup_threads(); + + spawn_threads(0); + /* checkpoint: all ready threads get executed when k_sleep */ + k_sleep(100); + for (int i = 0; i < THREADS_NUM; i++) { + assert_true(tdata[i].executed == 1, NULL); + } + + /* restore environment */ + teardown_threads(); +} + +void test_busy_wait_cooperative(void) +{ + /* set current thread to a preemptible priority */ + init_prio = -1; + setup_threads(); + + spawn_threads(0); + k_busy_wait(100000); /* 100 ms */ + /* checkpoint: No other threads get executed */ + for (int i = 0; i < THREADS_NUM; i++) { + assert_true(tdata[i].executed == 0, NULL); + } + /* restore environment */ + teardown_threads(); +} + +void test_sleep_wakeup_preemptible(void) +{ + /* set current thread to a preemptible priority */ + init_prio = 0; + setup_threads(); + + spawn_threads(10 * 1000); /* 10 second */ + /* checkpoint: lower threads not executed, high threads are in sleep */ + for (int i = 0; i < THREADS_NUM; i++) { + assert_true(tdata[i].executed == 0, NULL); + } + k_wakeup(tdata[0].tid); + assert_true(tdata[0].executed == 1, NULL); + /* restore environment */ + teardown_threads(); +} + +void test_time_slicing_preemptible(void) +{ + /* set current thread to a preemptible priority */ + init_prio = 0; + setup_threads(); + + k_sched_time_slice_set(200, 0); /* 200 ms */ + spawn_threads(0); + /* checkpoint: higher priority threads get executed immediately */ + assert_true(tdata[0].executed == 1, NULL); + k_busy_wait(500000); /* 500 ms */ + /* checkpoint: equal priority threads get executed every time slice */ + assert_true(tdata[1].executed == 1, NULL); + for (int i = 2; i < THREADS_NUM; i++) { + assert_true(tdata[i].executed == 0, NULL); + } + + /* restore environment */ + k_sched_time_slice_set(0, 0); /* disable time slice */ + teardown_threads(); +} + +void test_time_slicing_disable_preemptible(void) +{ + /* set current thread to a preemptible priority */ + init_prio = 0; + setup_threads(); + + spawn_threads(0); + /* checkpoint: higher priority threads get executed immediately */ + assert_true(tdata[0].executed == 1, NULL); + k_busy_wait(500000); /* 500 ms */ + /* checkpoint: equal priority threads get executed every time slice */ + assert_true(tdata[1].executed == 0, NULL); + for (int i = 2; i < THREADS_NUM; i++) { + assert_true(tdata[i].executed == 0, NULL); + } + /* restore environment */ + teardown_threads(); +} + +void test_lock_preemptible(void) +{ + /* set current thread to a preemptible priority */ + init_prio = 0; + setup_threads(); + + k_sched_lock(); + spawn_threads(0); + /* do critical thing */ + k_busy_wait(100000); + /* checkpoint: all other threads not been executed */ + for (int i = 0; i < THREADS_NUM; i++) { + assert_true(tdata[i].executed == 0, NULL); + } + /* make current thread unready */ + k_sleep(100); + /* checkpoint: all other threads get executed */ + for (int i = 0; i < THREADS_NUM; i++) { + assert_true(tdata[i].executed == 1, NULL); + } + /* restore environment */ + teardown_threads(); +} + +void test_unlock_preemptible(void) +{ + /* set current thread to a preemptible priority */ + init_prio = 0; + setup_threads(); + + k_sched_lock(); + spawn_threads(0); + /* do critical thing */ + k_busy_wait(100000); + + k_sched_unlock(); + /* checkpoint: higher threads get executed */ + assert_true(tdata[0].executed == 1, NULL); + for (int i = 1; i < THREADS_NUM; i++) { + assert_true(tdata[i].executed == 0, NULL); + } + /* restore environment */ + teardown_threads(); +} diff --git a/tests/kernel/threads_scheduling/schedule_api/testcase.ini b/tests/kernel/threads_scheduling/schedule_api/testcase.ini new file mode 100644 index 00000000000..effdb40b936 --- /dev/null +++ b/tests/kernel/threads_scheduling/schedule_api/testcase.ini @@ -0,0 +1,4 @@ +[test] +tags = kernel +# tickless is not supported on nios2 +arch_exclude = nios2