scheduler: use a linked list for threads in a process instead of a table

This commit is contained in:
Matheus
2024-05-29 12:55:10 -03:00
parent 5f7791cd08
commit 9260c8cb96
6 changed files with 95 additions and 98 deletions
+24 -30
View File
@@ -50,23 +50,14 @@ proc_t *sched_getprocfrompid(int pid) {
}
static void rtdpc(context_t *, dpcarg_t arg) {
proc_t *proc = arg;
// XXX this can sleep and if it does the entire system burns down sooooooo yeah fix this for the love of god
__assert(proc->threadtablesize == 1);
signal_signalproc(arg, SIGALRM);
}
static void vtdpc(context_t *, dpcarg_t arg) {
proc_t *proc = arg;
// XXX this can sleep and if it does the entire system burns down sooooooo yeah fix this for the love of god
__assert(proc->threadtablesize == 1);
signal_signalproc(arg, SIGVTALRM);
}
static void profdpc(context_t *, dpcarg_t arg) {
proc_t *proc = arg;
// XXX this can sleep and if it does the entire system burns down sooooooo yeah fix this for the love of god
__assert(proc->threadtablesize == 1);
signal_signalproc(arg, SIGPROF);
}
@@ -77,15 +68,8 @@ proc_t *sched_newproc() {
memset(proc, 0, sizeof(proc_t));
proc->threads = alloc(sizeof(thread_t *));
if (proc->threads == NULL) {
slab_free(processcache, proc);
return NULL;
}
// TODO move this to slab
proc->state = SCHED_PROC_STATE_NORMAL;
proc->threadtablesize = 1;
proc->runningthreadcount = 1;
MUTEX_INIT(&proc->mutex);
SPINLOCK_INIT(proc->nodeslock);
@@ -101,10 +85,10 @@ proc_t *sched_newproc() {
itimer_init(&proc->timer.realtime, rtdpc, proc);
itimer_init(&proc->timer.virtualtime, vtdpc, proc);
itimer_init(&proc->timer.profiling, profdpc, proc);
SPINLOCK_INIT(proc->threadlistlock);
proc->fd = alloc(sizeof(fd_t) * 3);
if (proc->fd == NULL) {
free(proc->threads);
slab_free(processcache, proc);
return NULL;
}
@@ -117,7 +101,6 @@ proc_t *sched_newproc() {
if (hashtable_set(&pidtable, proc, &proc->pid, sizeof(proc->pid), true)) {
MUTEX_RELEASE(&sched_pidtablemutex);
free(proc->fd);
free(proc->threads);
slab_free(processcache, proc);
return NULL;
}
@@ -168,7 +151,6 @@ void sched_destroythread(thread_t *thread) {
}
void sched_destroyproc(proc_t *proc) {
free(proc->threads);
free(proc->fd);
slab_free(processcache, proc);
}
@@ -353,8 +335,9 @@ void sched_procexit() {
signal_signalproc(proc->parent, SIGCHLD);
semaphore_signal(&proc->parent->waitsem);
// TODO sigaction flag for this
for (int i = 0; i < belowzombiecount; ++i)
for (int i = 0; i < belowzombiecount; ++i) {
semaphore_signal(&sched_initproc->waitsem);
}
}
void sched_inactiveproc(proc_t *proc) {
@@ -469,21 +452,31 @@ __attribute__((no_caller_saved_registers)) void sched_userspacecheck(context_t *
void sched_stopotherthreads() {
thread_t *thread = _cpu()->thread;
proc_t *proc = thread->proc;
MUTEX_ACQUIRE(&proc->mutex, false);
bool intstatus = interrupt_set(false);
spinlock_acquire(&proc->threadlistlock);
proc->nomorethreads = true;
thread_t *threadlist = proc->threadlist;
for (int i = 0; i < proc->threadtablesize; ++i) {
if (proc->threads[i] == NULL || proc->threads[i] == thread)
spinlock_release(&proc->threadlistlock);
interrupt_set(intstatus);
while (threadlist) {
if (threadlist == thread) {
threadlist = threadlist->procnext;
continue;
}
proc->threads[i]->shouldexit = true;
sched_wakeup(proc->threads[i], SCHED_WAKEUP_REASON_INTERRUPTED);
proc->threads[i] = NULL;
threadlist->shouldexit = true;
sched_wakeup(threadlist, SCHED_WAKEUP_REASON_INTERRUPTED);
threadlist = threadlist->procnext;
}
MUTEX_RELEASE(&proc->mutex);
while (__atomic_load_n(&proc->runningthreadcount, __ATOMIC_SEQ_CST) > 1) sched_yield();
while (__atomic_load_n(&proc->runningthreadcount, __ATOMIC_SEQ_CST) > 1) {
arch_e9_puts("sched_stopotherthreads\n");
sched_yield();
}
proc->nomorethreads = false;
}
@@ -809,7 +802,8 @@ void sched_runinit() {
thread_t *uthread = sched_newthread(entry, PAGE_SIZE * 16, 1, proc, stack);
__assert(uthread);
proc->threads[0] = uthread;
proc->threadlist = uthread;
uthread->procnext = NULL;
uthread->vmmctx = vmmctx;
sched_queue(uthread);
+21 -15
View File
@@ -161,7 +161,6 @@ void signal_signalthread(struct thread_t *thread, int signal, bool urgent) {
void signal_signalproc(struct proc_t *proc, int signal) {
// first, check if any threads have the signal unmasked, and set as pending for the first one
MUTEX_ACQUIRE(&proc->mutex, false);
PROCESS_ENTER(proc);
void *address = proc->signals.actions[signal].address;
bool notignored = address != SIG_IGN && ((address == SIG_DFL && signal_defaultactions[signal] != ACTION_IGN) || address != SIG_DFL);
@@ -172,28 +171,31 @@ void signal_signalproc(struct proc_t *proc, int signal) {
// TODO is one thread has a stop signal masked, the masked wait breaks completely
for (int i = 0; i < proc->threadtablesize; ++i) {
if (proc->threads[i] == NULL || (proc->threads[i]->flags & SCHED_THREAD_FLAGS_DEAD))
thread_t *thread = proc->threadlist;
while (thread) {
if (thread->flags & SCHED_THREAD_FLAGS_DEAD) {
thread = thread->procnext;
continue;
}
THREAD_ENTER(proc->threads[i]);
THREAD_ENTER(thread);
if (SIGNAL_GET(&proc->threads[i]->signals.mask, signal) == 0 || notignorable) {
SIGNAL_SETON(&proc->threads[i]->signals.pending, signal);
if (SIGNAL_GET(&thread->signals.mask, signal) == 0 || notignorable) {
SIGNAL_SETON(&thread->signals.pending, signal);
if (notignored || notignorable) {
int reason = SCHED_WAKEUP_REASON_INTERRUPTED;
if (proc->threads[i]->signals.stopped && (signal == SIGCONT || signal == SIGKILL)) {
if (thread->signals.stopped && (signal == SIGCONT || signal == SIGKILL)) {
// thread is stopped and we have to wake it up
proc->threads[i]->signals.stopped = false;
thread->signals.stopped = false;
reason = 0; // wake it up from the non interruptible sleep
threadcontinued = true;
} else if (signal == SIGCONT) {
// thread is not stopped, ignore SIGCONT
SIGNAL_SETOFF(&proc->threads[i]->signals.pending, signal);
SIGNAL_SETOFF(&thread->signals.pending, signal);
goto threadleave;
}
sched_wakeup(proc->threads[i], reason);
sched_wakeup(thread, reason);
// TODO ipi if running isn't self
}
@@ -201,19 +203,23 @@ void signal_signalproc(struct proc_t *proc, int signal) {
// XXX possible race condition where a thread will change the process mask or the action of stopping signals
// after it was sent to everyone
if ((shouldstop || signal == SIGCONT) == false) {
THREAD_LEAVE(proc->threads[i]);
THREAD_LEAVE(thread);
PROCESS_LEAVE(proc);
MUTEX_RELEASE(&proc->mutex);
spinlock_release(&proc->threadlistlock);
return;
}
}
threadleave:
THREAD_LEAVE(proc->threads[i]);
THREAD_LEAVE(thread);
thread = thread->procnext;
}
spinlock_release(&proc->threadlistlock);
// tell the parent that a child stopped
// TODO when stopping make sure that a thread was actually stopped
// TODO protect parent with a spinlock
if ((notignorable || notignored) && signal_defaultactions[signal] == ACTION_STOP) {
proc->status = 0x7f;
proc->status |= signal << 8;
@@ -229,14 +235,14 @@ void signal_signalproc(struct proc_t *proc, int signal) {
if (threadcontinued) {
proc->status = 0xffff;
proc->signals.continueunwaited = true;
if (proc->parent && (proc->parent->signals.actions[SIGCHLD].flags & SA_NOCLDSTOP) == 0) {
signal_signalproc(proc->parent, SIGCHLD);
}
semaphore_signal(&proc->parent->waitsem);
}
MUTEX_RELEASE(&proc->mutex);
// if its ignorable and there wasn't any thread with it unmasked, set it as pending for the whole process
// not ignorable signals will be sent to all threads individually
if (notignorable == false && (threadcontinued || shouldstop) == false) {
+2 -1
View File
@@ -46,7 +46,8 @@ syscallret_t syscall_fork(context_t *ctx) {
nproc->umask = _cpu()->thread->proc->umask;
nproc->root = sched_getroot();
nproc->cwd = sched_getcwd();
nproc->threads[0] = nthread;
nproc->threadlist = nthread;
nthread->procnext = NULL;
ARCH_CONTEXT_THREADSAVE(nthread, ctx);
+26 -41
View File
@@ -8,6 +8,7 @@ syscallret_t syscall_newthread(context_t *, void *entry, void *stack) {
.ret = -1
};
// TODO perhaps clean up threads here as well
// if the kernel was specified to have no user threading in the cmdline
if (cmdline_get("nomultithread")) {
ret.errno = ENOSYS;
@@ -16,53 +17,37 @@ syscallret_t syscall_newthread(context_t *, void *entry, void *stack) {
proc_t *proc = _cpu()->thread->proc;
MUTEX_ACQUIRE(&proc->mutex, false);
thread_t *thread = sched_newthread(entry, 16 * PAGE_SIZE, 1, _cpu()->thread->proc, stack);
if (thread == NULL) {
ret.errno = ENOMEM;
return ret;
}
// if we currently don't want any more threads to start
if (proc->nomorethreads == true) {
MUTEX_RELEASE(&proc->mutex);
thread->vmmctx = _cpu()->thread->vmmctx;
bool intstatus = interrupt_set(false);
spinlock_acquire(&proc->threadlistlock);
bool shouldntstart = proc->nomorethreads;
if (shouldntstart == false) {
thread->procnext = proc->threadlist;
proc->threadlist = thread;
__atomic_fetch_add(&proc->runningthreadcount, 1, __ATOMIC_SEQ_CST);
}
spinlock_release(&proc->threadlistlock);
interrupt_set(intstatus);
if (shouldntstart) {
sched_destroythread(thread);
ret.errno = EAGAIN;
return ret;
}
int i;
for (i = 0; i < proc->threadtablesize; ++i) {
thread_t *thread = proc->threads[i];
if (thread == NULL || (thread->flags & SCHED_THREAD_FLAGS_DEAD) == 0)
continue;
// reuse the slot in the threads table
sched_destroythread(thread);
break;
}
if (i == proc->threadtablesize) {
// resize it
void *tmp = realloc(proc->threads, (proc->threadtablesize + 1) * sizeof(thread_t *));
if (tmp == NULL) {
ret.errno = ENOMEM;
goto cleanup;
}
proc->threads = tmp;
}
proc->threads[i] = sched_newthread(entry, 16 * PAGE_SIZE, 1, _cpu()->thread->proc, stack);
if (proc->threads[i] == NULL) {
ret.errno = ENOMEM;
goto cleanup;
}
proc->threads[i]->vmmctx = _cpu()->thread->vmmctx;
ret.errno = 0;
ret.ret = proc->threads[i]->tid;
ret.ret = thread->tid;
__atomic_fetch_add(&proc->runningthreadcount, 1, __ATOMIC_SEQ_CST);
sched_queue(proc->threads[i]);
cleanup:
MUTEX_RELEASE(&proc->mutex);
if (shouldntstart == false)
sched_queue(thread);
return ret;
}
+18 -8
View File
@@ -102,16 +102,26 @@ syscallret_t syscall_waitpid(context_t *context, pid_t pid, int *status, int opt
MUTEX_RELEASE(&proc->mutex);
for (int i = 0; i < iterator->threadtablesize && zombie; ++i) {
if (iterator->threads[i] == NULL)
continue;
if (zombie) {
bool intstatus = interrupt_set(false);
spinlock_acquire(&iterator->threadlistlock);
volatile int *flags = (volatile int *)(&iterator->threads[i]->flags);
// wait until the thread can actually be unallocated
while ((*flags & SCHED_THREAD_FLAGS_DEAD) == 0)
sched_yield();
thread_t *threadlist = iterator->threadlist;
iterator->threadlist = NULL;
sched_destroythread(iterator->threads[i]);
spinlock_release(&iterator->threadlistlock);
interrupt_set(intstatus);
while (threadlist) {
thread_t *freethread = threadlist;
threadlist = threadlist->procnext;
volatile int *flags = (volatile int *)(&freethread->flags);
// wait until the thread can actually be unallocated
while ((*flags & SCHED_THREAD_FLAGS_DEAD) == 0)
sched_yield();
sched_destroythread(freethread);
}
}
ret.ret = iterator->pid;