diff options
author | sotech117 <michael_foiani@brown.edu> | 2024-02-20 18:23:50 +0000 |
---|---|---|
committer | sotech117 <michael_foiani@brown.edu> | 2024-02-20 18:23:50 +0000 |
commit | d2b13e08f8d6111b7f7e79a47a124fc47dd9433c (patch) | |
tree | ec46a99622c40dae1d45ff516699f949193275a2 | |
parent | 84aae99b4f2f4997db95b9ea6db2a61f582b51ea (diff) |
make tests and fix bugs that the tests caught :)
-rw-r--r-- | kernel/proc/kthread.c | 2 | ||||
-rw-r--r-- | kernel/proc/proc.c | 16 | ||||
-rw-r--r-- | kernel/proc/sched.c | 1 | ||||
-rw-r--r-- | kernel/test/proctest.c | 237 |
4 files changed, 247 insertions, 9 deletions
diff --git a/kernel/proc/kthread.c b/kernel/proc/kthread.c index 42beb77..46dd627 100644 --- a/kernel/proc/kthread.c +++ b/kernel/proc/kthread.c @@ -99,7 +99,7 @@ kthread_t *kthread_create(proc_t *proc, kthread_func_t func, long arg1, list_init(&new_thread->kt_mutexes); new_thread->kt_recent_core = 0; - dbg(DBG_THR, "SUCCESFULLY created a new thread with proc name=%s, id=%d\n", proc->p_name, proc->p_pid); + dbg(DBG_THR, "SUCCESFULLY created a new THREAD with proc name=%s, id=%d\n", proc->p_name, proc->p_pid); return new_thread; } diff --git a/kernel/proc/proc.c b/kernel/proc/proc.c index 2a7cdba..d0566ec 100644 --- a/kernel/proc/proc.c +++ b/kernel/proc/proc.c @@ -181,8 +181,6 @@ proc_t *proc_create(const char *name) return NULL; } - dbg(DBG_PROC, "creating process name=%s & pid =%d\n", name, proc_pid); - proc_t *proc = (proc_t *)slab_obj_alloc(proc_allocator); if (proc == NULL) { @@ -219,6 +217,8 @@ proc_t *proc_create(const char *name) list_insert_tail(&curproc->p_children, &proc->p_child_link); list_insert_tail(&proc_list, &proc->p_list_link); + dbg(DBG_PROC, "SUCESSFULLY created PROCESS name=%s & pid =%d\n", name, proc_pid); + return proc; } @@ -325,12 +325,11 @@ void proc_kill_all() { // NOT_YET_IMPLEMENTED("PROCS: proc_kill_all"); - // TODO: consider children on children - list_iterate(&proc_initproc->p_children, thr, kthread_t, kt_plink) + list_iterate(&proc_list, p, proc_t, p_list_link) { - if((proc_t *) &thr->kt_proc != curproc) + if (p->p_pid != curproc->p_pid && p->p_pid != PID_IDLE) { - kthread_cancel(thr, 0); + proc_kill(p, 0); } } @@ -435,8 +434,10 @@ pid_t do_waitpid(pid_t pid, int *status, int options) if (status != NULL) { + dbg(DBG_PROC, "setting status to %d\n", child->p_status); *status = child->p_status; } + list_remove(&child->p_child_link); proc_destroy(child); dbg(DBG_PROC, "exited child pid=%d\n", child->p_pid); @@ -459,6 +460,7 @@ pid_t do_waitpid(pid_t pid, int *status, int options) dbg(DBG_PROC, "found a dead thread with pid=%d\n", child->p_pid); if (status != NULL) { + dbg(DBG_PROC, "setting status to %d\n", child->p_status); *status = child->p_status; } int child_pid = child->p_pid; @@ -487,7 +489,7 @@ void do_exit(long status) { // NOT_YET_IMPLEMENTED("PROCS: do_exit"); - dbg(DBG_PROC, "proc exiting with pid=%d", curproc->p_pid); + dbg(DBG_PROC, "proc exiting with pid=%d and status=%ld\n", curproc->p_pid, status); kthread_exit((void *)status); } diff --git a/kernel/proc/sched.c b/kernel/proc/sched.c index 0461279..cd8e438 100644 --- a/kernel/proc/sched.c +++ b/kernel/proc/sched.c @@ -243,7 +243,6 @@ void sched_switch(ktqueue_t *queue) intr_enable(); intr_setipl(oldIPL); - // TODO: ask about why we need the old thread context } /* diff --git a/kernel/test/proctest.c b/kernel/test/proctest.c index eb70c81..9ab4c65 100644 --- a/kernel/test/proctest.c +++ b/kernel/test/proctest.c @@ -45,6 +45,235 @@ void test_termination() "Expected: %d, Actual: %d number of processes have been cleaned up\n", num_procs_created, count); } +/* + Tests the edge cases of do_waitpid. + 1. No child processes to wait for + 2. Waiting on a specific process id + 3. Waiting any child process (-1) +*/ +void test_do_waitpid() +{ + int status; + int ret = do_waitpid(-1, &status, 0); + test_assert(ret == -ECHILD, "No child processes to wait for"); + + proc_t *new_proc1 = proc_create("proc test 1"); + kthread_t *new_kthread1 = kthread_create(new_proc1, test_func, new_proc1->p_pid, new_proc1); + sched_make_runnable(new_kthread1); + + proc_t *new_proc2 = proc_create("proc test 2"); + kthread_t *new_kthread2 = kthread_create(new_proc2, test_func, new_proc2->p_pid, new_proc2); + sched_make_runnable(new_kthread2); + + proc_t *new_proc3 = proc_create("proc test 3"); + kthread_t *new_kthread3 = kthread_create(new_proc3, test_func, new_proc3->p_pid, new_proc3); + sched_make_runnable(new_kthread3); + + // wait for a specific process id + ret = do_waitpid(new_proc2->p_pid, &status, 0); + test_assert(ret == new_proc2->p_pid, "Waiting for a specific process id"); + + dbg(DBG_TEST, "Successfully removed proc 2 with pid: %d\n", new_proc2->p_pid); + + // wait for any child process + ret = do_waitpid(-1, &status, 0); + test_assert(ret == new_proc1->p_pid, "Waiting for any child process"); + dbg(DBG_TEST, "Successfully removed proc 1 with pid: %d\n", new_proc1->p_pid); + ret = do_waitpid(-1, &status, 0); + test_assert(ret == new_proc3->p_pid, "Waiting for any child process"); + dbg(DBG_TEST, "Successfully removed proc 3 with pid: %d\n", new_proc3->p_pid); + + ret = do_waitpid(-1, &status, 0); + test_assert(ret == -ECHILD, "No child processes to wait for"); +} + +/* + Tests proc_kill_all on a single process. +*/ +void *test_func_kill(long arg1, void *arg2) +{ + // function to be called by the process + proc_t *proc_as_arg = (proc_t *)arg2; + test_assert(arg1 == proc_as_arg->p_pid, "Arguments are not set up correctly"); + test_assert(proc_as_arg->p_state == PROC_RUNNING, "Process state is not running"); + test_assert(list_empty(&proc_as_arg->p_children), "There should be no child processes"); + dbg(DBG_TEST, "Process with pid=%d is running and calling proc_kill_all\n", proc_as_arg->p_pid); + proc_kill_all(); + return NULL; +} +void test_proc_kill_all() +{ + proc_t *new_proc_kill = proc_create("proc test kill"); + kthread_t *new_kthread_kill = kthread_create(new_proc_kill, test_func_kill, new_proc_kill->p_pid, new_proc_kill); + sched_make_runnable(new_kthread_kill); + + proc_t *new_proc1 = proc_create("proc test 1"); + kthread_t *new_kthread1 = kthread_create(new_proc1, test_func, new_proc1->p_pid, new_proc1); + sched_make_runnable(new_kthread1); + + proc_t *new_proc2 = proc_create("proc test 2"); + kthread_t *new_kthread2 = kthread_create(new_proc2, test_func, new_proc2->p_pid, new_proc2); + sched_make_runnable(new_kthread2); + + // wait on the process that calls proc_kill_all + int status; + int ret = do_waitpid(new_proc_kill->p_pid, &status, 0); + test_assert(ret == new_proc_kill->p_pid, "Waiting for a specific process id"); + dbg(DBG_TEST, "Successfully removed proc_kill with pid: %d\n", new_proc_kill->p_pid); + + // wait for all the threads to finish + int count = 0; + while (do_waitpid(-1, &status, 0) != -ECHILD) + { + dbg(DBG_TEST, "Waiting for child process to terminate\n"); + test_assert(status == 0, "Returned status not set correctly"); + count++; + } + test_assert(count == 2, + "Expected: %d, Actual: %d number of processes have been cleaned up\n", 2, count); + +} + +/* + Test to run several threads and processes concurrently. +*/ +void test_multiple() +{ + int num_procs_created = 0; + proc_t *new_proc1 = proc_create("proc test 1"); + kthread_t *new_kthread1 = kthread_create(new_proc1, test_func, new_proc1->p_pid, new_proc1); + num_procs_created++; + sched_make_runnable(new_kthread1); + + proc_t *new_proc2 = proc_create("proc test 2"); + kthread_t *new_kthread2 = kthread_create(new_proc2, test_func, new_proc2->p_pid, new_proc2); + num_procs_created++; + sched_make_runnable(new_kthread2); + + proc_t *new_proc3 = proc_create("proc test 3"); + kthread_t *new_kthread3 = kthread_create(new_proc3, test_func, new_proc3->p_pid, new_proc3); + num_procs_created++; + sched_make_runnable(new_kthread3); + + // print all of the threads in the system + list_iterate(&curproc->p_threads, thread, kthread_t, kt_plink) + { + dbg(DBG_THR, "Thread: %s\n", thread->kt_proc->p_name); + } + + // wait for all the threads to finish + int count = 0; + int status; + while (do_waitpid(-1, &status, 0) != -ECHILD) + { + dbg(DBG_TEST, "Waiting for child process to terminate\n"); + test_assert(status == 0, "Returned status not set correctly"); + count++; + } + test_assert(count == num_procs_created, + "Expected: %d, Actual: %d number of processes have been cleaned up\n", num_procs_created, count); +} + +/* + Test that creates several child processes and forces them to terminate out of order. + Then it checks to see if the processes are cleaned up correctly. +*/ +void *func_forever_yielding(long arg1, void *arg2) +{ + // function that always just yields + // goal is that it is cleaned up by a different process + while (1) + { + // have a way to stop the thread + int *to_stop = (int *)arg2; + if (*to_stop) + { + dbg(DBG_TEST, "Thread with pid=%d is stopping yield loop\n", curproc->p_pid); + do_exit(1); // return different status code + break; + } + // wait a bit + for (int i = 0; i < 1000000; i++) + { + ; + } + dbg(DBG_TEST, "Thread with pid=%d is yielding on it's parent\n", curproc->p_pid); + sched_yield(); + } + return NULL; +} +void test_out_of_order_termination() +{ + // create a yielding proc that will last for a long time + proc_t *new_proc1 = proc_create("yieling proc 1"); + int stop_func_1 = 0; + kthread_t *new_kthread1 = kthread_create(new_proc1, func_forever_yielding, new_proc1->p_pid, &stop_func_1); + sched_make_runnable(new_kthread1); + + proc_t *new_proc2 = proc_create("yielding proc 2"); + int stop_func_2 = 0; + kthread_t *new_kthread2 = kthread_create(new_proc2, func_forever_yielding, new_proc2->p_pid, &stop_func_2); + sched_make_runnable(new_kthread2); + + proc_t *new_proc3 = proc_create("proc test 3"); + kthread_t *new_kthread3 = kthread_create(new_proc3, test_func, new_proc3->p_pid, new_proc3); + sched_make_runnable(new_kthread3); + + proc_t *new_proc4 = proc_create("proc test 4"); + kthread_t *new_kthread4 = kthread_create(new_proc4, test_func, new_proc4->p_pid, new_proc4); + sched_make_runnable(new_kthread4); + + // let to first and second procs go and yield + sched_yield(); + + // the first and second proc should yield to the third proc and fourth proc + // which will return here to the init proc + test_assert(curproc->p_pid == PID_INIT, "Should have returned to init proc"); + test_assert(new_proc3->p_state == PROC_DEAD, "Third proc should be dead"); + test_assert(new_proc4->p_state == PROC_DEAD, "Fourth proc should be dead"); + + // destroy procs 3 and 4 + + // terminate the second proc + stop_func_2 = 1; + sched_yield(); + + // we should return back to the init proc + test_assert(curproc->p_pid == PID_INIT, "Should have returned to init proc"); + test_assert(new_proc2->p_state == PROC_DEAD, "Second proc should be dead"); + + // terminate the first proc + stop_func_1 = 1; + sched_yield(); + + // we should return back to the init proc + test_assert(curproc->p_pid == PID_INIT, "Should have returned to init proc"); + test_assert(new_proc1->p_state == PROC_DEAD, "First proc should be dead"); + + // clean up proc 2 using do_waitpid, with specific status 1 + int status; + int ret = do_waitpid(new_proc2->p_pid, &status, 0); + test_assert(ret == new_proc2->p_pid, "wrong specific process id"); + test_assert(status == 1, "Returned status not set correctly"); + test_assert(new_proc2->p_status == 1, "Status not set correctly"); + + // clean up the rest of the dead procs using do_waitpid + int count = 0; + while ((ret = do_waitpid(-1, &status, 0)) != -ECHILD) + { + dbg(DBG_TEST, "found child with pid=%d that needs to be cleaned up\n", ret); + if (ret == new_proc1->p_pid) { + test_assert(status == 1, "Returned status not set correctly for proc 1 with pid=%d", new_proc1->p_pid); + } else { + test_assert(status == 0, "Returned status not set correctly"); + } + count++; + } + test_assert(count == 3, + "Expected: %d, Actual: %d number of processes have been cleaned up\n", 3, count); +} + + long proctest_main(long arg1, void *arg2) { dbg(DBG_TEST, "\nStarting Procs tests\n"); @@ -53,6 +282,14 @@ long proctest_main(long arg1, void *arg2) // Add more tests here! // We highly recommend looking at section 3.8 on the handout for help! + dbg(DBG_TEST, "\nStarting test_multiple\n"); + test_multiple(); + dbg(DBG_TEST, "\nStarting test_do_waitpid\n"); + test_do_waitpid(); + dbg(DBG_TEST, "\nStarting test_proc_kill_all\n"); + test_proc_kill_all(); + dbg(DBG_TEST, "\nStarting test_out_of_order_termination\n"); + test_out_of_order_termination(); test_fini(); return 0; |