Skip to content

Commit

Permalink
First pass at implementing thread groups
Browse files Browse the repository at this point in the history
It builds and will give you a shell. Gonna call it a day.
  • Loading branch information
tbodt committed Jan 28, 2018
1 parent c776bb8 commit 7e5e9e5
Show file tree
Hide file tree
Showing 13 changed files with 246 additions and 125 deletions.
20 changes: 15 additions & 5 deletions fs/tty.c
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ static int tty_get(int type, int num, struct tty **tty_out) {
return 0;
}

static void tty_release(struct tty *tty) {
void tty_release(struct tty *tty) {
lock(&ttys_lock);
lock(&tty->lock);
if (--tty->refcount == 0) {
Expand Down Expand Up @@ -92,8 +92,10 @@ static int tty_open(int major, int minor, int type, struct fd *fd) {

lock(&pids_lock);
if (current->sid == current->pid) {
if (current->tty == NULL) {
current->tty = tty;
lock(&current->group->lock);
if (current->group->tty == NULL) {
current->group->tty = tty;
unlock(&current->group->lock);
tty->session = current->sid;
tty->fg_group = current->pgid;
}
Expand Down Expand Up @@ -313,6 +315,13 @@ static ssize_t tty_ioctl_size(struct fd *fd, int cmd) {
return -1;
}

static bool tty_is_current(struct tty *tty) {
lock(&current->group->lock);
bool is_current = current->group->tty == tty;
unlock(&current->group->lock);
return is_current;
}

static int tty_ioctl(struct fd *fd, int cmd, void *arg) {
int err = 0;
struct tty *tty = fd->tty;
Expand Down Expand Up @@ -342,14 +351,15 @@ static int tty_ioctl(struct fd *fd, int cmd, void *arg) {
break;

case TIOCGPRGP_:
if (tty != current->tty || tty->fg_group == 0) {
if (tty_is_current(tty) || tty->fg_group == 0) {
err = _ENOTTY;
break;
}
TRACELN("tty group = %d", tty->fg_group);
*(dword_t *) arg = tty->fg_group; break;
case TIOCSPGRP_:
if (tty != current->tty || current->sid != tty->session) {
// FIXME I think current->sid needs to be locked
if (tty_is_current(tty) || current->sid != tty->session) {
err = _ENOTTY;
break;
}
Expand Down
3 changes: 2 additions & 1 deletion kernel/exec.c
Original file line number Diff line number Diff line change
Expand Up @@ -346,7 +346,8 @@ static int elf_exec(struct fd *fd, const char *file, char *const argv[], char *c
current->cpu.esp = sp;
current->cpu.eip = entry;
current->cpu.fcw = 0x37f;
notify(&current->vfork_done);
current->vfork_done = true;
notify(&current->vfork_cond);

err = 0;
out_free_interp:
Expand Down
136 changes: 84 additions & 52 deletions kernel/exit.c
Original file line number Diff line number Diff line change
Expand Up @@ -3,116 +3,148 @@
#include "kernel/calls.h"
#include "fs/fd.h"

void (*exit_hook)(int code) = NULL;
static void halt_system(int status);

static void exit_tgroup(struct task *task) {
struct tgroup *group = task->group;
lock(&group->lock);
list_remove(&task->group_links);
if (list_empty(&group->threads))
free(group);
else
unlock(&group->lock);
}

noreturn void do_exit(int status) {
// this is the part where we release all our resources
bool was_leader = task_is_leader(current);

// release all our resources
mem_release(current->cpu.mem);
fdtable_release(current->files);
fs_info_release(current->fs);
sighand_release(current->sighand);
exit_tgroup(current);
vfork_notify(current);

// save things that our parent might be interested in
current->exit_code = status;
struct rusage_ rusage = rusage_get_current();
lock(&current->group->lock);
rusage_add(&current->group->rusage, &rusage);
unlock(&current->group->lock);

// notify everyone we died
bool init_died = current->pid == 1;
lock(&pids_lock);
struct task *parent = current->parent;
unlock(&pids_lock);
lock(&parent->exit_lock);
lock(&parent->group->lock);
if (was_leader) {
current->zombie = true;
notify(&parent->group->child_exit);
}
unlock(&parent->group->lock);

current->exit_code = status;
current->zombie = true;
current->rusage = rusage_get_current();
rusage_add(&parent->children_rusage, &current->rusage);

notify(&parent->child_exit);
unlock(&parent->exit_lock);
notify(&current->vfork_done);
// TODO futex wake on current->clear_tid

if (init_died) {
// brutally murder everything
// which will leave everything in an inconsistent state. I will solve this problem later.
lock(&pids_lock);
for (int i = 2; i < MAX_PID; i++) {
struct task *task = pid_get_task(i);
if (task != NULL)
pthread_kill(task->thread, SIGKILL);
}
unlock(&pids_lock);
if (current->pid == 1)
halt_system(status);
pthread_exit(NULL);
}

// unmount all filesystems
struct mount *mount = mounts;
while (mount) {
if (mount->fs->umount)
mount->fs->umount(mount);
mount = mount->next;
}
void (*exit_hook)(int code) = NULL;
static void halt_system(int status) {
// brutally murder everything
// which will leave everything in an inconsistent state. I will solve this problem later.
lock(&pids_lock);
for (int i = 2; i < MAX_PID; i++) {
struct task *task = pid_get_task(i);
if (task != NULL)
pthread_kill(task->thread, SIGKILL);
}
unlock(&pids_lock);

if (exit_hook != NULL)
exit_hook(status);
// unmount all filesystems
struct mount *mount = mounts;
while (mount) {
if (mount->fs->umount)
mount->fs->umount(mount);
mount = mount->next;
}
pthread_exit(NULL);

if (exit_hook != NULL)
exit_hook(status);
}

dword_t sys_exit(dword_t status) {
do_exit(status << 8);
}

dword_t sys_exit_group(dword_t status) {
do_exit(status << 8);
TODO("exit_group");
/* do_exit(status << 8); */
}

static int reap_if_zombie(struct task *task, addr_t status_addr, addr_t rusage_addr) {
if (task->zombie) {
if (status_addr != 0)
if (user_put(status_addr, task->exit_code))
return _EFAULT;
if (rusage_addr != 0)
if (user_put(rusage_addr, task->rusage))
return _EFAULT;
task_destroy(task);
return 1;
}
return 0;
assert(task_is_leader(task));
if (!task->zombie)
return 0;

// account for task's resource usage
lock(&task->group->lock);
struct rusage_ rusage = task->group->rusage;
rusage_add(&current->group->children_rusage, &rusage);
unlock(&task->group->lock);

if (status_addr != 0)
if (user_put(status_addr, task->exit_code))
return _EFAULT;
if (rusage_addr != 0)
if (user_put(rusage_addr, rusage))
return _EFAULT;

task_destroy(task);
return 1;
}

#define WNOHANG_ 1

dword_t sys_wait4(dword_t id, addr_t status_addr, dword_t options, addr_t rusage_addr) {
STRACE("wait(%d, 0x%x, 0x%x, 0x%x)", id, status_addr, options, rusage_addr);
lock(&current->exit_lock);
lock(&current->group->lock);

retry:
if (id == (dword_t) -1) {
// look for a zombie child
struct task *task;
lock(&pids_lock);
list_for_each_entry(&current->children, task, siblings) {
id = task->pid;
if (reap_if_zombie(task, status_addr, rusage_addr))
if (reap_if_zombie(task, status_addr, rusage_addr)) {
unlock(&pids_lock);
goto found_zombie;
}
}
unlock(&pids_lock);
} else {
// check if this child is a zombie
struct task *task = pid_get_task_zombie(id);
if (task == NULL || task->parent != current) {
unlock(&current->exit_lock);
unlock(&current->group->lock);
return _ECHILD;
}
if (reap_if_zombie(task, status_addr, rusage_addr))
goto found_zombie;
}

if (options & WNOHANG_) {
unlock(&current->exit_lock);
unlock(&current->group->lock);
return _ECHILD;
}

// no matching zombie found, wait for one
wait_for(&current->child_exit, &current->exit_lock);
wait_for(&current->group->child_exit, &current->group->lock);
goto retry;

found_zombie:
unlock(&current->exit_lock);
unlock(&current->group->lock);
return id;
}

Expand Down
45 changes: 37 additions & 8 deletions kernel/fork.c
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
#include "kernel/task.h"
#include "fs/fd.h"
#include "kernel/calls.h"
#include "fs/tty.h"

#define CSIGNAL_ 0x000000ff
#define CLONE_VM_ 0x00000100
Expand Down Expand Up @@ -30,6 +31,20 @@
#define IMPLEMENTED_FLAGS (CLONE_VM_|CLONE_FILES_|CLONE_FS_|CLONE_SIGHAND_|CLONE_SYSVSEM_|CLONE_VFORK_|\
CLONE_SETTLS_|CLONE_CHILD_SETTID_|CLONE_PARENT_SETTID_|CLONE_CHILD_CLEARTID_|CLONE_DETACHED_)

static struct tgroup *tgroup_copy(struct tgroup *old_group) {
struct tgroup *group = malloc(sizeof(struct tgroup));
*group = *old_group;
list_init(&group->threads);
group->tty->refcount++;
group->has_timer = false;
group->timer = NULL;
group->group_exit = false;
group->children_rusage = (struct rusage_) {};
pthread_cond_init(&group->child_exit, NULL);
lock_init(&group->lock);
return group;
}

static int copy_task(struct task *task, dword_t flags, addr_t ptid_addr, addr_t tls_addr, addr_t ctid_addr) {
int err;
struct mem *mem = task->cpu.mem;
Expand Down Expand Up @@ -67,6 +82,17 @@ static int copy_task(struct task *task, dword_t flags, addr_t ptid_addr, addr_t
goto fail_free_fs;
}

struct tgroup *group = task->group;
lock(&group->lock);
if (flags & CLONE_THREAD_) {
list_add(&group->threads, &task->group_links);
} else {
task->group = tgroup_copy(group);
task->group->leader = task;
list_add(&task->group->threads, &task->group_links);
}
unlock(&group->lock);

if (flags & CLONE_SETTLS_) {
err = task_set_thread_area(task, tls_addr);
if (err < 0)
Expand Down Expand Up @@ -101,10 +127,6 @@ static int copy_task(struct task *task, dword_t flags, addr_t ptid_addr, addr_t
return err;
}

// eax = syscall number
// ebx = flags
// ecx = stack
// edx, esi, edi = unimplemented garbage
dword_t sys_clone(dword_t flags, addr_t stack, addr_t ptid, addr_t tls, addr_t ctid) {
STRACE("clone(0x%x, 0x%x, 0x%x, 0x%x, 0x%x)", flags, stack, ptid, tls, ctid);
if (flags & ~CSIGNAL_ & ~IMPLEMENTED_FLAGS) {
Expand Down Expand Up @@ -141,10 +163,10 @@ dword_t sys_clone(dword_t flags, addr_t stack, addr_t ptid, addr_t tls, addr_t c
task_start(task);

if (flags & CLONE_VFORK_) {
// FIXME this doesn't loop, it must be wrong
lock(&task->exit_lock);
wait_for(&task->vfork_done, &task->exit_lock);
unlock(&task->exit_lock);
lock(&task->vfork_lock);
while (!task->vfork_done)
wait_for(&task->vfork_cond, &task->vfork_lock);
unlock(&task->vfork_lock);
}
return task->pid;
}
Expand All @@ -156,3 +178,10 @@ dword_t sys_fork() {
dword_t sys_vfork() {
return sys_clone(CLONE_VFORK_ | CLONE_VM_ | SIGCHLD_, 0, 0, 0, 0);
}

void vfork_notify(struct task *task) {
lock(&task->vfork_lock);
task->vfork_done = true;
notify(&task->vfork_cond);
unlock(&task->vfork_lock);
}
2 changes: 1 addition & 1 deletion kernel/getset.c
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

dword_t sys_getpid() {
STRACE("getpid()");
return current->pid;
return current->tgid;
}
dword_t sys_gettid() {
STRACE("gettid()");
Expand Down
8 changes: 4 additions & 4 deletions kernel/group.c
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ dword_t sys_setpgid(dword_t id, dword_t pgid) {
// when creating a process group, you need to specify your own pid
// TODO or a child in the same session
err = _EPERM;
if (list_empty(&pid->group) && id != pgid)
if (list_empty(&pid->pgroup) && id != pgid)
goto out;
// TODO you can only join a process group in the same session

Expand All @@ -35,9 +35,9 @@ dword_t sys_setpgid(dword_t id, dword_t pgid) {
// TODO cannot set process group of a child that has done exec

if (task->pgid != pgid) {
list_remove(&task->group);
list_remove(&task->pgroup);
task->pgid = pgid;
list_add(&pid->group, &task->group);
list_add(&pid->pgroup, &task->pgroup);
}

err = 0;
Expand All @@ -60,7 +60,7 @@ dword_t sys_setsid() {
struct pid *pid = pid_get(current->pid);
list_add(&pid->session, &current->session);
current->sid = current->pid;
list_add(&pid->group, &current->group);
list_add(&pid->pgroup, &current->pgroup);
current->pgid = current->pid;

unlock(&pids_lock);
Expand Down
Loading

0 comments on commit 7e5e9e5

Please sign in to comment.