static int common_prog(int nargs, char **args) { int result; char **args_copy; #if OPT_SYNCHPROBS kprintf("Warning: this probably won't work with a " "synchronization-problems kernel.\n"); #endif /* * Implementation of & option for menu when running programs. * By default, menu will wait for user program to end before running. * If on menu command line has a '&' at the end, * it will not wait for the user program to end * before continuing to run. */ bool toDetach; toDetach = false; pid_t childpid; if (*args[nargs - 1] == '&'){ nargs--; // So we don't pass on & toDetach = true; } /* demke: Make a copy of arguments to pass to new thread, * so that we aren't depending on parent's stack! */ args_copy = copy_args(nargs, args); if (!args_copy) { return ENOMEM; } /* demke: and now call thread_fork with the copy */ result = thread_fork(args_copy[0] /* thread name */, cmd_progthread /* thread function */, args_copy /* thread arg */, nargs /* thread arg */, &childpid); if (result) { kprintf("thread_fork failed: %s\n", strerror(result)); /* demke: need to free copy of args if fork fails */ free_args(nargs, args_copy); return result; } // If toDetach, then wont wait for program to end before continuing menu. if (toDetach) { pid_detach(childpid); } // Wait for program to end before continuing // (unless it's detached, in which case join will do nothing.) pid_join(childpid, NULL, (int)NULL); return 0; }
/* * pid_exit * - sets the exit status of this thread (i.e. curthread). * - disowns children. * - if dodetach is true, children are also detached. * - wakes any thread waiting for the curthread to exit. * - frees the PID and exit status if the curthread has been detached. * - must be called only if the thread has had a pid assigned. */ void pid_exit(int status, bool dodetach) { struct pidinfo *my_pi; // Implement me. Existing code simply sets the exit status. lock_acquire(pidlock); my_pi = pi_get(curthread->t_pid); KASSERT(my_pi != NULL); my_pi->pi_exitstatus = status; my_pi->pi_exited = true; /* Disown kids */ int i; struct pidinfo *current_kid; for(i = 0; i < my_pi->kids_tail; i++){ current_kid = pi_get(my_pi->kids[i]); if(current_kid != NULL){ current_kid->pi_ppid = INVALID_PID; if(dodetach){ lock_release(pidlock); pid_detach(current_kid->pi_pid); } } } //wake people up cv_broadcast(my_pi->pi_cv, pidlock); if(my_pi->detached){ my_pi->pi_ppid = INVALID_PID; pi_drop(my_pi->pi_pid); } if(lock_do_i_hold(pidlock)){ lock_release(pidlock); } }
/* * pid_exit * - sets the exit status of this thread (i.e. curthread). * - disowns children. * - if dodetach is true, children are also detached. * - wakes any thread waiting for the curthread to exit. * - frees the PID and exit status if the curthread has been detached. * - must be called only if the thread has had a pid assigned. */ void pid_exit(int status, bool dodetach) { // Lock aquired lock_acquire(pidlock); // Get the current thread pid struct pidinfo *my_pi; my_pi = pi_get(curthread->t_pid); KASSERT(my_pi != NULL); // Set the current pid exit to true and set the exitstatus my_pi->pi_exited = true; my_pi->pi_exitstatus = status; struct pidinfo *this_pid = NULL; // If dodetach is true, then we detach every child of this process if(dodetach){ for(int i =PID_MIN; i < PID_MAX; i++){ this_pid = pi_get((pid_t)i); if((this_pid != NULL) && (this_pid->pi_ppid == my_pi->pi_pid)){ lock_release(pidlock); pid_detach(this_pid->pi_pid); // Set Parent pid to invalid after detach lock_acquire(pidlock); this_pid->detached = true; } } } // tell the waiting thread about the exit status cv_broadcast(my_pi->pi_cv, pidlock); // if the pid we called is being detached, then we clear it in the // pid list if(my_pi->detached){ my_pi->pi_ppid = INVALID_PID; pi_drop(my_pi->pi_pid); } lock_release(pidlock); }
/* * pid_exit * - sets the exit status of this thread (i.e. curthread). * - disowns children. * - if dodetach is true, children are also detached. * - wakes any thread waiting for the curthread to exit. * - frees the PID and exit status if the curthread has been detached. * - must be called only if the thread has had a pid assigned. */ void pid_exit(int status, bool dodetach) { struct pidinfo *my_pi; lock_acquire(pidlock); my_pi = pi_get(curthread->t_pid); KASSERT(my_pi != NULL); /* Thread has had a pid assigned */ KASSERT(my_pi->pi_pid == curthread->t_pid); my_pi->pi_exitstatus = status; my_pi->pi_exited = true; /* Disown children */ int i; for (i = 0; i < PROCS_MAX; i++){ if (pidinfo[i] != NULL && pidinfo[i]->pi_ppid == my_pi->pi_pid){ pidinfo[i]->pi_ppid = INVALID_PID; if (dodetach){ pid_detach(pidinfo[i]->pi_pid); } } } /* Wake up all threads waiting for curthread to exit */ if (my_pi->waitingThreads != 0){ cv_broadcast(my_pi->pi_cv, pidlock); } /* Free the PID and exit status if curthread is detached */ if (my_pi->detached){ my_pi->pi_ppid = INVALID_PID; pi_drop(my_pi->pi_pid); } lock_release(pidlock); }
int thread_detach(pid_t pid) { /* Same as thread_join, we just hand off to pid function. */ return pid_detach(pid); }
int waittest(int nargs, char **args) { int i, spl, status, err; pid_t kid; pid_t kids2[NTHREADS]; int kids2_head = 0, kids2_tail = 0; (void)nargs; (void)args; init_sem(); kprintf("Starting wait test...\n"); /* * This first set should (hopefully) still be running when * wait is called (helped by the splhigh). */ kprintf("\n"); kprintf("Set 1 (wait should generally succeed)\n"); kprintf("-------------------------------------\n"); spl = splhigh(); for (i = 0; i < NTHREADS; i++) { err = thread_fork("wait test thread", waitfirstthread, NULL, i, &kid); if (err) { panic("waittest: thread_fork failed (%d)\n", err); } kprintf("Spawned pid %d\n", kid); kids2[kids2_tail] = kid; kids2_tail = (kids2_tail+1) % NTHREADS; } splx(spl); for (i = 0; i < NTHREADS; i++) { kid = kids2[kids2_head]; kids2_head = (kids2_head+1) % NTHREADS; kprintf("Waiting on pid %d...\n", kid); err = pid_join(kid, &status, 0); if (err) { kprintf("Pid %d waitpid error %d!\n", kid, err); } else { kprintf("Pid %d exit status: %d\n", kid, status); } } /* * This second set has to V their semaphore before the exit, * so when wait is called, they will have already exited, but * their parent is still alive. */ kprintf("\n"); kprintf("Set 2 (wait should always succeed)\n"); kprintf("----------------------------------\n"); for (i = 0; i < NTHREADS; i++) { err = thread_fork("wait test thread", exitfirstthread, NULL, i, &kid); if (err) { panic("waittest: thread_fork failed (%d)\n", err); } kprintf("Spawned pid %d\n", kid); kids2[kids2_tail] = kid; kids2_tail = (kids2_tail+1) % NTHREADS; if (err) { panic("waittest: q_addtail failed (%d)\n", err); } } for (i = 0; i < NTHREADS; i++) { kid = kids2[kids2_head]; kids2_head = (kids2_head+1) % NTHREADS; kprintf("Waiting for pid %d to V()...\n", kid); P(exitsems[i]); kprintf("Appears that pid %d P()'d\n", kid); kprintf("Waiting on pid %d...\n", kid); err = pid_join(kid, &status, 0); if (err) { kprintf("Pid %d waitpid error %d!\n", kid, err); } else { kprintf("Pid %d exit status: %d\n", kid, status); } } /* * This third set has to V their semaphore before the exit, so * when wait is called, they will have already exited, and * since we've gone through and disowned them all, their exit * statuses should have been disposed of already and our waits * should all fail. */ kprintf("\n"); kprintf("Set 3 (wait should never succeed)\n"); kprintf("---------------------------------\n"); for (i = 0; i < NTHREADS; i++) { err = thread_fork("wait test thread", exitfirstthread, NULL, i, &kid); if (err) { panic("waittest: thread_fork failed (%d)\n", err); } kprintf("Spawned pid %d\n", kid); pid_detach(kid); kids2[kids2_tail] = kid; kids2_tail = (kids2_tail+1) % NTHREADS; } for (i = 0; i < NTHREADS; i++) { kid = kids2[kids2_head]; kids2_head = (kids2_head+1) % NTHREADS; kprintf("Waiting for pid %d to V()...\n", kid); P(exitsems[i]); kprintf("Appears that pid %d P()'d\n", kid); kprintf("Waiting on pid %d...\n", kid); err = pid_join(kid, &status, 0); if (err) { kprintf("Pid %d waitpid error %d!\n", kid, err); } else { kprintf("Pid %d exit status: %d\n", kid, status); } } kprintf("\nWait test done.\n"); return 0; }