diff options
Diffstat (limited to 'kc/core/task.c')
| -rw-r--r-- | kc/core/task.c | 439 |
1 files changed, 240 insertions, 199 deletions
diff --git a/kc/core/task.c b/kc/core/task.c index 3e0288d..a298a05 100644 --- a/kc/core/task.c +++ b/kc/core/task.c @@ -12,13 +12,9 @@ #include <stdbool.h> #include <lib/elf.h> -#include <lib/kstdio.h> +#include <libc/stdio.h> -static const size_t INTERRUPT_STACK_SIZE = 4096; -static const size_t THREAD_SIZE = 16834; -static const enum vm_alloc_flags ALLOC_FLAGS = VM_ALLOC_ANY|VM_ALLOC_ANONYMOUS; - -extern uint64_t *get_tss_rsp0(void); +#include "arch_thread.h" static void lock_scheduler(void); static void unlock_scheduler(void); @@ -27,7 +23,7 @@ static void unlock_preempt(void); static void update_time(void); -static struct kc_thread *create_thread(void (*thread_entry)(void)); +static struct kc_thread *create_thread(void *(*thread_f)(void *), void *p); static void destroy_thread(struct kc_thread *thread); static void set_thread(struct kc_thread *thread); static void block_thread(enum kc_thread_status reason); @@ -37,10 +33,6 @@ static void sleep_thread_until(uint64_t nanoseconds); static int sleeping_thread_callback(uint64_t nanoseconds); -static struct kc_thread *ready_thread_pop(void); -static void ready_thread_push(struct kc_thread *thread); -static void ready_thread_push_back(struct kc_thread *thread); - const struct timer_source * timesource; /* static threads that are always present @@ -50,130 +42,138 @@ static struct kc_thread *idle_thread; // thread lists for the scheduler to manipulate static struct kc_thread *current_thread; -static struct kc_thread *first_ready_thread; -static struct kc_thread *last_ready_thread; -static struct kc_thread *sleeping_threads; + +struct thread_queue +{ + struct kc_thread + *first, + *last; +}; + +static void thread_queue_push(struct thread_queue *queue, struct kc_thread *thread); +static void thread_queue_push_back(struct thread_queue *queue, struct kc_thread *thread); + +static void thread_queue_push(struct thread_queue *queue, struct kc_thread *thread) +{ + thread->next = queue->first; + queue->first = thread; + + if (!queue->last) + { + thread_queue_push_back(queue, thread); + } +} + +static void thread_queue_push_back(struct thread_queue *queue, struct kc_thread *thread) +{ + if (!queue->first) + { + thread_queue_push(queue, thread); + } + else if (queue->last) + { + queue->last->next = thread; + } + + queue->last = thread; +} + +struct kc_thread *thread_queue_pop(struct thread_queue *queue) +{ + if (!queue->first) + { + return nullptr; + } + + struct kc_thread *thread = queue->first; + queue->first = thread->next; + thread->next = nullptr; + if (!queue->first) + { + queue->last = nullptr; + } + + return thread; +} + +bool thread_queue_any(struct thread_queue queue) +{ + return queue.first && queue.last; +} + +static struct thread_queue ready_queue; +static struct thread_queue sleep_queue; +static struct thread_queue wait_queue; static volatile atomic_uint_fast64_t preempt_switch_count = 0; static volatile atomic_bool preempt_switch_flag = false; -static void idle_thread_entry(void) +static void *idle_thread_entry(void *) { - kprintf("idle thread started\n"); - unlock_scheduler(); - - __asm__ ("sti"); + printf("idle thread started\n"); + __asm__ ("sti"); + + while (true) { __asm__ ("hlt;"); } -} - -#define PS2INPUT 0x60 -#define PS2STATUS 0x64 -#define PS2STATUS_IBF 0x1 -static void keyboard_input_loop(void) -{ - bool has_bytes = true; - while (has_bytes) - { - has_bytes = inb(PS2STATUS) & PS2STATUS_IBF; - if (has_bytes) - { - kprintf("read byte from keyboard input: %hhd\n", inb(PS2INPUT)); - } - else - { - kprintf("no bytes in keyboard input\n"); - } - } + return nullptr; } -static void sleepy_thread_entry(void) +static void *sleepy_thread_entry(void *p) { + uint64_t sleep_nanoseconds = (uint64_t)p; int thread_slept_count = 0; while (true) { - sleep_thread(1000000000); + sleep_thread(sleep_nanoseconds); thread_slept_count++; - //keyboard_input_loop(); + printf("thread slept %d times\n", thread_slept_count); } (void)thread_slept_count; + return nullptr; } -static void create_interrupt_stack(size_t size) -{ - char *rsp0 = vm_alloc(size, ALLOC_FLAGS); - memset(rsp0, 0, size); - *get_tss_rsp0() = (uintptr_t)rsp0 + size; -} -noreturn void task_init(void) -{ - lock_scheduler(); - timesource = &pit8253_timer_source; - timesource->append_callback(sleeping_thread_callback); - timesource->start(); - kprintf( - "starting task management, timesource delta %luns\n", - timesource->nanoseconds_delta()); - // XXX: unfuck this mess at some point - // XXX: make this also not demand-allocated or it's gonna fail - create_interrupt_stack(INTERRUPT_STACK_SIZE); - - // initalize static threads - idle_thread = create_thread(idle_thread_entry); - struct kc_thread *sleepy_thread = create_thread(sleepy_thread_entry); - - current_thread = idle_thread; - ready_thread_push_back(sleepy_thread); +static uint64_t scheduler_flags; - idle_thread->status = RUNNING; - cpu_set_thread(&idle_thread->state, NULL, get_tss_rsp0()); +static inline bool check_preempt_count() +{ + if (atomic_load(&preempt_switch_count) > 0) + { + return true; + } - // shouldn't ever get here - PANIC(DEAD_END); + return false; } void task_schedule(void) { - if (atomic_load(&preempt_switch_count)) - { - preempt_switch_flag = true; - return; - } + if(check_preempt_count()) + { + preempt_switch_flag = true; + return; + } - if (first_ready_thread) - { - struct kc_thread *this_thread = ready_thread_pop(); + struct kc_thread *t = thread_queue_pop(&ready_queue); - if (this_thread == idle_thread) - { - if (first_ready_thread) - { - this_thread->status = READY; - this_thread = ready_thread_pop(); - ready_thread_push(idle_thread); - } - else if (current_thread->status == RUNNING) - { - return; - } - else - { - // NULL statement????? idon't like - } - } - set_thread(this_thread); - } -} + if (t == idle_thread) + { + thread_queue_push_back(&ready_queue, t); + t = thread_queue_pop(&ready_queue); + } -static uint64_t scheduler_flags; + if (t && current_thread->status != RUNNING) + { + set_thread(t); + } +} static void lock_scheduler(void) { @@ -193,7 +193,7 @@ static void unlock_scheduler(void) static void unlock_preempt(void) { - if (atomic_load(&preempt_switch_count) >= 1) + if (atomic_load(&preempt_switch_count) > 0) { preempt_switch_count--; } @@ -220,42 +220,21 @@ void update_time(void) } } -static struct kc_thread *create_thread(void (*thread_f)(void)) -{ - char *task_bottom = vm_alloc(16384, ALLOC_FLAGS); - struct kc_thread *thread = - (struct kc_thread *)(task_bottom + 16384 - sizeof(*thread)); - - thread->next = NULL; - - thread->time_elapsed = 0; - thread->sleep_expiration = 0; +#define KC_THREAD_SIZE 0x3000 - thread->state.stack = (uintptr_t)thread; - thread->state.stack_top = *get_tss_rsp0(); - thread->state.page_map = mmu_get_map(); - - // set up the expected stack values for the state - struct task_register_state - { - uint64_t rbp; - uint64_t r15; - uint64_t r14; - uint64_t r13; - uint64_t r12; - uint64_t rbx; - uint64_t rip; - } - *register_state = - (struct task_register_state *) - (thread->state.stack -= sizeof(*register_state)); +static struct kc_thread *create_thread(void *(*thread_f)(void *), void *p) +{ + void *buffer = vm_alloc(KC_THREAD_SIZE, VM_ALLOC_ANONYMOUS|VM_ALLOC_ANY); + memset(buffer, 0, KC_THREAD_SIZE); - memset(register_state, 0, sizeof(*register_state)); + uintptr_t stack_head = (uintptr_t)buffer + KC_THREAD_SIZE; + struct kc_thread *thread = (struct kc_thread *)(stack_head -= sizeof(*thread)); - register_state->rip = (uint64_t)thread_f; - register_state->rbp = thread->state.stack; + thread->kernel_stack_head = (void *)stack_head; + thread->kernel_stack_pointer = (void *)stack_head; + thread->status = READY; - thread->status = READY; + arch_create_thread(thread, thread_f, p); return thread; } @@ -267,11 +246,17 @@ static void destroy_thread(struct kc_thread *thread) static void set_thread(struct kc_thread *thread) { + if (thread == current_thread) + { + return; + } + if (atomic_load(&preempt_switch_count)) { preempt_switch_flag = true; return; } + update_time(); struct kc_thread *previous_thread = current_thread; current_thread = thread; @@ -279,15 +264,11 @@ static void set_thread(struct kc_thread *thread) if (previous_thread->status == RUNNING) { previous_thread->status = READY; - ready_thread_push(previous_thread); + thread_queue_push(&ready_queue, previous_thread); } - cpu_set_thread( - ¤t_thread->state, - &previous_thread->state, - get_tss_rsp0()); - current_thread->status = RUNNING; + arch_swap_thread(previous_thread, current_thread); } static void block_thread(enum kc_thread_status reason) @@ -304,14 +285,14 @@ static void unblock_thread(struct kc_thread *thread) thread->status = READY; - if (!first_ready_thread || (current_thread == idle_thread)) + if (!thread_queue_any(ready_queue) || (current_thread == idle_thread)) { unlock_preempt(); set_thread(thread); } else { - ready_thread_push_back(thread); + thread_queue_push_back(&ready_queue, thread); } unlock_scheduler(); @@ -332,89 +313,149 @@ static void sleep_thread_until(uint64_t nanoseconds) return; } -// kprintf("time elapsed is now %ld\r\n", timesource->nanoseconds_elapsed()); -// kprintf("thread will now sleep until %ld\r\n", nanoseconds); current_thread->sleep_expiration = nanoseconds; - current_thread->next = sleeping_threads; - sleeping_threads = current_thread; + + thread_queue_push_back(&sleep_queue, current_thread); unlock_preempt(); block_thread(SLEEPING); } -static struct kc_thread *ready_thread_pop(void) +static int sleeping_thread_callback(uint64_t timestamp) { - struct kc_thread *thread = first_ready_thread; + lock_preempt(); - if (thread) - { - first_ready_thread = thread->next; - } + struct thread_queue queue = sleep_queue; - if (!first_ready_thread) - { - last_ready_thread = NULL; - } + sleep_queue = (struct thread_queue){ nullptr, nullptr }; - return thread; + { + struct kc_thread *t; + while((t = thread_queue_pop(&queue)) != nullptr) + { + if (t->sleep_expiration <= timestamp) + { + t->sleep_expiration = 0; + unblock_thread(t); + } + else + { + thread_queue_push(&sleep_queue, t); + } + } + } + + unlock_preempt(); + lock_scheduler(); + task_schedule(); + unlock_scheduler(); + + return 0; } -static void ready_thread_push(struct kc_thread *thread) -{ - thread->next = first_ready_thread; - first_ready_thread = thread; +typedef void (locked_scheduler_action)(void *); - if (!last_ready_thread) - { - last_ready_thread = first_ready_thread; - } +static void with_locked_scheduler( + locked_scheduler_action *action, + void *action_parameter) +{ + lock_scheduler(); + action(action_parameter); + unlock_scheduler(); } -static void ready_thread_push_back(struct kc_thread *thread) +static void terminate_action(void *result) { - if (last_ready_thread) - { - last_ready_thread->next = thread; - } - else - { - first_ready_thread = thread; - } - last_ready_thread = thread; + union kc_thread_result *result_value = result; + current_thread->result = *result_value; + current_thread->kernel_stack_pointer = current_thread->kernel_stack_head; + + struct thread_queue queue = wait_queue; + + lock_preempt(); + + wait_queue = (struct thread_queue) { nullptr, nullptr }; + + { + struct kc_thread *t; + while((t = thread_queue_pop(&queue)) != nullptr) + { + if (t->wait_target == current_thread) + { + t->wait_target = nullptr; + t->status = READY; + thread_queue_push_back(&ready_queue, t); + } + else + { + thread_queue_push(&wait_queue, t); + } + } + } + + unlock_preempt(); + + block_thread(TERMINATED); } -static int sleeping_thread_callback(uint64_t nanoseconds) +void kct_terminate(union kc_thread_result result) { - lock_preempt(); + with_locked_scheduler(terminate_action, &result); +} - struct kc_thread *sleeping = sleeping_threads; - sleeping_threads = NULL; +union kc_thread_result kct_wait(struct kc_thread *target) +{ + current_thread->wait_target = target; + thread_queue_push_back(&wait_queue, current_thread); + block_thread(WAITING); + return target->result; +} - while (sleeping != NULL) - { - struct kc_thread *this_thread = sleeping; - sleeping = sleeping->next; +static void *waited_thread_test(void *) +{ + printf("worker thread, sleeping for some time...\n"); + sleep_thread(30000000000); + return (void *)1; +} - if (this_thread->sleep_expiration <= nanoseconds) - { - //kprintf("thread awakened: %p\n", this_thread); - this_thread->sleep_expiration = 0; - unblock_thread(this_thread); - } - else - { - this_thread->next = sleeping_threads; - sleeping_threads = this_thread; - } - } +static void *waiting_thread_test(void *) +{ + printf("waiting thread\n"); + struct kc_thread *t = create_thread(waited_thread_test, nullptr); + t->status = READY; + thread_queue_push_back(&ready_queue, t); + union kc_thread_result r = kct_wait(t); + printf("waited thread terminated result: %d\n", r); + return nullptr; +} - unlock_preempt(); +noreturn void task_init(void) +{ + struct kc_thread boot_thread; + boot_thread.status = RUNNING; + current_thread = &boot_thread; - lock_scheduler(); - task_schedule(); - unlock_scheduler(); + timesource = &pit8253_timer_source; + timesource->append_callback(sleeping_thread_callback); + timesource->start(); + printf( + "starting task management, timesource delta %luns\n", + timesource->nanoseconds_delta()); - return 0; + idle_thread = create_thread(idle_thread_entry, nullptr); + struct kc_thread *sleepy_thread = create_thread(sleepy_thread_entry, (void *)3000000000); + struct kc_thread *sleepy_thread2 = create_thread(sleepy_thread_entry, (void *)5000000000); + struct kc_thread *waiting_thread = create_thread(waiting_thread_test, nullptr); + thread_queue_push(&ready_queue, sleepy_thread); + thread_queue_push(&ready_queue, sleepy_thread2); + thread_queue_push_back(&ready_queue, waiting_thread); + __asm__ volatile ("movq %%rsp, %0" : "=g"(boot_thread.kernel_stack_pointer) :); + thread_queue_push(&ready_queue, idle_thread); + block_thread(BLOCKED); + // shouldn't ever get here + printf("the idle thread exited somehow. we're back in the boot thread. this is not good.\n"); + PANIC(DEAD_END); } + |
