189 lines
4.6 KiB
C
189 lines
4.6 KiB
C
#include <stdint.h>
|
|
|
|
#include <tianole/early_log.h>
|
|
#include <tianole/errno.h>
|
|
#include <tianole/sched.h>
|
|
#include <tianole/spinlock.h>
|
|
|
|
#include "sched/sched.h"
|
|
|
|
#define STACK_ALIGNMENT 16u
|
|
|
|
static void thread_selftest_entry(void *arg)
|
|
{
|
|
(void)arg;
|
|
}
|
|
|
|
void sched_selftest(void)
|
|
{
|
|
struct spinlock test_lock;
|
|
struct thread *first =
|
|
kernel_thread_create("worker-a", thread_selftest_entry, 0);
|
|
struct thread *second =
|
|
kernel_thread_create("worker-b", thread_selftest_entry, 0);
|
|
uint64_t flags;
|
|
|
|
test_lock.locked = 0;
|
|
|
|
if (first == 0 || second == 0 || first == second) {
|
|
panic("kernel thread selftest allocation failed");
|
|
}
|
|
|
|
if (first->id == second->id || first->state != THREAD_READY ||
|
|
second->state != THREAD_READY) {
|
|
panic("kernel thread selftest state failed");
|
|
}
|
|
|
|
if ((first->stack_top & (STACK_ALIGNMENT - 1)) != 0 ||
|
|
(second->stack_top & (STACK_ALIGNMENT - 1)) != 0) {
|
|
panic("kernel thread selftest stack alignment failed");
|
|
}
|
|
|
|
if (run_queue_head != first || first->next != second ||
|
|
run_queue_tail != second) {
|
|
panic("kernel thread selftest run queue failed");
|
|
}
|
|
|
|
spin_lock_irqsave(&test_lock, &flags);
|
|
spin_unlock_irqrestore(&test_lock, flags);
|
|
|
|
early_log_puts("kernel thread selftest ok\n");
|
|
}
|
|
|
|
static void scheduler_demo_entry(void *arg)
|
|
{
|
|
uint64_t id = (uint64_t)(uintptr_t)arg;
|
|
uint64_t step;
|
|
|
|
for (step = 1; step <= 3; step++) {
|
|
early_log_puts("preempt thread ");
|
|
early_log_u64_decimal(id);
|
|
early_log_puts(" step=");
|
|
early_log_u64_decimal(step);
|
|
early_log_puts("\n");
|
|
sched_sleep(2);
|
|
}
|
|
}
|
|
|
|
static struct wait_queue demo_wait_queue;
|
|
static struct wait_queue condition_wait_queue;
|
|
static struct wait_queue timeout_wait_queue;
|
|
static int condition_ready;
|
|
|
|
static int condition_is_ready(void *arg)
|
|
{
|
|
int *ready = arg;
|
|
|
|
return *ready != 0;
|
|
}
|
|
|
|
static void wait_queue_demo_waiter(void *arg)
|
|
{
|
|
(void)arg;
|
|
|
|
early_log_puts("waiter sleeping\n");
|
|
wait_queue_sleep(&demo_wait_queue);
|
|
early_log_puts("waiter woke\n");
|
|
}
|
|
|
|
static void wait_queue_demo_waker(void *arg)
|
|
{
|
|
(void)arg;
|
|
|
|
early_log_puts("waker sleeping\n");
|
|
sched_sleep(4);
|
|
early_log_puts("waker wake_one\n");
|
|
wait_queue_wake_one(&demo_wait_queue);
|
|
}
|
|
|
|
static void condition_wait_demo_waiter(void *arg)
|
|
{
|
|
(void)arg;
|
|
|
|
early_log_puts("condition waiter sleeping\n");
|
|
if (wait_queue_wait(&condition_wait_queue,
|
|
condition_is_ready,
|
|
&condition_ready) != 0) {
|
|
panic("condition wait failed");
|
|
}
|
|
early_log_puts("condition waiter woke\n");
|
|
}
|
|
|
|
static void condition_wait_demo_waker(void *arg)
|
|
{
|
|
(void)arg;
|
|
|
|
early_log_puts("condition waker sleeping\n");
|
|
sched_sleep(5);
|
|
condition_ready = 1;
|
|
early_log_puts("condition waker wake_all\n");
|
|
wait_queue_wake_all(&condition_wait_queue);
|
|
}
|
|
|
|
static void timeout_wait_demo_waiter(void *arg)
|
|
{
|
|
int never_ready = 0;
|
|
|
|
(void)arg;
|
|
|
|
early_log_puts("timeout waiter sleeping\n");
|
|
if (wait_queue_wait_timeout(
|
|
&timeout_wait_queue, condition_is_ready, &never_ready, 3) !=
|
|
-ETIMEDOUT) {
|
|
panic("timeout wait did not time out");
|
|
}
|
|
early_log_puts("timeout waiter timed out\n");
|
|
}
|
|
|
|
static void return_exit_demo_thread(void *arg)
|
|
{
|
|
(void)arg;
|
|
|
|
early_log_puts("return exit thread returning\n");
|
|
}
|
|
|
|
static void explicit_exit_demo_thread(void *arg)
|
|
{
|
|
(void)arg;
|
|
|
|
early_log_puts("explicit exit thread exiting\n");
|
|
kernel_thread_exit();
|
|
}
|
|
|
|
void sched_demo_start(void)
|
|
{
|
|
struct thread *first = kernel_thread_create(
|
|
"round-robin-a", scheduler_demo_entry, (void *)(uintptr_t)1);
|
|
struct thread *second = kernel_thread_create(
|
|
"round-robin-b", scheduler_demo_entry, (void *)(uintptr_t)2);
|
|
struct thread *waiter =
|
|
kernel_thread_create("waiter", wait_queue_demo_waiter, 0);
|
|
struct thread *waker =
|
|
kernel_thread_create("waker", wait_queue_demo_waker, 0);
|
|
struct thread *condition_waiter = kernel_thread_create(
|
|
"condition-waiter", condition_wait_demo_waiter, 0);
|
|
struct thread *condition_waker = kernel_thread_create(
|
|
"condition-waker", condition_wait_demo_waker, 0);
|
|
struct thread *timeout_waiter = kernel_thread_create(
|
|
"timeout-waiter", timeout_wait_demo_waiter, 0);
|
|
struct thread *return_exit =
|
|
kernel_thread_create("return-exit", return_exit_demo_thread, 0);
|
|
struct thread *explicit_exit = kernel_thread_create(
|
|
"explicit-exit", explicit_exit_demo_thread, 0);
|
|
|
|
if (first == 0 || second == 0 || waiter == 0 || waker == 0 ||
|
|
condition_waiter == 0 || condition_waker == 0 ||
|
|
timeout_waiter == 0 || return_exit == 0 || explicit_exit == 0) {
|
|
panic("scheduler demo thread creation failed");
|
|
}
|
|
|
|
wait_queue_init(&demo_wait_queue);
|
|
wait_queue_init(&condition_wait_queue);
|
|
wait_queue_init(&timeout_wait_queue);
|
|
condition_ready = 0;
|
|
early_log_puts("scheduler starting\n");
|
|
sched_yield();
|
|
|
|
panic("scheduler returned to boot context");
|
|
}
|