import (
        "fmt"
        "internal/testenv"
+       "io"
        "os"
        "os/user"
        "path/filepath"
 
 func init() {
        registerHelperCommand("pwd", cmdPwd)
-       registerHelperCommand("sleep", cmdSleep)
 }
 
 func cmdPwd(...string) {
        fmt.Println(pwd)
 }
 
-func cmdSleep(args ...string) {
-       n, err := strconv.Atoi(args[0])
-       if err != nil {
-               fmt.Println(err)
-               os.Exit(1)
-       }
-       time.Sleep(time.Duration(n) * time.Second)
-}
-
 func TestCredentialNoSetGroups(t *testing.T) {
        if runtime.GOOS == "android" {
                maySkipHelperCommand("echo")
 func TestWaitid(t *testing.T) {
        t.Parallel()
 
-       cmd := helperCommand(t, "sleep", "3")
+       cmd := helperCommand(t, "pipetest")
+       stdin, err := cmd.StdinPipe()
+       if err != nil {
+               t.Fatal(err)
+       }
+       stdout, err := cmd.StdoutPipe()
+       if err != nil {
+               t.Fatal(err)
+       }
        if err := cmd.Start(); err != nil {
                t.Fatal(err)
        }
 
-       // The sleeps here are unnecessary in the sense that the test
-       // should still pass, but they are useful to make it more
-       // likely that we are testing the expected state of the child.
-       time.Sleep(100 * time.Millisecond)
+       // Wait for the child process to come up and register any signal handlers.
+       const msg = "O:ping\n"
+       if _, err := io.WriteString(stdin, msg); err != nil {
+               t.Fatal(err)
+       }
+       buf := make([]byte, len(msg))
+       if _, err := io.ReadFull(stdout, buf); err != nil {
+               t.Fatal(err)
+       }
+       // Now leave the pipes open so that the process will hang until we close stdin.
 
        if err := cmd.Process.Signal(syscall.SIGSTOP); err != nil {
                cmd.Process.Kill()
                ch <- cmd.Wait()
        }()
 
-       time.Sleep(100 * time.Millisecond)
+       // Give a little time for Wait to block on waiting for the process.
+       // (This is just to give some time to trigger the bug; it should not be
+       // necessary for the test to pass.)
+       if testing.Short() {
+               time.Sleep(1 * time.Millisecond)
+       } else {
+               time.Sleep(10 * time.Millisecond)
+       }
 
+       // This call to Signal should succeed because the process still exists.
+       // (Prior to the fix for #19314, this would fail with os.ErrProcessDone
+       // or an equivalent error.)
        if err := cmd.Process.Signal(syscall.SIGCONT); err != nil {
                t.Error(err)
                syscall.Kill(cmd.Process.Pid, syscall.SIGCONT)
        }
 
-       cmd.Process.Kill()
-
-       <-ch
+       // The SIGCONT should allow the process to wake up, notice that stdin
+       // is closed, and exit successfully.
+       stdin.Close()
+       err = <-ch
+       if err != nil {
+               t.Fatal(err)
+       }
 }
 
 // https://go.dev/issue/50599: if Env is not set explicitly, setting Dir should