Kamil Dudka 7e97ff
From 223ac53797d33b0473323efc0d5a44d1dceaf746 Mon Sep 17 00:00:00 2001
Kamil Dudka 7e97ff
From: Peter Stephenson <p.w.stephenson@ntlworld.com>
Kamil Dudka 7e97ff
Date: Sun, 26 Oct 2014 17:47:42 +0000
Kamil Dudka 7e97ff
Subject: [PATCH 1/2] 33531 with additions: retain status of exited background
Kamil Dudka 7e97ff
 jobs.
Kamil Dudka 7e97ff
Kamil Dudka 7e97ff
Add linked list of unwaited-for background jobs.
Kamil Dudka 7e97ff
Truncate at value of _SC_CHILD_MAX discarding oldest.
Kamil Dudka 7e97ff
Remove old lastpid_status mechanism for latest exited process only.
Kamil Dudka 7e97ff
Slightly tighten safety of permanently allocated linked lists so
Kamil Dudka 7e97ff
that this doesn't compromise signal handling.
Kamil Dudka 7e97ff
Kamil Dudka 7e97ff
Upstream-commit: b4f7ccecd93ca9e64c3c3c774fdaefae83d7204a
Kamil Dudka 7e97ff
Signed-off-by: Kamil Dudka <kdudka@redhat.com>
Kamil Dudka 7e97ff
---
Kamil Dudka 7e97ff
 Doc/Zsh/builtins.yo |  16 ++++++
Kamil Dudka 7e97ff
 Doc/Zsh/options.yo  |   8 +--
Kamil Dudka 7e97ff
 Src/exec.c          |   2 -
Kamil Dudka 7e97ff
 Src/init.c          |   1 -
Kamil Dudka 7e97ff
 Src/jobs.c          | 138 ++++++++++++++++++++++++++++++++++++++++++++--------
Kamil Dudka 7e97ff
 Src/linklist.c      |   4 ++
Kamil Dudka 7e97ff
 Src/signals.c       |  14 +++---
Kamil Dudka 7e97ff
 7 files changed, 148 insertions(+), 35 deletions(-)
Kamil Dudka 7e97ff
Kamil Dudka 7e97ff
diff --git a/Doc/Zsh/builtins.yo b/Doc/Zsh/builtins.yo
Kamil Dudka 7e97ff
index 46f40cc..edc335e 100644
Kamil Dudka 7e97ff
--- a/Doc/Zsh/builtins.yo
Kamil Dudka 7e97ff
+++ b/Doc/Zsh/builtins.yo
Kamil Dudka 7e97ff
@@ -2059,6 +2059,22 @@ then all currently active child processes are waited for.
Kamil Dudka 7e97ff
 Each var(job) can be either a job specification or the process ID
Kamil Dudka 7e97ff
 of a job in the job table.
Kamil Dudka 7e97ff
 The exit status from this command is that of the job waited for.
Kamil Dudka 7e97ff
+
Kamil Dudka 7e97ff
+It is possible to wait for recent processes (specified by process ID,
Kamil Dudka 7e97ff
+not by job) that were running in the background even if the process has
Kamil Dudka 7e97ff
+exited.  Typically the process ID will be recorded by capturing the
Kamil Dudka 7e97ff
+value of the variable tt($!) immediately after the process has been
Kamil Dudka 7e97ff
+started.  There is a limit on the number of process IDs remembered by
Kamil Dudka 7e97ff
+the shell; this is given by the value of the system configuration
Kamil Dudka 7e97ff
+parameter tt(CHILD_MAX).  When this limit is reached, older process IDs
Kamil Dudka 7e97ff
+are discarded, least recently started processes first.
Kamil Dudka 7e97ff
+
Kamil Dudka 7e97ff
+Note there is no protection against the process ID wrapping, i.e. if the
Kamil Dudka 7e97ff
+wait is not executed soon enough there is a chance the process waited
Kamil Dudka 7e97ff
+for is the wrong one.  A conflict implies both process IDs have been
Kamil Dudka 7e97ff
+generated by the shell, as other processes are not recorded, and that
Kamil Dudka 7e97ff
+the user is potentially interested in both, so this problem is intrinsic
Kamil Dudka 7e97ff
+to process IDs.
Kamil Dudka 7e97ff
 )
Kamil Dudka 7e97ff
 findex(whence)
Kamil Dudka 7e97ff
 item(tt(whence) [ tt(-vcwfpams) ] var(name) ...)(
Kamil Dudka 7e97ff
diff --git a/Doc/Zsh/options.yo b/Doc/Zsh/options.yo
Kamil Dudka 7e97ff
index 068a253..452b258 100644
Kamil Dudka 7e97ff
--- a/Doc/Zsh/options.yo
Kamil Dudka 7e97ff
+++ b/Doc/Zsh/options.yo
Kamil Dudka 7e97ff
@@ -1434,10 +1434,10 @@ shell is saved for output within a subshell (for example, within a
Kamil Dudka 7e97ff
 pipeline).  When the option is set, the output of tt(jobs) is empty
Kamil Dudka 7e97ff
 until a job is started within the subshell.
Kamil Dudka 7e97ff
 
Kamil Dudka 7e97ff
-When the option is set, it becomes possible to use the tt(wait) builtin to
Kamil Dudka 7e97ff
-wait for the last job started in the background (as given by tt($!)) even
Kamil Dudka 7e97ff
-if that job has already exited.  This works even if the option is turned
Kamil Dudka 7e97ff
-on temporarily around the use of the tt(wait) builtin.
Kamil Dudka 7e97ff
+In previous versions of the shell, it was necessary to enable
Kamil Dudka 7e97ff
+tt(POSIX_JOBS) in order for the builtin command tt(wait) to return the
Kamil Dudka 7e97ff
+status of background jobs that had already exited.  This is no longer
Kamil Dudka 7e97ff
+the case.
Kamil Dudka 7e97ff
 )
Kamil Dudka 7e97ff
 enditem()
Kamil Dudka 7e97ff
 
Kamil Dudka 7e97ff
diff --git a/Src/exec.c b/Src/exec.c
Kamil Dudka 7e97ff
index d0fadd6..a9c4688 100644
Kamil Dudka 7e97ff
--- a/Src/exec.c
Kamil Dudka 7e97ff
+++ b/Src/exec.c
Kamil Dudka 7e97ff
@@ -2941,8 +2941,6 @@ execcmd(Estate state, int input, int output, int how, int last1)
Kamil Dudka 7e97ff
 	    close(synch[0]);
Kamil Dudka 7e97ff
 	    if (how & Z_ASYNC) {
Kamil Dudka 7e97ff
 		lastpid = (zlong) pid;
Kamil Dudka 7e97ff
-		/* indicate it's possible to set status for lastpid */
Kamil Dudka 7e97ff
-		lastpid_status = -2L;
Kamil Dudka 7e97ff
 	    } else if (!jobtab[thisjob].stty_in_env && varspc) {
Kamil Dudka 7e97ff
 		/* search for STTY=... */
Kamil Dudka 7e97ff
 		Wordcode p = varspc;
Kamil Dudka 7e97ff
diff --git a/Src/init.c b/Src/init.c
Kamil Dudka 7e97ff
index c26d887..6666f98 100644
Kamil Dudka 7e97ff
--- a/Src/init.c
Kamil Dudka 7e97ff
+++ b/Src/init.c
Kamil Dudka 7e97ff
@@ -1036,7 +1036,6 @@ setupvals(void)
Kamil Dudka 7e97ff
     bufstack = znewlinklist();
Kamil Dudka 7e97ff
     hsubl = hsubr = NULL;
Kamil Dudka 7e97ff
     lastpid = 0;
Kamil Dudka 7e97ff
-    lastpid_status = -1L;
Kamil Dudka 7e97ff
 
Kamil Dudka 7e97ff
     get_usage();
Kamil Dudka 7e97ff
 
Kamil Dudka 7e97ff
diff --git a/Src/jobs.c b/Src/jobs.c
Kamil Dudka 7e97ff
index bd95afb..18bb648 100644
Kamil Dudka 7e97ff
--- a/Src/jobs.c
Kamil Dudka 7e97ff
+++ b/Src/jobs.c
Kamil Dudka 7e97ff
@@ -104,15 +104,6 @@ int prev_errflag, prev_breaks, errbrk_saved;
Kamil Dudka 7e97ff
 /**/
Kamil Dudka 7e97ff
 int numpipestats, pipestats[MAX_PIPESTATS];
Kamil Dudka 7e97ff
 
Kamil Dudka 7e97ff
-/*
Kamil Dudka 7e97ff
- * The status associated with the process lastpid.
Kamil Dudka 7e97ff
- * -1 if not set and no associated lastpid
Kamil Dudka 7e97ff
- * -2 if lastpid is set and status isn't yet
Kamil Dudka 7e97ff
- * else the value returned by wait().
Kamil Dudka 7e97ff
- */
Kamil Dudka 7e97ff
-/**/
Kamil Dudka 7e97ff
-long lastpid_status;
Kamil Dudka 7e97ff
-
Kamil Dudka 7e97ff
 /* Diff two timevals for elapsed-time computations */
Kamil Dudka 7e97ff
 
Kamil Dudka 7e97ff
 /**/
Kamil Dudka 7e97ff
@@ -1309,14 +1300,6 @@ addproc(pid_t pid, char *text, int aux, struct timeval *bgtime)
Kamil Dudka 7e97ff
 {
Kamil Dudka 7e97ff
     Process pn, *pnlist;
Kamil Dudka 7e97ff
 
Kamil Dudka 7e97ff
-    if (pid == lastpid && lastpid_status != -2L) {
Kamil Dudka 7e97ff
-	/*
Kamil Dudka 7e97ff
-	 * The status for the previous lastpid is invalid.
Kamil Dudka 7e97ff
-	 * Presumably process numbers have wrapped.
Kamil Dudka 7e97ff
-	 */
Kamil Dudka 7e97ff
-	lastpid_status = -1L;
Kamil Dudka 7e97ff
-    }
Kamil Dudka 7e97ff
-
Kamil Dudka 7e97ff
     DPUTS(thisjob == -1, "No valid job in addproc.");
Kamil Dudka 7e97ff
     pn = (Process) zshcalloc(sizeof *pn);
Kamil Dudka 7e97ff
     pn->pid = pid;
Kamil Dudka 7e97ff
@@ -1940,6 +1923,122 @@ maybeshrinkjobtab(void)
Kamil Dudka 7e97ff
     unqueue_signals();
Kamil Dudka 7e97ff
 }
Kamil Dudka 7e97ff
 
Kamil Dudka 7e97ff
+/*
Kamil Dudka 7e97ff
+ * Definitions for the background process stuff recorded below.
Kamil Dudka 7e97ff
+ * This would be more efficient as a hash, but
Kamil Dudka 7e97ff
+ * - that's quite heavyweight for something not needed very often
Kamil Dudka 7e97ff
+ * - we need some kind of ordering as POSIX allows us to limit
Kamil Dudka 7e97ff
+ *   the size of the list to the value of _SC_CHILD_MAX and clearly
Kamil Dudka 7e97ff
+ *   we want to clear the oldest first
Kamil Dudka 7e97ff
+ * - cases with a long list of background jobs where the user doesn't
Kamil Dudka 7e97ff
+ *   wait for a large number, and then does wait for one (the only
Kamil Dudka 7e97ff
+ *   inefficient case) are rare
Kamil Dudka 7e97ff
+ * - in the context of waiting for an external process, looping
Kamil Dudka 7e97ff
+ *   over a list isn't so very inefficient.
Kamil Dudka 7e97ff
+ * Enough excuses already.
Kamil Dudka 7e97ff
+ */
Kamil Dudka 7e97ff
+
Kamil Dudka 7e97ff
+/* Data in the link list, a key (process ID) / value (exit status) pair. */
Kamil Dudka 7e97ff
+struct bgstatus {
Kamil Dudka 7e97ff
+    pid_t pid;
Kamil Dudka 7e97ff
+    int status;
Kamil Dudka 7e97ff
+};
Kamil Dudka 7e97ff
+typedef struct bgstatus *Bgstatus;
Kamil Dudka 7e97ff
+/* The list of those entries */
Kamil Dudka 7e97ff
+LinkList bgstatus_list;
Kamil Dudka 7e97ff
+/* Count of entries.  Reaches value of _SC_CHILD_MAX and stops. */
Kamil Dudka 7e97ff
+long bgstatus_count;
Kamil Dudka 7e97ff
+
Kamil Dudka 7e97ff
+/*
Kamil Dudka 7e97ff
+ * Remove and free a bgstatus entry.
Kamil Dudka 7e97ff
+ */
Kamil Dudka 7e97ff
+static void rembgstatus(LinkNode node)
Kamil Dudka 7e97ff
+{
Kamil Dudka 7e97ff
+    zfree(remnode(bgstatus_list, node), sizeof(struct bgstatus));
Kamil Dudka 7e97ff
+    bgstatus_count--;
Kamil Dudka 7e97ff
+}
Kamil Dudka 7e97ff
+
Kamil Dudka 7e97ff
+/*
Kamil Dudka 7e97ff
+ * Record the status of a background process that exited so we
Kamil Dudka 7e97ff
+ * can execute the builtin wait for it.
Kamil Dudka 7e97ff
+ *
Kamil Dudka 7e97ff
+ * We can't execute the wait builtin for something that exited in the
Kamil Dudka 7e97ff
+ * foreground as it's not visible to the user, so don't bother recording.
Kamil Dudka 7e97ff
+ */
Kamil Dudka 7e97ff
+
Kamil Dudka 7e97ff
+/**/
Kamil Dudka 7e97ff
+void
Kamil Dudka 7e97ff
+addbgstatus(pid_t pid, int status)
Kamil Dudka 7e97ff
+{
Kamil Dudka 7e97ff
+    static long child_max;
Kamil Dudka 7e97ff
+    Bgstatus bgstatus_entry;
Kamil Dudka 7e97ff
+
Kamil Dudka 7e97ff
+    if (!child_max) {
Kamil Dudka 7e97ff
+#ifdef _SC_CHILD_MAX
Kamil Dudka 7e97ff
+	child_max = sysconf(_SC_CHILD_MAX);
Kamil Dudka 7e97ff
+	if (!child_max) /* paranoia */
Kamil Dudka 7e97ff
+#endif
Kamil Dudka 7e97ff
+	{
Kamil Dudka 7e97ff
+	    /* Be inventive */
Kamil Dudka 7e97ff
+	    child_max = 1024L;
Kamil Dudka 7e97ff
+	}
Kamil Dudka 7e97ff
+    }
Kamil Dudka 7e97ff
+
Kamil Dudka 7e97ff
+    if (!bgstatus_list) {
Kamil Dudka 7e97ff
+	bgstatus_list = znewlinklist();
Kamil Dudka 7e97ff
+	/*
Kamil Dudka 7e97ff
+	 * We're not always robust about memory failures, but
Kamil Dudka 7e97ff
+	 * this is pretty deep in the shell basics to be failing owing
Kamil Dudka 7e97ff
+	 * to memory, and a failure to wait is reported loudly, so test
Kamil Dudka 7e97ff
+	 * and fail silently here.
Kamil Dudka 7e97ff
+	 */
Kamil Dudka 7e97ff
+	if (!bgstatus_list)
Kamil Dudka 7e97ff
+	    return;
Kamil Dudka 7e97ff
+    }
Kamil Dudka 7e97ff
+    if (bgstatus_count == child_max) {
Kamil Dudka 7e97ff
+	/* Overflow.  List is in order, remove first */
Kamil Dudka 7e97ff
+	rembgstatus(firstnode(bgstatus_list));
Kamil Dudka 7e97ff
+    }
Kamil Dudka 7e97ff
+    bgstatus_entry = (Bgstatus)zalloc(sizeof(*bgstatus_entry));
Kamil Dudka 7e97ff
+    if (!bgstatus_entry) {
Kamil Dudka 7e97ff
+	/* See note above */
Kamil Dudka 7e97ff
+	return;
Kamil Dudka 7e97ff
+    }
Kamil Dudka 7e97ff
+    bgstatus_entry->pid = pid;
Kamil Dudka 7e97ff
+    bgstatus_entry->status = status;
Kamil Dudka 7e97ff
+    if (!zaddlinknode(bgstatus_list, bgstatus_entry)) {
Kamil Dudka 7e97ff
+	zfree(bgstatus_entry, sizeof(*bgstatus_entry));
Kamil Dudka 7e97ff
+	return;
Kamil Dudka 7e97ff
+    }
Kamil Dudka 7e97ff
+    bgstatus_count++;
Kamil Dudka 7e97ff
+}
Kamil Dudka 7e97ff
+
Kamil Dudka 7e97ff
+/*
Kamil Dudka 7e97ff
+ * See if pid has a recorded exit status.
Kamil Dudka 7e97ff
+ * Note we make no guarantee that the PIDs haven't wrapped, so this
Kamil Dudka 7e97ff
+ * may not be the right process.
Kamil Dudka 7e97ff
+ *
Kamil Dudka 7e97ff
+ * This is only used by wait, which must only work on each
Kamil Dudka 7e97ff
+ * pid once, so we need to remove the entry if we find it.
Kamil Dudka 7e97ff
+ */
Kamil Dudka 7e97ff
+
Kamil Dudka 7e97ff
+static int getbgstatus(pid_t pid)
Kamil Dudka 7e97ff
+{
Kamil Dudka 7e97ff
+    LinkNode node;
Kamil Dudka 7e97ff
+    Bgstatus bgstatus_entry;
Kamil Dudka 7e97ff
+
Kamil Dudka 7e97ff
+    if (!bgstatus_list)
Kamil Dudka 7e97ff
+	return -1;
Kamil Dudka 7e97ff
+    for (node = firstnode(bgstatus_list); node; incnode(node)) {
Kamil Dudka 7e97ff
+	bgstatus_entry = (Bgstatus)getdata(node);
Kamil Dudka 7e97ff
+	if (bgstatus_entry->pid == pid) {
Kamil Dudka 7e97ff
+	    int status = bgstatus_entry->status;
Kamil Dudka 7e97ff
+	    rembgstatus(node);
Kamil Dudka 7e97ff
+	    return status;
Kamil Dudka 7e97ff
+	}
Kamil Dudka 7e97ff
+    }
Kamil Dudka 7e97ff
+    return -1;
Kamil Dudka 7e97ff
+}
Kamil Dudka 7e97ff
 
Kamil Dudka 7e97ff
 /* bg, disown, fg, jobs, wait: most of the job control commands are     *
Kamil Dudka 7e97ff
  * here.  They all take the same type of argument.  Exception: wait can *
Kamil Dudka 7e97ff
@@ -2085,10 +2184,7 @@ bin_fg(char *name, char **argv, Options ops, int func)
Kamil Dudka 7e97ff
 		}
Kamil Dudka 7e97ff
 		if (retval == 0)
Kamil Dudka 7e97ff
 		    retval = lastval2;
Kamil Dudka 7e97ff
-	    } else if (isset(POSIXJOBS) &&
Kamil Dudka 7e97ff
-		       pid == lastpid && lastpid_status >= 0L) {
Kamil Dudka 7e97ff
-		retval = (int)lastpid_status;
Kamil Dudka 7e97ff
-	    } else {
Kamil Dudka 7e97ff
+	    } else if ((retval = getbgstatus(pid)) < 0) {
Kamil Dudka 7e97ff
 		zwarnnam(name, "pid %d is not a child of this shell", pid);
Kamil Dudka 7e97ff
 		/* presumably lastval2 doesn't tell us a heck of a lot? */
Kamil Dudka 7e97ff
 		retval = 1;
Kamil Dudka 7e97ff
diff --git a/Src/linklist.c b/Src/linklist.c
Kamil Dudka 7e97ff
index 1e364fb..3aa8125 100644
Kamil Dudka 7e97ff
--- a/Src/linklist.c
Kamil Dudka 7e97ff
+++ b/Src/linklist.c
Kamil Dudka 7e97ff
@@ -118,6 +118,8 @@ znewlinklist(void)
Kamil Dudka 7e97ff
     LinkList list;
Kamil Dudka 7e97ff
 
Kamil Dudka 7e97ff
     list = (LinkList) zalloc(sizeof *list);
Kamil Dudka 7e97ff
+    if (!list)
Kamil Dudka 7e97ff
+	return NULL;
Kamil Dudka 7e97ff
     list->list.first = NULL;
Kamil Dudka 7e97ff
     list->list.last = &list->node;
Kamil Dudka 7e97ff
     list->list.flags = 0;
Kamil Dudka 7e97ff
@@ -152,6 +154,8 @@ zinsertlinknode(LinkList list, LinkNode node, void *dat)
Kamil Dudka 7e97ff
 
Kamil Dudka 7e97ff
     tmp = node->next;
Kamil Dudka 7e97ff
     node->next = new = (LinkNode) zalloc(sizeof *tmp);
Kamil Dudka 7e97ff
+    if (!new)
Kamil Dudka 7e97ff
+	return NULL;
Kamil Dudka 7e97ff
     new->prev = node;
Kamil Dudka 7e97ff
     new->dat = dat;
Kamil Dudka 7e97ff
     new->next = tmp;
Kamil Dudka 7e97ff
diff --git a/Src/signals.c b/Src/signals.c
Kamil Dudka 7e97ff
index 2df69f9..e728505 100644
Kamil Dudka 7e97ff
--- a/Src/signals.c
Kamil Dudka 7e97ff
+++ b/Src/signals.c
Kamil Dudka 7e97ff
@@ -522,14 +522,14 @@ wait_for_processes(void)
Kamil Dudka 7e97ff
 	    get_usage();
Kamil Dudka 7e97ff
 	}
Kamil Dudka 7e97ff
 	/*
Kamil Dudka 7e97ff
-	 * Remember the status associated with $!, so we can
Kamil Dudka 7e97ff
-	 * wait for it even if it's exited.  This value is
Kamil Dudka 7e97ff
-	 * only used if we can't find the PID in the job table,
Kamil Dudka 7e97ff
-	 * so it doesn't matter that the value we save here isn't
Kamil Dudka 7e97ff
-	 * useful until the process has exited.
Kamil Dudka 7e97ff
+	 * Accumulate a list of older jobs.  We only do this for
Kamil Dudka 7e97ff
+	 * background jobs, which is something in the job table
Kamil Dudka 7e97ff
+	 * that's not marked as in the current shell or as shell builtin
Kamil Dudka 7e97ff
+	 * and is not equal to the current foreground job.
Kamil Dudka 7e97ff
 	 */
Kamil Dudka 7e97ff
-	if (pn != NULL && pid == lastpid && lastpid_status != -1L)
Kamil Dudka 7e97ff
-	    lastpid_status = lastval2;
Kamil Dudka 7e97ff
+	if (jn && !(jn->stat & (STAT_CURSH|STAT_BUILTIN)) &&
Kamil Dudka 7e97ff
+	    jn - jobtab != thisjob)
Kamil Dudka 7e97ff
+	    addbgstatus(pid, (int)lastval2);
Kamil Dudka 7e97ff
     }
Kamil Dudka 7e97ff
 }
Kamil Dudka 7e97ff
 
Kamil Dudka 7e97ff
-- 
Kamil Dudka 7e97ff
2.1.0
Kamil Dudka 7e97ff
Kamil Dudka 7e97ff
Kamil Dudka 7e97ff
From 2d59469450ba80b69449dc2777f0fc0673e0fbd6 Mon Sep 17 00:00:00 2001
Kamil Dudka 7e97ff
From: Peter Stephenson <p.w.stephenson@ntlworld.com>
Kamil Dudka 7e97ff
Date: Sun, 26 Oct 2014 19:04:47 +0000
Kamil Dudka 7e97ff
Subject: [PATCH 2/2] 33542: test logic for waiting for already exited
Kamil Dudka 7e97ff
 processes
Kamil Dudka 7e97ff
Kamil Dudka 7e97ff
Upstream-commit: 9a551ca85999ff329714fd2cca138ce2f7d3c3d9
Kamil Dudka 7e97ff
Signed-off-by: Kamil Dudka <kdudka@redhat.com>
Kamil Dudka 7e97ff
---
Kamil Dudka 7e97ff
 Test/A05execution.ztst | 29 +++++++++++++++++++++++++++--
Kamil Dudka 7e97ff
 1 file changed, 27 insertions(+), 2 deletions(-)
Kamil Dudka 7e97ff
Kamil Dudka 7e97ff
diff --git a/Test/A05execution.ztst b/Test/A05execution.ztst
Kamil Dudka 7e97ff
index ca97f4f..589815f 100644
Kamil Dudka 7e97ff
--- a/Test/A05execution.ztst
Kamil Dudka 7e97ff
+++ b/Test/A05execution.ztst
Kamil Dudka 7e97ff
@@ -190,9 +190,9 @@
Kamil Dudka 7e97ff
                    print "${pipestatus[@]}")
Kamil Dudka 7e97ff
 	ZTST_hashmark
Kamil Dudka 7e97ff
   done | sort | uniq -c | sed 's/^ *//'
Kamil Dudka 7e97ff
-0:Check whether `$pipestatus[]' behaves.
Kamil Dudka 7e97ff
+0:Check whether '$pipestatus[]' behaves.
Kamil Dudka 7e97ff
 >2048 2 1 0
Kamil Dudka 7e97ff
-F:This test checks for a bug in `$pipestatus[]' handling.  If it breaks then
Kamil Dudka 7e97ff
+F:This test checks for a bug in '$pipestatus[]' handling.  If it breaks then
Kamil Dudka 7e97ff
 F:the bug is still there or it reappeared. See workers-29973 for details.
Kamil Dudka 7e97ff
 
Kamil Dudka 7e97ff
   { setopt MONITOR } 2>/dev/null
Kamil Dudka 7e97ff
@@ -244,3 +244,28 @@ F:anonymous function, and a descriptor leak when backgrounding a pipeline
Kamil Dudka 7e97ff
 >autoload_redir () {
Kamil Dudka 7e97ff
 >	print Autoloaded ksh style
Kamil Dudka 7e97ff
 >} > autoload.log
Kamil Dudka 7e97ff
+
Kamil Dudka 7e97ff
+# This tests that we record the status of processes that have already exited
Kamil Dudka 7e97ff
+# for when we wait for them.
Kamil Dudka 7e97ff
+#
Kamil Dudka 7e97ff
+# Actually, we don't guarantee here that the jobs have already exited, but
Kamil Dudka 7e97ff
+# the order of the waits means it's highly likely we do need to recall a
Kamil Dudka 7e97ff
+# previous status, barring accidents which shouldn't happen very often.  In
Kamil Dudka 7e97ff
+# other words, we rely on the test working repeatedly rather than just
Kamil Dudka 7e97ff
+# once.  The monitor option is irrelevant to the logic, so we'll make
Kamil Dudka 7e97ff
+# our job easier by turning it off.
Kamil Dudka 7e97ff
+  unsetopt monitor
Kamil Dudka 7e97ff
+  (exit 1) &
Kamil Dudka 7e97ff
+  one=$!
Kamil Dudka 7e97ff
+  (exit 2) &
Kamil Dudka 7e97ff
+  two=$!
Kamil Dudka 7e97ff
+  (exit 3) &
Kamil Dudka 7e97ff
+  three=$!
Kamil Dudka 7e97ff
+  wait $three
Kamil Dudka 7e97ff
+  print $?
Kamil Dudka 7e97ff
+  wait $two
Kamil Dudka 7e97ff
+  print $?
Kamil Dudka 7e97ff
+  wait $one
Kamil Dudka 7e97ff
+1:The status of recently exited background jobs is recorded
Kamil Dudka 7e97ff
+>3
Kamil Dudka 7e97ff
+>2
Kamil Dudka 7e97ff
-- 
Kamil Dudka 7e97ff
2.1.0
Kamil Dudka 7e97ff