Allow SYS_waitpid to be aborted
authorBarret Rhoden <brho@cs.berkeley.edu>
Thu, 13 Jun 2019 20:26:53 +0000 (16:26 -0400)
committerBarret Rhoden <brho@cs.berkeley.edu>
Thu, 11 Jul 2019 18:29:21 +0000 (14:29 -0400)
This is useful for shells that want to abort a waitpid in response to a
signal.  Both busybox and bash expect their waitpid calls to return in
response to a signal.

We probably can rewrite the wait code to use rendezes instead of CVs,
and then we'd get aborting for free.  As it stands, it's not too hard to
build it in.  The should_abort() check actually covers the old 'is
dying' check, which is nice.

The ugliest thing is that wait_one and wait_any are very similar.

Signed-off-by: Barret Rhoden <brho@cs.berkeley.edu>
kern/src/syscall.c

index 66b2601..43164ec 100644 (file)
@@ -1234,20 +1234,21 @@ static pid_t wait_one(struct proc *parent, struct proc *child, int *ret_status,
                       int options)
 {
        pid_t retval;
+       struct cv_lookup_elm cle;
 
        cv_lock(&parent->child_wait);
+       __reg_abortable_cv(&cle, &parent->child_wait);
        /* retval == 0 means we should block */
        retval = __try_wait(parent, child, ret_status, options);
        if ((retval == 0) && (options & WNOHANG))
                goto out_unlock;
        while (!retval) {
-               cpu_relax();
-               cv_wait(&parent->child_wait);
-               /* If we're dying, then we don't need to worry about waiting.
-                * We don't do this yet, but we'll need this outlet when we deal
-                * with orphaned children and having init inherit them. */
-               if (proc_is_dying(parent))
+               if (should_abort(&cle)) {
+                       retval = -1;
+                       set_error(EINTR, "wait aborted");
                        goto out_unlock;
+               }
+               cv_wait(&parent->child_wait);
                /* Any child can wake us up, but we check for the particular
                 * child we care about */
                retval = __try_wait(parent, child, ret_status, options);
@@ -1259,6 +1260,7 @@ static pid_t wait_one(struct proc *parent, struct proc *child, int *ret_status,
        /* Fallthrough */
 out_unlock:
        cv_unlock(&parent->child_wait);
+       dereg_abortable_cv(&cle);
        if (retval > 0)
                proc_decref(child);
        return retval;
@@ -1271,17 +1273,21 @@ out_unlock:
 static pid_t wait_any(struct proc *parent, int *ret_status, int options)
 {
        pid_t retval;
+       struct cv_lookup_elm cle;
        struct proc *child;
 
        cv_lock(&parent->child_wait);
+       __reg_abortable_cv(&cle, &parent->child_wait);
        retval = __try_wait_any(parent, ret_status, options, &child);
        if ((retval == 0) && (options & WNOHANG))
                goto out_unlock;
        while (!retval) {
-               cpu_relax();
-               cv_wait(&parent->child_wait);
-               if (proc_is_dying(parent))
+               if (should_abort(&cle)) {
+                       retval = -1;
+                       set_error(EINTR, "wait aborted");
                        goto out_unlock;
+               }
+               cv_wait(&parent->child_wait);
                /* Any child can wake us up from the CV.  This is a linear
                 * __try_wait scan.  If we have a lot of children, we could
                 * optimize this. */
@@ -1292,6 +1298,7 @@ static pid_t wait_any(struct proc *parent, int *ret_status, int options)
        /* Fallthrough */
 out_unlock:
        cv_unlock(&parent->child_wait);
+       dereg_abortable_cv(&cle);
        if (retval > 0)
                proc_decref(child);
        return retval;