mirror of
https://github.com/Mathewnd/Astral.git
synced 2026-10-27 20:58:22 +00:00
scheduler: use a linked list for threads in a process instead of a table
This commit is contained in:
@@ -34,6 +34,7 @@ typedef struct thread_t {
|
||||
struct thread_t *prev;
|
||||
struct thread_t *sleepnext;
|
||||
struct thread_t *sleepprev;
|
||||
struct thread_t *procnext;
|
||||
struct proc_t *proc;
|
||||
struct cpu_t *cpu;
|
||||
struct cpu_t *cputarget;
|
||||
@@ -70,8 +71,9 @@ typedef struct proc_t {
|
||||
struct proc_t *child;
|
||||
pid_t pid;
|
||||
cred_t cred;
|
||||
thread_t **threads;
|
||||
size_t threadtablesize;
|
||||
spinlock_t threadlistlock;
|
||||
thread_t *threadlist;
|
||||
bool nomorethreads;
|
||||
size_t runningthreadcount;
|
||||
size_t fdcount;
|
||||
uintmax_t fdfirst;
|
||||
@@ -84,7 +86,6 @@ typedef struct proc_t {
|
||||
spinlock_t nodeslock;
|
||||
semaphore_t waitsem;
|
||||
spinlock_t exiting;
|
||||
bool nomorethreads;
|
||||
|
||||
spinlock_t jobctllock;
|
||||
struct {
|
||||
|
||||
+24
-30
@@ -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
@@ -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) {
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user