diff --git a/kernel-src/include/kernel/scheduler.h b/kernel-src/include/kernel/scheduler.h index 2b6583e6..bc563315 100644 --- a/kernel-src/include/kernel/scheduler.h +++ b/kernel-src/include/kernel/scheduler.h @@ -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 { diff --git a/kernel-src/sys/scheduler.c b/kernel-src/sys/scheduler.c index c50c49a2..29d9a9ea 100644 --- a/kernel-src/sys/scheduler.c +++ b/kernel-src/sys/scheduler.c @@ -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); diff --git a/kernel-src/sys/signal.c b/kernel-src/sys/signal.c index f5b65174..f0995419 100644 --- a/kernel-src/sys/signal.c +++ b/kernel-src/sys/signal.c @@ -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) { diff --git a/kernel-src/sys/syscalls/fork.c b/kernel-src/sys/syscalls/fork.c index b1906274..8a60dc85 100644 --- a/kernel-src/sys/syscalls/fork.c +++ b/kernel-src/sys/syscalls/fork.c @@ -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); diff --git a/kernel-src/sys/syscalls/newthread.c b/kernel-src/sys/syscalls/newthread.c index 87afb877..694694ca 100644 --- a/kernel-src/sys/syscalls/newthread.c +++ b/kernel-src/sys/syscalls/newthread.c @@ -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; } diff --git a/kernel-src/sys/syscalls/waitpid.c b/kernel-src/sys/syscalls/waitpid.c index 4984aaf8..c651ef9c 100644 --- a/kernel-src/sys/syscalls/waitpid.c +++ b/kernel-src/sys/syscalls/waitpid.c @@ -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;