aboutsummaryrefslogtreecommitdiff
path: root/src/kernel/core
diff options
context:
space:
mode:
authorAlex Auvolat <alex.auvolat@ens.fr>2015-03-04 11:51:30 +0100
committerAlex Auvolat <alex.auvolat@ens.fr>2015-03-04 11:51:30 +0100
commit503f176e001ddf15e6e32bc912cf10b5764bc23b (patch)
tree2595bc45e582df3ede9241c00656b65866301e38 /src/kernel/core
parent0934f9943ef7bdec8c2eee3ea31b5e0f1e8a3faf (diff)
downloadkogata-503f176e001ddf15e6e32bc912cf10b5764bc23b.tar.gz
kogata-503f176e001ddf15e6e32bc912cf10b5764bc23b.zip
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.
Diffstat (limited to 'src/kernel/core')
-rw-r--r--src/kernel/core/idt.c15
-rw-r--r--src/kernel/core/thread.c110
-rw-r--r--src/kernel/core/worker.c9
3 files changed, 111 insertions, 23 deletions
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 <idt.h>
#include <gdt.h>
+#include <hashtbl.h>
+
#include <frame.h>
#include <paging.h>
#include <worker.h>
@@ -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(&current_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(&current_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;
}
}
}