From 503f176e001ddf15e6e32bc912cf10b5764bc23b Mon Sep 17 00:00:00 2001 From: Alex Auvolat Date: Wed, 4 Mar 2015 11:51:30 +0100 Subject: Process exiting & thread termination. IMPORTANT NOTE FOLLOWS. Threads may now communicate via wait_on(void* ressource) and resume_on (void* ressource). Previous pause() is replaced by wait_on(current_thread) and resume(thread) by resume_on(thread). wait_on(x) may return false, indicating that the reason for returning is NOT that resume_on(x) was called but something else happenned. Typically false indicates that the curent thread is being killed and must terminate its kernel-land processing as soon as possible. --- src/kernel/core/idt.c | 15 +++++++ src/kernel/core/thread.c | 110 ++++++++++++++++++++++++++++++++++++++--------- src/kernel/core/worker.c | 9 ++-- 3 files changed, 111 insertions(+), 23 deletions(-) (limited to 'src/kernel/core') diff --git a/src/kernel/core/idt.c b/src/kernel/core/idt.c index 5c77502..fcf8074 100644 --- a/src/kernel/core/idt.c +++ b/src/kernel/core/idt.c @@ -103,6 +103,11 @@ void idt_ex_handler(registers_t *regs) { current_thread->user_ex_handler(regs); } } + + // maybe exit + if (current_thread != 0 && regs->eip < K_HIGHHALF_ADDR && current_thread->must_exit) { + exit(); + } } /* Called in interrupt.s when an IRQ fires (interrupt 32 to 47) */ @@ -120,11 +125,21 @@ void idt_irq_handler(registers_t *regs) { } exit_critical(st); + + // maybe exit + if (current_thread != 0 && regs->eip < K_HIGHHALF_ADDR && current_thread->must_exit) { + exit(); + } } /* Caled in interrupt.s when a syscall is called */ void idt_syscall_handler(registers_t *regs) { syscall_handler(regs); + + // maybe exit + if (current_thread != 0 && regs->eip < K_HIGHHALF_ADDR && current_thread->must_exit) { + exit(); + } } /* For internal use only. Sets up an entry of the IDT with given parameters. */ diff --git a/src/kernel/core/thread.c b/src/kernel/core/thread.c index 63523af..144a905 100644 --- a/src/kernel/core/thread.c +++ b/src/kernel/core/thread.c @@ -4,6 +4,8 @@ #include #include +#include + #include #include #include @@ -15,6 +17,9 @@ void resume_context(saved_context_t *ctx); thread_t *current_thread = 0; +static hashtbl_t *waiters = 0; // threads waiting on a ressource +STATIC_MUTEX(waiters_mutex); + // ====================== // // THE PROGRAMMABLE TIMER // // ====================== // @@ -180,9 +185,12 @@ thread_t *new_thread(entry_t entry, void* data) { *(--t->ctx.esp) = 0; // push invalid return address (the run_thread function never returns) t->ctx.eip = (void(*)())run_thread; - t->state = T_STATE_PAUSED; + t->state = T_STATE_LOADING; t->last_ran = 0; + t->waiting_on = 0; + t->must_exit = false; + t->current_pd_d = get_kernel_pagedir(); t->critical_level = CL_USER; @@ -195,6 +203,8 @@ thread_t *new_thread(entry_t entry, void* data) { } static void delete_thread(thread_t *t) { + dbg_printf("Deleting thread 0x%p\n", t); + if (t->proc != 0) process_thread_deleted(t); @@ -213,13 +223,17 @@ static void irq0_handler(registers_t *regs) { } } void threading_setup(entry_t cont, void* arg) { + waiters = create_hashtbl(id_key_eq_fun, id_hash_fun, 0); + ASSERT(waiters != 0); + set_pit_frequency(TASK_SWITCH_FREQUENCY); idt_set_irq_handler(IRQ0, irq0_handler); thread_t *t = new_thread(cont, arg); ASSERT(t != 0); - resume_thread(t); + start_thread(t); + exit_critical(CL_USER); run_scheduler(); // never returns @@ -230,27 +244,65 @@ void threading_setup(entry_t cont, void* arg) { // TASK STATE MANIPULATION // // ======================= // +void start_thread(thread_t *t) { + ASSERT(t->state == T_STATE_LOADING); + + t->state = T_STATE_RUNNING; + + { int st = enter_critical(CL_NOINT); + + enqueue_thread(t, false); + + exit_critical(st); } +} + void yield() { ASSERT(current_thread != 0 && current_thread->critical_level != CL_EXCL); save_context_and_enter_scheduler(¤t_thread->ctx); } -void pause() { +bool wait_on(void* x) { ASSERT(current_thread != 0 && current_thread->critical_level != CL_EXCL); + mutex_lock(&waiters_mutex); + + void* prev_th = hashtbl_find(waiters, x); + if (prev_th == 0) { + bool add_ok = hashtbl_add(waiters, x, (void*)1); + if (!add_ok) return false; // should not happen to often, I hope + } else if (prev_th != (void*)1) { + mutex_unlock(&waiters_mutex); + return false; + } + + int st = enter_critical(CL_NOSWITCH); + + if (current_thread->must_exit) return false; + + current_thread->waiting_on = x; + + ASSERT(hashtbl_change(waiters, x, current_thread)); + mutex_unlock(&waiters_mutex); + current_thread->state = T_STATE_PAUSED; save_context_and_enter_scheduler(¤t_thread->ctx); + + exit_critical(st); + + if (current_thread->must_exit) return false; + return true; } void usleep(int usecs) { - void sleeper_resume(void* t) { - thread_t *thread = (thread_t*)t; - resume_thread(thread); - } if (current_thread == 0) return; - bool ok = worker_push_in(usecs, sleeper_resume, current_thread); - if (ok) pause(); + + void resume_on_v(void* x) { + resume_on(x); + } + bool ok = worker_push_in(usecs, resume_on_v, current_thread); + + if (ok) wait_on(current_thread); } void exit() { @@ -263,6 +315,8 @@ void exit() { // (it may switch before adding the delete_thread task), but once the task is added // no other switch may happen, therefore this thread will not get re-enqueued + dbg_printf("Thread 0x%p exiting.\n", current_thread); + worker_push(delete_thread_v, current_thread); current_thread->state = T_STATE_FINISHED; @@ -272,20 +326,29 @@ void exit() { ASSERT(false); } -bool resume_thread(thread_t *thread) { - bool ret = false; +bool resume_on(void* x) { + thread_t *thread; + + { mutex_lock(&waiters_mutex); - int st = enter_critical(CL_NOINT); + thread = hashtbl_find(waiters, x); + hashtbl_change(waiters, x, (void*)1); - if (thread->state == T_STATE_PAUSED) { - ret = true; + mutex_unlock(&waiters_mutex); } + + if (thread == 0 || thread == (void*)1) return false; + + { int st = enter_critical(CL_NOINT); + + ASSERT(thread->state == T_STATE_PAUSED); thread->state = T_STATE_RUNNING; + thread->waiting_on = 0; + enqueue_thread(thread, false); - } - exit_critical(st); + exit_critical(st); } - return ret; + return true; } void kill_thread(thread_t *thread) { @@ -293,9 +356,16 @@ void kill_thread(thread_t *thread) { int st = enter_critical(CL_NOSWITCH); - thread->state = T_STATE_FINISHED; - remove_thread_from_queue(thread); - delete_thread(thread); + thread->must_exit = true; + + int i = 0; + while (thread->state != T_STATE_FINISHED) { + if (thread->state == T_STATE_PAUSED) { + resume_on(thread->waiting_on); + } + yield(); + if (i++ > 100) dbg_printf("Thread 0x%p must be killed but will not exit.\n", thread); + } exit_critical(st); } diff --git a/src/kernel/core/worker.c b/src/kernel/core/worker.c index e50085a..c3fa04d 100644 --- a/src/kernel/core/worker.c +++ b/src/kernel/core/worker.c @@ -36,7 +36,10 @@ void start_workers(int n) { nworkers = n; for (int i = 0; i < n; i++) { workers[i] = new_thread(worker_thread, 0); - dbg_printf("New worker thread: 0x%p\n", workers[i]); + if (workers[i] != 0) { + dbg_printf("New worker thread: 0x%p\n", workers[i]); + start_thread(workers[i]); + } } } @@ -59,7 +62,7 @@ void worker_thread(void* x) { t->fun(t->data); free(t); } else { - pause(); + ASSERT(wait_on(current_thread)); } } } @@ -89,7 +92,7 @@ void worker_notify_time(int usecs) { time += usecs; if (next_task_time <= time) { for (int i = 0; i < nworkers; i++) { - if (resume_thread(workers[i])) break; + if (resume_on(workers[i])) break; } } } -- cgit v1.2.3