sched: add thread management syscalls
parent
a370ef69f6
commit
104578d072
@ -0,0 +1,95 @@
|
|||||||
|
/* See the end of this file for copyright, license, and warranty information. */
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <arch/hardware.h>
|
||||||
|
|
||||||
|
#include <ardix/kent.h>
|
||||||
|
#include <ardix/kevent.h>
|
||||||
|
#include <ardix/malloc.h>
|
||||||
|
#include <ardix/sched.h>
|
||||||
|
#include <ardix/util.h>
|
||||||
|
|
||||||
|
enum task_state {
|
||||||
|
/** Task is dead / doesn't exist */
|
||||||
|
TASK_DEAD,
|
||||||
|
/** Task is currently running. */
|
||||||
|
TASK_RUNNING,
|
||||||
|
/** Task is waiting for its next time share. */
|
||||||
|
TASK_QUEUE,
|
||||||
|
/** Task is sleeping, `task::sleep` specifies for how many ticks. */
|
||||||
|
TASK_SLEEP,
|
||||||
|
/** Task is waiting for I/O to flush buffers. */
|
||||||
|
TASK_IOWAIT,
|
||||||
|
/** Task is waiting for a mutex to be unlocked. */
|
||||||
|
TASK_LOCKWAIT,
|
||||||
|
/** Task is waiting for child to */
|
||||||
|
TASK_WAITPID,
|
||||||
|
};
|
||||||
|
|
||||||
|
/** @brief Core structure holding information about a task. */
|
||||||
|
struct task {
|
||||||
|
struct tcb tcb;
|
||||||
|
|
||||||
|
struct kent kent;
|
||||||
|
/**
|
||||||
|
* @brief Points to the bottom of the stack.
|
||||||
|
* In a full-descending stack, this is one word after the highest stack address.
|
||||||
|
*/
|
||||||
|
void *bottom;
|
||||||
|
/** @brief Lowest address in the stack, as returned by malloc. */
|
||||||
|
void *stack;
|
||||||
|
/** @brief If state is `TASK_SLEEP`, the total amount of ticks to sleep */
|
||||||
|
unsigned long int sleep;
|
||||||
|
/** @brief Last execution in ticks */
|
||||||
|
unsigned long int last_tick;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* if a child process exited before its parent called waitpid(),
|
||||||
|
* this is where the children are stored temporarily
|
||||||
|
*/
|
||||||
|
struct list_head pending_sigchld;
|
||||||
|
struct mutex pending_sigchld_lock;
|
||||||
|
|
||||||
|
enum task_state state;
|
||||||
|
pid_t pid;
|
||||||
|
};
|
||||||
|
|
||||||
|
__always_inline void task_get(struct task *task)
|
||||||
|
{
|
||||||
|
kent_get(&task->kent);
|
||||||
|
}
|
||||||
|
|
||||||
|
__always_inline void task_put(struct task *task)
|
||||||
|
{
|
||||||
|
kent_put(&task->kent);
|
||||||
|
}
|
||||||
|
|
||||||
|
__always_inline struct task *task_parent(struct task *task)
|
||||||
|
{
|
||||||
|
if (task->pid == 0)
|
||||||
|
return NULL;
|
||||||
|
else
|
||||||
|
return container_of(task->kent.parent, struct task, kent);
|
||||||
|
}
|
||||||
|
|
||||||
|
struct task_kevent {
|
||||||
|
struct kevent kevent;
|
||||||
|
struct task *task;
|
||||||
|
int status;
|
||||||
|
};
|
||||||
|
|
||||||
|
void task_kevent_create_and_dispatch(struct task *task, int status);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This file is part of Ardix.
|
||||||
|
* Copyright (c) 2020, 2021 Felix Kopp <owo@fef.moe>.
|
||||||
|
*
|
||||||
|
* Ardix is non-violent software: you may only use, redistribute,
|
||||||
|
* and/or modify it under the terms of the CNPLv6+ as found in
|
||||||
|
* the LICENSE file in the source code root directory or at
|
||||||
|
* <https://git.pixie.town/thufie/CNPL>.
|
||||||
|
*
|
||||||
|
* Ardix comes with ABSOLUTELY NO WARRANTY, to the extent
|
||||||
|
* permitted by applicable law. See the CNPLv6+ for details.
|
||||||
|
*/
|
@ -0,0 +1,21 @@
|
|||||||
|
/* See the end of this file for copyright, license, and warranty information. */
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <toolchain.h>
|
||||||
|
|
||||||
|
__shared pid_t waitpid(pid_t pid, int *stat_loc, int options);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This file is part of Ardix.
|
||||||
|
* Copyright (c) 2020, 2021 Felix Kopp <owo@fef.moe>.
|
||||||
|
*
|
||||||
|
* Ardix is non-violent software: you may only use, redistribute,
|
||||||
|
* and/or modify it under the terms of the CNPLv6+ as found in
|
||||||
|
* the LICENSE file in the source code root directory or at
|
||||||
|
* <https://git.pixie.town/thufie/CNPL>.
|
||||||
|
*
|
||||||
|
* Ardix comes with ABSOLUTELY NO WARRANTY, to the extent
|
||||||
|
* permitted by applicable law. See the CNPLv6+ for details.
|
||||||
|
*/
|
@ -0,0 +1,189 @@
|
|||||||
|
/* See the end of this file for copyright, license, and warranty information. */
|
||||||
|
|
||||||
|
#include <arch-generic/do_switch.h>
|
||||||
|
|
||||||
|
#include <ardix/kent.h>
|
||||||
|
#include <ardix/kevent.h>
|
||||||
|
#include <ardix/malloc.h>
|
||||||
|
#include <ardix/mutex.h>
|
||||||
|
#include <ardix/sched.h>
|
||||||
|
#include <ardix/syscall.h>
|
||||||
|
#include <ardix/task.h>
|
||||||
|
#include <ardix/userspace.h>
|
||||||
|
#include <ardix/util.h>
|
||||||
|
|
||||||
|
#include <errno.h>
|
||||||
|
#include <toolchain.h>
|
||||||
|
|
||||||
|
#include <arch/debug.h>
|
||||||
|
|
||||||
|
static void task_kevent_destroy(struct kent *kent)
|
||||||
|
{
|
||||||
|
struct kevent *kevent = container_of(kent, struct kevent, kent);
|
||||||
|
struct task_kevent *task_kevent = container_of(kevent, struct task_kevent, kevent);
|
||||||
|
kfree(task_kevent);
|
||||||
|
}
|
||||||
|
|
||||||
|
void task_kevent_create_and_dispatch(struct task *task, int status)
|
||||||
|
{
|
||||||
|
struct task_kevent *event = kmalloc(sizeof(*event));
|
||||||
|
if (event == NULL)
|
||||||
|
return; /* TODO: we're fucked here */
|
||||||
|
|
||||||
|
event->kevent.kent.parent = &task->kent;
|
||||||
|
event->kevent.kent.destroy = task_kevent_destroy;
|
||||||
|
event->kevent.kind = KEVENT_TASK;
|
||||||
|
kent_init(&event->kevent.kent);
|
||||||
|
event->task = task;
|
||||||
|
event->status = status;
|
||||||
|
kevent_dispatch(&event->kevent);
|
||||||
|
}
|
||||||
|
|
||||||
|
struct dead_child {
|
||||||
|
struct list_head link; /* -> task::pending_sigchld */
|
||||||
|
struct task *child;
|
||||||
|
int status;
|
||||||
|
};
|
||||||
|
|
||||||
|
__noreturn void sys_exit(int status)
|
||||||
|
{
|
||||||
|
struct task *task = current;
|
||||||
|
|
||||||
|
struct task *parent = task_parent(task);
|
||||||
|
task_kevent_create_and_dispatch(task, status);
|
||||||
|
|
||||||
|
if (parent->state != TASK_WAITPID) {
|
||||||
|
/*
|
||||||
|
* atomic_kmalloc wouldn't actually be needed here, but we use
|
||||||
|
* it anyway because it has a separate heap which is more likely
|
||||||
|
* to have an emergency reserve of memory. A failing allocation
|
||||||
|
* would *really* be inconvenient here.
|
||||||
|
*/
|
||||||
|
struct dead_child *entry = atomic_kmalloc(sizeof(*entry));
|
||||||
|
if (entry == NULL) {
|
||||||
|
schedule(); /* TODO: we're severely fucked here */
|
||||||
|
}
|
||||||
|
|
||||||
|
entry->child = task;
|
||||||
|
|
||||||
|
mutex_lock(&parent->pending_sigchld_lock);
|
||||||
|
list_insert(&parent->pending_sigchld, &entry->link);
|
||||||
|
mutex_unlock(&parent->pending_sigchld_lock);
|
||||||
|
}
|
||||||
|
|
||||||
|
task->state = TASK_DEAD;
|
||||||
|
|
||||||
|
schedule();
|
||||||
|
|
||||||
|
/* we should never get here, this is only needed to make gcc happy */
|
||||||
|
while (1);
|
||||||
|
}
|
||||||
|
|
||||||
|
struct task_kevent_extra {
|
||||||
|
struct task *parent;
|
||||||
|
|
||||||
|
/* this is a return value from the listener */
|
||||||
|
struct {
|
||||||
|
struct task *child;
|
||||||
|
int status;
|
||||||
|
} ret;
|
||||||
|
};
|
||||||
|
|
||||||
|
static int task_kevent_listener(struct kevent *event, void *_extra)
|
||||||
|
{
|
||||||
|
struct task_kevent_extra *extra = _extra;
|
||||||
|
struct task_kevent *task_kevent = container_of(event, struct task_kevent, kevent);
|
||||||
|
struct task *child = task_kevent->task;
|
||||||
|
|
||||||
|
if (extra->parent != task_parent(child))
|
||||||
|
return KEVENT_CB_NONE;
|
||||||
|
|
||||||
|
extra->parent->state = TASK_QUEUE;
|
||||||
|
|
||||||
|
extra->ret.child = child;
|
||||||
|
extra->ret.status = task_kevent->status;
|
||||||
|
|
||||||
|
return KEVENT_CB_STOP | KEVENT_CB_LISTENER_DEL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* manually poll for dead children if there is no memory for a kevent listener */
|
||||||
|
static int waitpid_poll(struct task *parent)
|
||||||
|
{
|
||||||
|
/* mutex is already locked here */
|
||||||
|
|
||||||
|
while (list_is_empty(&parent->pending_sigchld)) {
|
||||||
|
mutex_unlock(&parent->pending_sigchld_lock);
|
||||||
|
/*
|
||||||
|
* TODO: This has to be gotten rid of when the scheduler
|
||||||
|
* isn't a simple round robin one anymore!
|
||||||
|
*/
|
||||||
|
yield(TASK_QUEUE);
|
||||||
|
mutex_lock(&parent->pending_sigchld_lock);
|
||||||
|
}
|
||||||
|
|
||||||
|
mutex_unlock(&parent->pending_sigchld_lock);
|
||||||
|
|
||||||
|
struct dead_child *dead_child = list_first_entry(&parent->pending_sigchld,
|
||||||
|
struct dead_child,
|
||||||
|
link);
|
||||||
|
int status = dead_child->status;
|
||||||
|
|
||||||
|
task_put(dead_child->child);
|
||||||
|
list_delete(&dead_child->link);
|
||||||
|
kfree(dead_child);
|
||||||
|
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* use kevent system to wait for dying children */
|
||||||
|
static int waitpid_yield(struct task *parent)
|
||||||
|
{
|
||||||
|
/* mutex is already locked here */
|
||||||
|
|
||||||
|
struct task_kevent_extra extra = {
|
||||||
|
.parent = parent,
|
||||||
|
};
|
||||||
|
|
||||||
|
if (kevent_listener_add(KEVENT_TASK, task_kevent_listener, &extra) == NULL)
|
||||||
|
return waitpid_poll(parent);
|
||||||
|
|
||||||
|
mutex_unlock(&parent->pending_sigchld_lock);
|
||||||
|
yield(TASK_WAITPID);
|
||||||
|
|
||||||
|
/* extra.ret is set by task_kevent_listener */
|
||||||
|
task_put(extra.ret.child);
|
||||||
|
return extra.ret.status;
|
||||||
|
}
|
||||||
|
|
||||||
|
long sys_waitpid(pid_t pid, int __user *stat_loc, int options)
|
||||||
|
{
|
||||||
|
struct task *parent = current;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* both waitpid_yield and waitpid_poll expect the mutex
|
||||||
|
* to be locked and will unlock it before returning
|
||||||
|
*/
|
||||||
|
mutex_lock(&parent->pending_sigchld_lock);
|
||||||
|
|
||||||
|
int status;
|
||||||
|
if (list_is_empty(&parent->pending_sigchld))
|
||||||
|
status = waitpid_yield(parent);
|
||||||
|
else
|
||||||
|
status = waitpid_poll(parent);
|
||||||
|
|
||||||
|
copy_to_user(stat_loc, &status, sizeof(*stat_loc));
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This file is part of Ardix.
|
||||||
|
* Copyright (c) 2020, 2021 Felix Kopp <owo@fef.moe>.
|
||||||
|
*
|
||||||
|
* Ardix is non-violent software: you may only use, redistribute,
|
||||||
|
* and/or modify it under the terms of the CNPLv6+ as found in
|
||||||
|
* the LICENSE file in the source code root directory or at
|
||||||
|
* <https://git.pixie.town/thufie/CNPL>.
|
||||||
|
*
|
||||||
|
* Ardix comes with ABSOLUTELY NO WARRANTY, to the extent
|
||||||
|
* permitted by applicable law. See the CNPLv6+ for details.
|
||||||
|
*/
|
@ -0,0 +1,23 @@
|
|||||||
|
/* See the end of this file for copyright, license, and warranty information. */
|
||||||
|
|
||||||
|
#include <ardix/syscall.h>
|
||||||
|
|
||||||
|
#include <sys/wait.h>
|
||||||
|
|
||||||
|
pid_t waitpid(pid_t pid, int *stat_loc, int options)
|
||||||
|
{
|
||||||
|
return (pid_t)syscall(SYS_waitpid, (sysarg_t)pid, (sysarg_t)stat_loc, (sysarg_t)options);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This file is part of Ardix.
|
||||||
|
* Copyright (c) 2020, 2021 Felix Kopp <owo@fef.moe>.
|
||||||
|
*
|
||||||
|
* Ardix is non-violent software: you may only use, redistribute,
|
||||||
|
* and/or modify it under the terms of the CNPLv6+ as found in
|
||||||
|
* the LICENSE file in the source code root directory or at
|
||||||
|
* <https://git.pixie.town/thufie/CNPL>.
|
||||||
|
*
|
||||||
|
* Ardix comes with ABSOLUTELY NO WARRANTY, to the extent
|
||||||
|
* permitted by applicable law. See the CNPLv6+ for details.
|
||||||
|
*/
|
Loading…
Reference in New Issue