zsh-workers
 help / color / mirror / code / Atom feed
* [BUG] queueing_enabled grows infinitely when in .recursive-edit
@ 2016-10-02 19:00 Sebastian Gniazdowski
  2016-10-02 19:02 ` Sebastian Gniazdowski
  2016-10-02 23:21 ` Bart Schaefer
  0 siblings, 2 replies; 14+ messages in thread
From: Sebastian Gniazdowski @ 2016-10-02 19:00 UTC (permalink / raw)
  To: Zsh hackers list; +Cc: Bart Schaefer, Peter Stephenson

[-- Attachment #1: Type: text/plain, Size: 3098 bytes --]

Hello,
below is code from raw_getbyte() / zle_main.c:

                case ZTM_FUNC:
                    // MY DEBUG
                    _F = fopen("/tmp/recursive.txt", "a+");
                    fprintf( _F, "queueing_enabled MARK[D] (%d)\n",
queueing_enabled );
                    fclose(_F);

                    while (firstnode(timedfns)) {
                        Timedfn tfdat = (Timedfn)getdata(firstnode(timedfns));
                        /*
                         * It's possible a previous function took
                         * a long time to run (though it can't
                         * call zle recursively), so recalculate
                         * the time on each iteration.
                         */
                        time_t now = time(NULL);
                        if (tfdat->when > now)
                            break;
                        tfdat->func();
                    }
                    // MY DEBUG
                    _F = fopen("/tmp/recursive.txt", "a+");
                    fprintf( _F, "queueing_enabled MARK[C] (%d)\n",
queueing_enabled );
                    fclose(_F);

When not in .recursive-edit, log messages look like:

zlecore() - queueing_enabled (1)
getkeycmd() - queueing_enabled (1)
getkeymapcmd() - queueing_enabled (1)
getkeybuf() - queueing_enabled (1)
getbyte() - queueing_enabled (1)
raw_getbyte() - queueing_enabled (0)
...
...
queueing_enabled MARK[2] (0)
queueing_enabled MARK[D] (0)
queueing_enabled MARK[C] (0)
queueing_enabled MARK[B] (0)
...
...

Timeout is reached every second (I do sched +1 and reschedule), the
logs are produced at that rate. However, when I press Ctrl-C to invoke
a .recursive-edit widget, then:

recursiveedit() - queueing_enabled (1)
zlecore() - queueing_enabled (1)
getkeycmd() - queueing_enabled (1)
getkeymapcmd() - queueing_enabled (1)
getkeybuf() - queueing_enabled (1)
getbyte() - queueing_enabled (1)
raw_getbyte() - queueing_enabled (0)
...
...
queueing_enabled MARK[2] (0)
queueing_enabled MARK[D] (0)
queueing_enabled MARK[C] (1)
queueing_enabled MARK[B] (1)
...
...
queueing_enabled MARK[2] (1)
queueing_enabled MARK[D] (1)
queueing_enabled MARK[C] (2)
queueing_enabled MARK[B] (2)
...
...
queueing_enabled MARK[2] (2)
queueing_enabled MARK[D] (2)
queueing_enabled MARK[C] (3)
queueing_enabled MARK[B] (3)
...
...

This causes Ctrl-C signal to be queued when in .recursive-edit, what
results in need of multiple Ctrl-C presses (errflag is set in middle
of sequence of checks of it's value and raw_getbytes() executes in
weird way). Also, errflag is set at random place after select() in
raw_getbyte(), and as Bart says, this prevents scheduled function to
execute at all, thus chain of rescheduling breaks.

For completeness I attach 10 context lines diff with the debug
messages, but the crucial two are pasted above. To see the efect only
this has to be ran:

wid() { zle .recursive-edit; }
zle -N wid
bindkey '^T' wid
fun() { sched +1 fun; }
fun
^T

PS. Because of problems with ML (maybe they're over?) I send also to
Bart and Peter.

Best regards,
Sebastian Gniazdowski

[-- Attachment #2: rec6_edit.6.diff.txt --]
[-- Type: text/plain, Size: 24980 bytes --]

diff --git a/Src/Zle/zle_keymap.c b/Src/Zle/zle_keymap.c
index 3db4207..e0fe2d0 100644
--- a/Src/Zle/zle_keymap.c
+++ b/Src/Zle/zle_keymap.c
@@ -1550,20 +1550,25 @@ getrestchar_keybuf(void)
 }
 /**/
 #endif
 
 /* read a sequence of keys that is bound to some command in a keymap */
 
 /**/
 char *
 getkeymapcmd(Keymap km, Thingy *funcp, char **strp)
 {
+    // MY DEBUG
+    FILE *_F = fopen("/tmp/recursive.txt", "a+");
+    fprintf( _F, "getkeymapcmd() - queueing_enabled (%d)\n", queueing_enabled );
+    fclose(_F);
+
     Thingy func = t_undefinedkey;
     char *str = NULL;
     int lastlen = 0, lastc = lastchar;
     int timeout = 0;
 
     keybuflen = 0;
     keybuf[0] = 0;
     /*
      * getkeybuf returns multibyte strings, which may not
      * yet correspond to complete wide characters, regardless
@@ -1653,20 +1658,25 @@ addkeybuf(int c)
  * middle of a wide character.  However, I think we're OK since
  * EOF and 0xff are distinct and we're reading bytes from the
  * lower level, so EOF really does mean something went wrong.  Even so,
  * I'm worried enough to leave this note here for now.
  */
 
 /**/
 static int
 getkeybuf(int w)
 {
+    // MY DEBUG
+    FILE *_F = fopen("/tmp/recursive.txt", "a+");
+    fprintf( _F, "getkeybuf() - queueing_enabled (%d)\n", queueing_enabled );
+    fclose(_F);
+
     int c = getbyte((long)w, NULL);
 
     if(c < 0)
 	return EOF;
     addkeybuf(c);
     return c;
 }
 
 /* Push back the last command sequence read by getkeymapcmd(). *
  * Must be executed at most once after each getkeymapcmd().    */
@@ -1677,20 +1687,25 @@ ungetkeycmd(void)
 {
     ungetbytes_unmeta(keybuf, keybuflen);
 }
 
 /* read a command from the current keymap, with widgets */
 
 /**/
 mod_export Thingy
 getkeycmd(void)
 {
+    // MY DEBUG
+    FILE *_F = fopen("/tmp/recursive.txt", "a+");
+    fprintf( _F, "getkeycmd() - queueing_enabled (%d)\n", queueing_enabled );
+    fclose(_F);
+
     Thingy func;
     int hops = 0;
     char *seq, *str;
 
     sentstring:
     seq = getkeymapcmd(curkeymap, &func, &str);
     if(!*seq)
 	return NULL;
     if(!func) {
 	if (++hops == 20) {
diff --git a/Src/Zle/zle_main.c b/Src/Zle/zle_main.c
index 9a83d41..0a7933a 100644
--- a/Src/Zle/zle_main.c
+++ b/Src/Zle/zle_main.c
@@ -450,78 +450,127 @@ static void
 calc_timeout(struct ztmout *tmoutp, long do_keytmout)
 {
     if (do_keytmout && (keytimeout > 0 || do_keytmout < 0)) {
 	if (do_keytmout < 0)
 	    tmoutp->exp100ths = (time_t)-do_keytmout;
 	else if (keytimeout > ZMAXTIMEOUT * 100 /* 24 days for a keypress???? */)
 	    tmoutp->exp100ths = ZMAXTIMEOUT * 100;
 	else
 	    tmoutp->exp100ths = keytimeout;
 	tmoutp->tp = ZTM_KEY;
-    } else
+        // MY DEBUG
+        FILE *_F = fopen("/tmp/recursive.txt", "a+");
+        fprintf( _F, "-- tmoutp->tp <- ZTM_KEY (%d) / calc_timeout() zle_main.c\n", tmoutp->exp100ths );
+        fclose(_F);
+    } else {
+        // MY DEBUG
+        FILE *_F = fopen("/tmp/recursive.txt", "a+");
+        fprintf( _F, "-- tmoutp->tp <- ZTM_NONE / calc_timeout( do_keytmout: %d ), keytimeout: %d / zle_main.c\n", do_keytmout, keytimeout );
+        fclose(_F);
+
 	tmoutp->tp = ZTM_NONE;
+    }
 
     if (timedfns) {
+        // MY DEBUG
+        FILE *_F = fopen("/tmp/recursive.txt", "a+");
+        fprintf( _F, "== CALC_TIMEOUT() one more chance (timedfns exp100ths: %d)\n", tmoutp->exp100ths );
+        fclose(_F);
 	for (;;) {
 	    LinkNode tfnode = firstnode(timedfns);
 	    Timedfn tfdat;
 	    time_t diff, exp100ths;
 
-	    if (!tfnode)
+	    if (!tfnode) {
+                // MY DEBUG
+                FILE *_F = fopen("/tmp/recursive.txt", "a+");
+                fprintf( _F, "-- CALC_TIMEOUT() !tfnode break\n" );
+                fclose(_F);
 		break;
+            }
 
 	    tfdat = (Timedfn)getdata(tfnode);
 	    diff = tfdat->when - time(NULL);
+
 	    if (diff < 0) {
+                // MY DEBUG
+                FILE *_F = fopen("/tmp/recursive.txt", "a+");
+                fprintf( _F, "-- CALC_TIMEOUT() tfnode TRUE no break CALLING >> DIFF=%d <<\n", diff );
+                fclose(_F);
+
 		/* Already due; call it and rescan. */
 		tfdat->func();
 		continue;
-	    }
+	    } else {
+                // MY DEBUG
+                FILE *_F = fopen("/tmp/recursive.txt", "a+");
+                fprintf( _F, "-- CALC_TIMEOUT() tfnode TRUE no break NOT calling >> DIFF=%d <<\n", diff );
+                fclose(_F);
+            }
 
 	    if (diff > ZMAXTIMEOUT) {
+                // MY DEBUG
+                FILE *_F = fopen("/tmp/recursive.txt", "a+");
+                fprintf( _F, "-- CALC_TIMEOUT() %d > %d ZTM_MAX(%d)\n", diff, ZMAXTIMEOUT, ZTM_MAX );
+                fclose(_F);
+
 		tmoutp->exp100ths = ZMAXTIMEOUT * 100;
 		tmoutp->tp = ZTM_MAX;
-	    } else if (diff > 0) {
+	    } else if (diff >= 0) {
 		exp100ths = diff * 100;
 		if (tmoutp->tp != ZTM_KEY ||
-		    exp100ths < tmoutp->exp100ths) {
+		    exp100ths <= tmoutp->exp100ths) {
+                    // MY DEBUG
+                    FILE *_F = fopen("/tmp/recursive.txt", "a+");
+                    fprintf( _F, "-- CALC_TIMEOUT() %d != %d || %d <= %d ZTM_FUNC(%d)\n", tmoutp->tp, ZTM_KEY, exp100ths, tmoutp->exp100ths, ZTM_FUNC );
+                    fclose(_F);
+
 		    tmoutp->exp100ths = exp100ths;
 		    tmoutp->tp = ZTM_FUNC;
 		}
 	    }
 	    break;
 	}
 	/* In case we called a function which messed up the display... */
 	if (resetneeded)
 	    zrefresh();
     }
 }
 
 /* see calc_timeout for use of do_keytmout */
 
 static int
 raw_getbyte(long do_keytmout, char *cptr)
 {
+    // MY DEBUG
+    FILE *_F = fopen("/tmp/recursive.txt", "a+");
+    fprintf( _F, "raw_getbyte() - queueing_enabled (%d)\n", queueing_enabled );
+    fclose(_F);
+
     int ret;
     struct ztmout tmout;
 #if defined(HAS_TIO) && \
   (defined(sun) || (!defined(HAVE_POLL) && !defined(HAVE_SELECT)))
     struct ttyinfo ti;
 #endif
 #ifndef HAVE_POLL
 # ifdef HAVE_SELECT
     fd_set foofd, errfd;
     FD_ZERO(&errfd);
 # endif
 #endif
 
     calc_timeout(&tmout, do_keytmout);
+    // MY DEBUG
+    _F = fopen("/tmp/recursive.txt", "a+");
+    fprintf( _F, "-- INIT tmout.tp(%d) ZTM_NONE(%d) ZTM_KEY(%d) / BEGIN RAW_GETBYTE() zle_main.c\n", tmout.tp, ZTM_NONE, ZTM_KEY );
+    fclose(_F);
 
     /*
      * Handle timeouts and watched fd's.  If a watched fd or a function
      * timeout triggers we restart any key timeout.  This is likely to
      * be harmless: the combination is extremely rare and a function
      * is likely to occupy the user for a little while anyway.  We used
      * to make timeouts take precedence, but we can't now that the
      * timeouts may be external, so we may have both a permanent watched
      * fd and a long-term timeout.
      */
@@ -565,150 +614,271 @@ raw_getbyte(long do_keytmout, char *cptr)
 	/*
 	 * POLLIN, POLLIN, POLLIN,
 	 * Keep those fd's POLLIN...
 	 */
 	fds[0].events = POLLIN;
 	for (i = 0; i < nwatch; i++) {
 	    fds[i+1].fd = watch_fds[i].fd;
 	    fds[i+1].events = POLLIN;
 	}
 # endif
+            // MY DEBUG
+            _F = fopen("/tmp/recursive.txt", "a+");
+            fprintf( _F, "queueing_enabled MARK[-6] (%d)\n", queueing_enabled );
+            fclose(_F);
 	for (;;) {
+            // MY DEBUG
+            _F = fopen("/tmp/recursive.txt", "a+");
+            fprintf( _F, "queueing_enabled MARK[-5] (%d)\n", queueing_enabled );
+            fclose(_F);
 # ifdef HAVE_POLL
 	    int poll_timeout;
 
 	    if (tmout.tp != ZTM_NONE)
 		poll_timeout = tmout.exp100ths * 10;
 	    else
 		poll_timeout = -1;
 
 	    winch_unblock();
 	    selret = poll(fds, errtry ? 1 : nfds, poll_timeout);
 	    winch_block();
 # else
 	    int fdmax = SHTTY;
 	    struct timeval *tvptr;
 	    struct timeval expire_tv;
 
+            // MY DEBUG
+            _F = fopen("/tmp/recursive.txt", "a+");
+            fprintf( _F, "queueing_enabled MARK[-4] (%d)\n", queueing_enabled );
+            fclose(_F);
+
 	    FD_ZERO(&foofd);
 	    FD_SET(SHTTY, &foofd);
 	    if (!errtry) {
 		for (i = 0; i < nwatch; i++) {
 		    int fd = watch_fds[i].fd;
 		    if (FD_ISSET(fd, &errfd))
 			continue;
 		    FD_SET(fd, &foofd);
 		    if (fd > fdmax)
 			fdmax = fd;
 		}
 	    }
 	    FD_ZERO(&errfd);
 
+            // MY DEBUG
+            _F = fopen("/tmp/recursive.txt", "a+");
+            fprintf( _F, "queueing_enabled MARK[-3] (%d)\n", queueing_enabled );
+            fclose(_F);
+
 	    if (tmout.tp != ZTM_NONE) {
 		expire_tv.tv_sec = tmout.exp100ths / 100;
 		expire_tv.tv_usec = (tmout.exp100ths % 100) * 10000L;
 		tvptr = &expire_tv;
+
+                // MY DEBUG
+                FILE *_F = fopen("/tmp/recursive.txt", "a+");
+                fprintf( _F, "\n-- tmoutp != ZTM_NONE / raw_getbyte() zle_main.c\n" );
+                fclose(_F);
 	    }
-	    else
+	    else {
+                // MY DEBUG
+                FILE *_F = fopen("/tmp/recursive.txt", "a+");
+                fprintf( _F, "\n-- FINAL (STOP) tmoutp == ZTM_NONE / raw_getbyte() zle_main.c\n" );
+                fclose(_F);
+
 		tvptr = NULL;
+            }
+
+            // MY DEBUG
+            _F = fopen("/tmp/recursive.txt", "a+");
+            fprintf( _F, "queueing_enabled MARK[-2] (%d)\n", queueing_enabled );
+            fclose(_F);
 
 	    winch_unblock();
+
+            // MY DEBUG
+            _F = fopen("/tmp/recursive.txt", "a+");
+            fprintf( _F, "queueing_enabled MARK[-1] (%d)\n", queueing_enabled );
+            fclose(_F);
+
+            // MY DEBUG
+            FILE *_F = fopen("/tmp/recursive.txt", "a+");
+            fprintf( _F, "Right before select() - queueing_enabled (%d)\n", queueing_enabled );
+            fclose(_F);
+
 	    selret = select(fdmax+1, (SELECT_ARG_2_T) & foofd,
 			    NULL, NULL, tvptr);
 	    winch_block();
 # endif
+            // MY DEBUG
+            _F = fopen("/tmp/recursive.txt", "a+");
+            fprintf( _F, "queueing_enabled MARK[0] (%d)\n", queueing_enabled );
+            fclose(_F);
+
 	    /*
 	     * Make sure a user interrupt gets passed on straight away.
 	     */
-	    if (selret < 0 && (errflag || retflag || breaks || exit_pending))
+	    if (selret < 0 && (errflag || retflag || breaks || exit_pending)) {
+                // MY DEBUG
+                FILE *_F = fopen("/tmp/recursive.txt", "a+");
+                fprintf( _F, "-- Doing break [selret:%d/%d/%d] / zle_main.c: errflag: %d, retflag: %d, breaks: %d, exit_pending: %d\n",
+                            selret, EINTR, errno, errflag, retflag, breaks, exit_pending );
+                fclose(_F);
 		break;
+            } else {
+                // MY DEBUG
+                FILE *_F = fopen("/tmp/recursive.txt", "a+");
+                fprintf( _F, "-- NOT doing break selret[%d/%d/%d] / zle_main.c: errflag: %d, retflag: %d, breaks: %d, exit_pending: %d\n",
+                            selret, EINTR, errno, errflag, retflag, breaks, exit_pending );
+                fclose(_F);
+            }
+
+            // MY DEBUG
+            _F = fopen("/tmp/recursive.txt", "a+");
+            fprintf( _F, "queueing_enabled MARK[1] (%d)\n", queueing_enabled );
+            fclose(_F);
+
 	    /*
 	     * Try to avoid errors on our special fd's from
 	     * messing up reads from the terminal.  Try first
 	     * with all fds, then try unsetting the special ones.
 	     */
 	    if (selret < 0 && !errtry) {
+                // MY DEBUG
+                FILE *_F = fopen("/tmp/recursive.txt", "a+");
+                fprintf( _F, "-- Trying again selret[%d], !errtry[%d] / zle_main.c\n", selret, errtry );
+                fclose(_F);
+
 		errtry = 1;
 		continue;
-	    }
+	    } else {
+                // MY DEBUG
+                FILE *_F = fopen("/tmp/recursive.txt", "a+");
+                fprintf( _F, "--  Passed !errtry(errtry:%d) selret[%d] / zle_main.c\n", errtry, selret );
+                fclose(_F);
+            }
+
+            // MY DEBUG
+            _F = fopen("/tmp/recursive.txt", "a+");
+            fprintf( _F, "queueing_enabled MARK[2] (%d)\n", queueing_enabled );
+            fclose(_F);
+
 	    if (selret == 0) {
 		/*
 		 * Nothing ready and no error, so we timed out.
 		 */
 		switch (tmout.tp) {
 		case ZTM_NONE:
 		    /* keeps compiler happy if not debugging */
 #ifdef DEBUG
 		    dputs("BUG: timeout fired with no timeout set.");
 #endif
 		    /* treat as if a key timeout triggered */
 		    /*FALLTHROUGH*/
 		case ZTM_KEY:
 		    /* Special value -2 signals nothing ready */
 		    selret = -2;
 		    break;
 
 		case ZTM_FUNC:
+                    // MY DEBUG
+                    _F = fopen("/tmp/recursive.txt", "a+");
+                    fprintf( _F, "queueing_enabled MARK[D] (%d)\n", queueing_enabled );
+                    fclose(_F);
+                    
 		    while (firstnode(timedfns)) {
 			Timedfn tfdat = (Timedfn)getdata(firstnode(timedfns));
 			/*
 			 * It's possible a previous function took
 			 * a long time to run (though it can't
 			 * call zle recursively), so recalculate
 			 * the time on each iteration.
 			 */
 			time_t now = time(NULL);
 			if (tfdat->when > now)
 			    break;
 			tfdat->func();
 		    }
+                    // MY DEBUG
+                    _F = fopen("/tmp/recursive.txt", "a+");
+                    fprintf( _F, "queueing_enabled MARK[C] (%d)\n", queueing_enabled );
+                    fclose(_F);
+
 		    /* Function may have messed up the display */
 		    if (resetneeded)
 			zrefresh();
+
+                    // MY DEBUG
+                    _F = fopen("/tmp/recursive.txt", "a+");
+                    fprintf( _F, "queueing_enabled MARK[B] (%d)\n", queueing_enabled );
+                    fclose(_F);
 		    /* We need to recalculate the timeout */
 		    /*FALLTHROUGH*/
 		case ZTM_MAX:
 		    /*
 		     * Reached the limit of our range, but not the
 		     * actual timeout; recalculate the timeout.
 		     * We're cheating with the key timeout here:
 		     * if one clashed with a function timeout we
 		     * reconsider the key timeout from scratch.
 		     * The effect of this is microscopic.
 		     */
 		    calc_timeout(&tmout, do_keytmout);
+                    // MY DEBUG
+                    FILE *_F = fopen("/tmp/recursive.txt", "a+");
+                    fprintf( _F, "^^^ LOOP-CALLED calc_timeout: tmout.tp == %d / zle_main.c\n", tmout.tp );
+                    fclose(_F);
 		    break;
 		}
 		/*
 		 * If we handled the timeout successfully,
 		 * carry on.
 		 */
 		if (selret == 0)
 		    continue;
 	    }
+
+            // MY DEBUG
+            _F = fopen("/tmp/recursive.txt", "a+");
+            fprintf( _F, "queueing_enabled MARK[3] (%d)\n", queueing_enabled );
+            fclose(_F);
+
 	    /* If error or unhandled timeout, give up. */
 	    if (selret < 0)
 		break;
+
+            // MY DEBUG
+            _F = fopen("/tmp/recursive.txt", "a+");
+            fprintf( _F, "queueing_enabled MARK[4] (%d)\n", queueing_enabled );
+            fclose(_F);
+
 	    /*
 	     * If there's user input handle it straight away.
 	     * This improves the user's ability to handle exceptional
 	     * conditions like runaway output.
 	     */
 	    if (
 # ifdef HAVE_POLL
 		 (fds[0].revents & POLLIN)
 # else
 		 FD_ISSET(SHTTY, &foofd)
 # endif
 		 )
 		break;
 	    if (nwatch && !errtry) {
+
+                // MY DEBUG
+                _F = fopen("/tmp/recursive.txt", "a+");
+                fprintf( _F, "queueing_enabled MARK[5] (%d)\n", queueing_enabled );
+                fclose(_F);
+
 		/*
 		 * Copy the details of the watch fds in case the
 		 * user decides to delete one from inside the
 		 * handler function.
 		 */
 		int lnwatch = nwatch;
 		Watch_fd lwatch_fds = zalloc(lnwatch*sizeof(struct watch_fd));
 		memcpy(lwatch_fds, watch_fds, lnwatch*sizeof(struct watch_fd));
 		for (i = 0; i < lnwatch; i++)
 		    lwatch_fds[i].func = ztrdup(lwatch_fds[i].func);
@@ -761,27 +931,35 @@ raw_getbyte(long do_keytmout, char *cptr)
 			    /* No sensible way of handling errors here */
 			    errflag &= ~ERRFLAG_ERROR;
 			    /*
 			     * Paranoia: don't run the hooks again this
 			     * time.
 			     */
 			    errtry = 1;
 			}
 		    }
 		}
+                // MY DEBUG
+                _F = fopen("/tmp/recursive.txt", "a+");
+                fprintf( _F, "queueing_enabled MARK[6] (%d)\n", queueing_enabled );
+                fclose(_F);
 		/* Function may have invalidated the display. */
 		if (resetneeded)
 		    zrefresh();
 		for (i = 0; i < lnwatch; i++)
 		    zsfree(lwatch_fds[i].func);
 		zfree(lwatch_fds, lnwatch*sizeof(struct watch_fd));
 
+                // MY DEBUG
+                _F = fopen("/tmp/recursive.txt", "a+");
+                fprintf( _F, "queueing_enabled MARK[7] (%d)\n", queueing_enabled );
+                fclose(_F);
 # ifdef HAVE_POLL
 		/* Function may have added or removed handlers */
 		nfds = 1 + nwatch;
 		if (nfds > 1) {
 		    fds = zrealloc(fds, sizeof(struct pollfd) * nfds);
 		    for (i = 0; i < nwatch; i++) {
 			/*
 			 * This is imperfect because it assumes fds[] and
 			 * watch_fds[] remain in sync, which may be false
 			 * if handlers are shuffled.  However, it should
@@ -791,26 +969,41 @@ raw_getbyte(long do_keytmout, char *cptr)
 			if (fds[i+1].fd == watch_fds[i].fd &&
 			    (fds[i+1].revents & (POLLERR|POLLHUP|POLLNVAL))) {
 			    fds[i+1].events = 0;	/* Don't poll this */
 			} else {
 			    fds[i+1].fd = watch_fds[i].fd;
 			    fds[i+1].events = POLLIN;
 			}
 			fds[i+1].revents = 0;
 		    }
 		}
+                // MY DEBUG
+                _F = fopen("/tmp/recursive.txt", "a+");
+                fprintf( _F, "queueing_enabled MARK[8] (%d)\n", queueing_enabled );
+                fclose(_F);
 # endif
 	    }
+
+            // MY DEBUG
+            _F = fopen("/tmp/recursive.txt", "a+");
+            fprintf( _F, "queueing_enabled MARK[A] (%d)\n", queueing_enabled );
+            fclose(_F);
 	}
 # ifdef HAVE_POLL
 	zfree(fds, sizeof(struct pollfd) * nfds);
 # endif
+
+        // MY DEBUG
+        _F = fopen("/tmp/recursive.txt", "a+");
+        fprintf( _F, "queueing_enabled MARK[9] (%d)\n", queueing_enabled );
+        fclose(_F);
+
 	if (selret < 0)
 	    return selret;
 #else
 # ifdef HAS_TIO
 	ti = shttyinfo;
 	ti.tio.c_lflag &= ~ICANON;
 	ti.tio.c_cc[VMIN] = 0;
 	ti.tio.c_cc[VTIME] = tmout.exp100ths / 10;
 #  ifdef HAVE_TERMIOS_H
 	tcsetattr(SHTTY, TCSANOW, &ti.tio);
@@ -818,38 +1011,49 @@ raw_getbyte(long do_keytmout, char *cptr)
 	ioctl(SHTTY, TCSETA, &ti.tio);
 #  endif
 	winch_unblock();
 	ret = read(SHTTY, cptr, 1);
 	winch_block();
 #  ifdef HAVE_TERMIOS_H
 	tcsetattr(SHTTY, TCSANOW, &shttyinfo.tio);
 #  else
 	ioctl(SHTTY, TCSETA, &shttyinfo.tio);
 #  endif
+
+        // MY DEBUG
+        _F = fopen("/tmp/recursive.txt", "a+");
+        fprintf( _F, "queueing_enabled MARK[10] (%d)\n", queueing_enabled );
+        fclose(_F);
+
 	return (ret <= 0) ? ret : *cptr;
 # endif
 #endif
     }
 
     winch_unblock();
     ret = read(SHTTY, cptr, 1);
     winch_block();
 
     return ret;
 }
 
 /* see calc_timeout for use of do_keytmout */
 
 /**/
 mod_export int
 getbyte(long do_keytmout, int *timeout)
 {
+    // MY DEBUG
+    FILE *_F = fopen("/tmp/recursive.txt", "a+");
+    fprintf( _F, "getbyte() - queueing_enabled (%d)\n", queueing_enabled );
+    fclose(_F);
+
     char cc;
     unsigned int ret;
     int die = 0, r, icnt = 0;
     int old_errno = errno, obreaks = breaks;
 
     if (timeout)
 	*timeout = 0;
 
 #ifdef MULTIBYTE_SUPPORT
     /*
@@ -888,22 +1092,36 @@ getbyte(long do_keytmout, int *timeout)
 		   the counter (icnt) so that this happens 20 times and than
 		   the shell gives up (yes, this is a bit dirty...). */
 		if ((zlereadflags & ZLRF_IGNOREEOF) && icnt++ < 20)
 		    continue;
 		stopmsg = 1;
 		zexit(1, 0);
 	    }
 	    icnt = 0;
 	    if (errno == EINTR) {
 		die = 0;
+                static int counter = 0;
+
+                // MY DEBUG
+                FILE *_F = fopen("/tmp/recursive.txt", "a+");
+                fprintf( _F, "-- Got EINTR %d\n", ++counter );
+                fclose(_F);
+
 		if (!errflag && !retflag && !breaks && !exit_pending)
+                {
+                    // MY DEBUG
+                    FILE *_F = fopen("/tmp/recursive.txt", "a+");
+                    fprintf( _F, "-- Continuing despite EINTR / zle_main.c: errflag: %d, retflag: %d, breaks: %d, exit_pending: %d\n",
+                                errflag, retflag, breaks, exit_pending );
+                    fclose(_F);
 		    continue;
+                }
 		errflag &= ~ERRFLAG_ERROR;
 		breaks = obreaks;
 		errno = old_errno;
 		return lastchar = EOF;
 	    } else if (errno == EWOULDBLOCK) {
 		fcntl(0, F_SETFL, 0);
 	    } else if (errno == EIO && !die) {
 		ret = opts[MONITOR];
 		opts[MONITOR] = 1;
 		attachtty(mypgrp);
@@ -1072,20 +1290,25 @@ void
 zlecore(void)
 {
     Keymap km;
 #if !defined(HAVE_POLL) && defined(HAVE_SELECT)
     struct timeval tv;
     fd_set foofd;
 
     FD_ZERO(&foofd);
 #endif
 
+    // MY DEBUG
+    FILE *_F = fopen("/tmp/recursive.txt", "a+");
+    fprintf( _F, "zlecore() - queueing_enabled (%d)\n", queueing_enabled );
+    fclose(_F);
+
     pushheap();
 
     /*
      * A widget function may decide to exit the shell.
      * We never exit directly from functions, to allow
      * the shell to tidy up, so we have to test for
      * that explicitly.
      */
     while (!done && !errflag && !exit_pending) {
 	UNMETACHECK();
@@ -1115,20 +1338,24 @@ zlecore(void)
 		if (eofsent)
 		    break;
 	    }
 	    handleprefixes();
 	    /* for vi mode, make sure the cursor isn't somewhere illegal */
 	    if (invicmdmode() && zlecs > findbol() &&
 		(zlecs == zlell || zleline[zlecs] == ZWC('\n')))
 		DECCS();
 	    handleundo();
 	} else {
+            // MY DEBUG
+            FILE *_F = fopen("/tmp/recursive.txt", "a+");
+            fprintf( _F, "-- Setting error in zlecore.c\n" );
+            fclose(_F);
 	    errflag |= ERRFLAG_ERROR;
 	    break;
 	}
 
 	redrawhook();
 #ifdef HAVE_POLL
 	if (baud && !(lastcmd & ZLE_MENUCMP)) {
 	    struct pollfd pfd;
 	    int to = cost * costmult / 1000; /* milliseconds */
 
@@ -1847,20 +2074,25 @@ whereis(UNUSED(char **args))
 	ff.msg = appstr(ff.msg, " et al");
     showmsg(ff.msg);
     zsfree(ff.msg);
     return 0;
 }
 
 /**/
 int
 recursiveedit(UNUSED(char **args))
 {
+    // MY DEBUG
+    FILE *_F = fopen("/tmp/recursive.txt", "a+");
+    fprintf( _F, "recursiveedit() - queueing_enabled (%d)\n", queueing_enabled );
+    fclose(_F);
+
     int locerror;
 
     redrawhook();
     zrefresh();
     zlecore();
 
     locerror = errflag ? 1 : 0;
     errflag = done = eofsent = 0;
 
     return locerror;
diff --git a/Src/signals.c b/Src/signals.c
index e2587dc..8a53f87 100644
--- a/Src/signals.c
+++ b/Src/signals.c
@@ -555,20 +555,23 @@ wait_for_processes(void)
 	unqueue_signals();
     }
 }
 
 /* the signal handler */
  
 /**/
 mod_export RETSIGTYPE
 zhandler(int sig)
 {
+    FILE *_F = fopen("/tmp/recursive.txt", "a+");
+    fprintf( _F, "## zhandler(%d) signals.c [queueing_enabled:%d]\n", sig, queueing_enabled );
+    fclose( _F );
     sigset_t newmask, oldmask;
 
 #if defined(NO_SIGNAL_BLOCKING)
     int do_jump;
     signal_jmp_buf jump_to;
 #endif
  
     last_signal = sig;
     signal_process(sig);
  
@@ -599,20 +602,24 @@ zhandler(int sig)
 	/* Make sure it's not full (extremely unlikely) */
         if (temp_rear != queue_front) {
 	    /* ok, not full, so add to queue   */
             queue_rear = temp_rear;
 	    /* save signal caught              */
             signal_queue[queue_rear] = sig;
 	    /* save current signal mask        */
             signal_mask_queue[queue_rear] = oldmask;
         }
         signal_reset(sig);
+
+        FILE *_F = fopen("/tmp/recursive.txt", "a+");
+        fprintf( _F, "## zhandler(%d) EXIT 1 signals.c\n", sig );
+        fclose( _F );
         return;
     }
  
     /* Reset signal mask, signal traps ok now */
     signal_setmask(oldmask);
  
     switch (sig) {
     case SIGCHLD:
 	wait_for_processes();
         break;
@@ -638,20 +645,25 @@ zhandler(int sig)
     case SIGINT:
         if (!handletrap(SIGINT)) {
 	    if ((isset(PRIVILEGED) || isset(RESTRICTED)) &&
 		isset(INTERACTIVE) && noerrexit < 0)
 		zexit(SIGINT, 1);
             if (list_pipe || chline || simple_pline) {
                 breaks = loops;
                 errflag |= ERRFLAG_INT;
 		inerrflush();
 		check_cursh_sig(SIGINT);
+
+                // MY DEBUG
+                FILE *_F = fopen("/tmp/recursive.txt", "a+");
+                fprintf( _F, "## set errflag to %d (ERRFLAG_INT:%d) / signals.c\n", errflag, ERRFLAG_INT );
+                fclose(_F);
             }
 	    lastval = 128 + SIGINT;
         }
         break;
 
 #ifdef SIGWINCH
     case SIGWINCH:
         adjustwinsize(1);  /* check window size and adjust */
 	(void) handletrap(SIGWINCH);
         break;

^ permalink raw reply	[flat|nested] 14+ messages in thread

* Re: [BUG] queueing_enabled grows infinitely when in .recursive-edit
  2016-10-02 19:00 [BUG] queueing_enabled grows infinitely when in .recursive-edit Sebastian Gniazdowski
@ 2016-10-02 19:02 ` Sebastian Gniazdowski
  2016-10-02 23:21 ` Bart Schaefer
  1 sibling, 0 replies; 14+ messages in thread
From: Sebastian Gniazdowski @ 2016-10-02 19:02 UTC (permalink / raw)
  To: Zsh hackers list; +Cc: Bart Schaefer, Peter Stephenson

Sorry:

"However, when I press Ctrl-C to invoke"

should be:

"However, when I press Ctrl-R to invoke"


^ permalink raw reply	[flat|nested] 14+ messages in thread

* Re: [BUG] queueing_enabled grows infinitely when in .recursive-edit
  2016-10-02 19:00 [BUG] queueing_enabled grows infinitely when in .recursive-edit Sebastian Gniazdowski
  2016-10-02 19:02 ` Sebastian Gniazdowski
@ 2016-10-02 23:21 ` Bart Schaefer
  2016-10-03 10:00   ` Sebastian Gniazdowski
                     ` (2 more replies)
  1 sibling, 3 replies; 14+ messages in thread
From: Bart Schaefer @ 2016-10-02 23:21 UTC (permalink / raw)
  To: Zsh hackers list

On Oct 2,  9:00pm, Sebastian Gniazdowski wrote:
}
} ...
} 
} This causes Ctrl-C signal to be queued when in .recursive-edit, what
} results in need of multiple Ctrl-C presses (errflag is set in middle
} of sequence of checks of it's value and raw_getbytes() executes in
} weird way). Also, errflag is set at random place after select() in
} raw_getbyte(), and as Bart says, this prevents scheduled function to
} execute at all, thus chain of rescheduling breaks.

There seem to be a bunch of inter-related things going on here.

The first is that recursiveedit() calls zlecore() which calls
getkeycmd() which cascades into raw_getbyte() with do_keytmout = 0
which in some circumstances means that raw_getbyte() effectively
does a blocking read on its first call and only runs the sched
after a key is pressed.  I haven't figured out what's wrong with
the calc_timeout() logic that makes this possible, but it's a
race -- it happens only once in a while.

The second is that zlecore() expects to be entered with the signal
queue disabled, but zle widgets are called with queueing enabled, so
recursiveedit() needs save/zero/restore the queue level around the
call to zlecore().

The third is that somewhere below execstring() from checksched(),
the signal queueing level is being incremented but not decremented.
The problem is that the execution code calls itself recursively so
deeply that I can't pinpoint the place this occurs.  Even with a
watchpoint on queueing_enabled, it looks as though we should be
fine -- it's as if the recursive calls never quite unwind all the
way back to the top, but printing stack traces in gdb doesn't show
that happening at any place where queueing_enabled changes.

Usually the third effect is hidden by the restore_queue_signals()
in getbyte(), but when in recursiveedit() the queue_signal_level()
in raw_getbyte() starts out > 0, and never goes all the way back
down again.  Or something like that.  I expected fixing the second
problem to again mask the third, but it does not.

It's almost like there's a tail-call optimization occuring that is
causing an unqueue_signals() to be skipped.  And in fact there are
cases where the queueing_enabled is decremented but the watchpoint
mysteriously does not trigger -- I see the smaller "old" value the
next time the watchpoint triggers on the *increment*, but I never
see the assignment that reduces the value.  I think this is because
gdb actually stops on the NEXT instruction AFTER the watched location,
and there are some places where there is no breakable next line (the
unqueue_signals() is the last line of a function).

It's quite normal for queueing_enabled to run up to 12 or so on a
normal execution stack and still decrement all the way back to zero,
so watching for a high-water mark is nearly useless.

Here's a patch (not to be committed) that causes a DPUTS() when
the condition is detected, but that doesn't help with tracking down
what causes it in the first place.

diff --git a/Src/Zle/zle_main.c b/Src/Zle/zle_main.c
index 0bdd82b..ba7ef90 100644
--- a/Src/Zle/zle_main.c
+++ b/Src/Zle/zle_main.c
@@ -1867,11 +1867,17 @@ int
 recursiveedit(UNUSED(char **args))
 {
     int locerror;
+    int q = queue_signal_level();
+
+    /* zlecore() expects to be entered with signal queue disabled */
+    dont_queue_signals();
 
     redrawhook();
     zrefresh();
     zlecore();
 
+    restore_queue_signals(q);
+
     locerror = errflag ? 1 : 0;
     errflag = done = eofsent = 0;
 
diff --git a/Src/signals.c b/Src/signals.c
index e2587dc..9b22dcd 100644
--- a/Src/signals.c
+++ b/Src/signals.c
@@ -67,7 +67,7 @@ static int exit_trap_posix;
 /* Variables used by signal queueing */
 
 /**/
-mod_export int queueing_enabled, queue_front, queue_rear;
+mod_export int queueing_enabled, queue_front, queue_rear, queue_in;
 /**/
 mod_export int signal_queue[MAX_QUEUE_SIZE];
 /**/
diff --git a/Src/signals.h b/Src/signals.h
index d680968..cb6a171 100644
--- a/Src/signals.h
+++ b/Src/signals.h
@@ -82,7 +82,7 @@
 
 #define MAX_QUEUE_SIZE 128
 
-#define queue_signals()    (queueing_enabled++)
+#define queue_signals()    (queue_in++, queueing_enabled++)
 
 #define run_queued_signals() do { \
     while (queue_front != queue_rear) {      /* while signals in queue */ \
@@ -96,17 +96,23 @@
 
 #define unqueue_signals()  do { \
     DPUTS(!queueing_enabled, "BUG: unqueue_signals called but not queueing"); \
+    --queue_in; \
     if (!--queueing_enabled) run_queued_signals(); \
 } while (0)
 
 #define queue_signal_level() queueing_enabled
 
 #define dont_queue_signals() do { \
+    queue_in = queueing_enabled; \
     queueing_enabled = 0; \
     run_queued_signals(); \
 } while (0)
 
-#define restore_queue_signals(q) (queueing_enabled = (q))
+#define restore_queue_signals(q) do { \
+    DPUTS2(queueing_enabled && queue_in != q, \
+         "BUG: q = %d != queue_in = %d", q, queue_in); \
+    queue_in = (queueing_enabled = (q)); \
+} while (0)
 
 #ifdef BSD_SIGNALS
 #define signal_block(S) sigblock(S)


^ permalink raw reply	[flat|nested] 14+ messages in thread

* Re: [BUG] queueing_enabled grows infinitely when in .recursive-edit
  2016-10-02 23:21 ` Bart Schaefer
@ 2016-10-03 10:00   ` Sebastian Gniazdowski
  2016-10-03 10:18   ` Peter Stephenson
  2016-10-03 16:33   ` Bart Schaefer
  2 siblings, 0 replies; 14+ messages in thread
From: Sebastian Gniazdowski @ 2016-10-03 10:00 UTC (permalink / raw)
  To: Bart Schaefer; +Cc: Zsh hackers list

[-- Attachment #1: Type: text/plain, Size: 3293 bytes --]

On 3 October 2016 at 01:21, Bart Schaefer <schaefer@brasslantern.com> wrote:
> There seem to be a bunch of inter-related things going on here.
>
> The first is that recursiveedit() calls zlecore() which calls
> getkeycmd() which cascades into raw_getbyte() with do_keytmout = 0
> which in some circumstances means that raw_getbyte() effectively
> does a blocking read on its first call and only runs the sched
> after a key is pressed.  I haven't figured out what's wrong with
> the calc_timeout() logic that makes this possible, but it's a
> race -- it happens only once in a while.

It could be either way, if this is to be resolved. I mean, stopping
sched when in .recursive-edit has some, if not total sense. It's like
if user would be in an application.

> The second is that zlecore() expects to be entered with the signal
> queue disabled, but zle widgets are called with queueing enabled, so
> recursiveedit() needs save/zero/restore the queue level around the
> call to zlecore().
>
> The third is that somewhere below execstring() from checksched(),
> the signal queueing level is being incremented but not decremented.
> The problem is that the execution code calls itself recursively so
> deeply that I can't pinpoint the place this occurs.  Even with a
> watchpoint on queueing_enabled, it looks as though we should be
> fine -- it's as if the recursive calls never quite unwind all the
> way back to the top, but printing stack traces in gdb doesn't show
> that happening at any place where queueing_enabled changes.
>
> Usually the third effect is hidden by the restore_queue_signals()
> in getbyte(), but when in recursiveedit() the queue_signal_level()
> in raw_getbyte() starts out > 0, and never goes all the way back
> down again.  Or something like that.  I expected fixing the second
> problem to again mask the third, but it does not.
>
> It's almost like there's a tail-call optimization occuring that is
> causing an unqueue_signals() to be skipped.  And in fact there are
> cases where the queueing_enabled is decremented but the watchpoint
> mysteriously does not trigger -- I see the smaller "old" value the
> next time the watchpoint triggers on the *increment*, but I never
> see the assignment that reduces the value.  I think this is because
> gdb actually stops on the NEXT instruction AFTER the watched location,
> and there are some places where there is no breakable next line (the
> unqueue_signals() is the last line of a function).
>
> It's quite normal for queueing_enabled to run up to 12 or so on a
> normal execution stack and still decrement all the way back to zero,
> so watching for a high-water mark is nearly useless.

Early this morning I read this text like if watchpoints didn't work
and now did whole path from recursiveedit to second raw_getbyte()
call. In lldb watchpoitns worked apparently always correctly, maybe
because I have no optimizations. Forgot to set breakpoint on
raw_getbyte() and ended in going multiple steps "repeated", but maybe
that's a luck, because vim/:setscrollbind shows nicely where
repetition starts and that queueing_enabled is off by 1:

https://asciinema.org/a/2c4p285gujuv0850uinrmj2le

The first common entry is #8. I submit and now start to think about
what's gathered.

Best regards,
Sebastian Gniazdowski

[-- Attachment #2: watch2.c.txt --]
[-- Type: text/plain, Size: 52956 bytes --]

Process 19127 stopped
* thread #1: tid = 0x2cbd2c, 0x000000010f28b3ba zle.so`recursiveedit(args=0x000000010f38b0a0) + 26 at zle_main.c:1916, queue = 'com.apple.main-thread', stop reason = breakpoint 1.1
    frame #0: 0x000000010f28b3ba zle.so`recursiveedit(args=0x000000010f38b0a0) + 26 at zle_main.c:1916
   1913 recursiveedit(UNUSED(char **args))
   1914 {
   1915     // MY DEBUG
-> 1916     FILE *_F = fopen("/tmp/recursive.txt", "a+");
   1917     fprintf( _F, "recursiveedit() - queueing_enabled (%d)\n", queueing_enabled );
   1918     fclose(_F);
   1919
(lldb) p queueing_enabled
(int) $0 = 1
(lldb) watchpoint set variable queueing_enabled                                                                                Watchpoint created: Watchpoint 1: addr = 0x10f016e28 size = 4 state = enabled type = w
    watchpoint spec = 'queueing_enabled'
    new value: 1
(lldb) c

, stop reason = watchpoint 1
    frame #0: 0x000000010efa513d zsh-5.2-dev-2`zalloc(size=6) + 45 at mem.c:956
   953      if (!size)
   954          size = 1;
   955      queue_signals();
-> 956      if (!(ptr = (void *) malloc(size))) {
   957          zerr("fatal error: out of memory");
   958          exit(1);
   959      }
(lldb) p queueing_enabled
(int) $1 = 2

    frame #0: 0x000000010efa51a2 zsh-5.2-dev-2`zalloc(size=6) + 146 at mem.c:960
   957          zerr("fatal error: out of memory");
   958          exit(1);
   959      }
-> 960      unqueue_signals();
   961
   962      return ptr;
   963  }
(lldb) p queueing_enabled
(int) $2 = 1

    frame #0: 0x000000010efa4f96 zsh-5.2-dev-2`pushheap + 22 at mem.c:302
   299      h_push++;
   300  #endif
   301
-> 302      for (h = heaps; h; h = h->next) {
   303          DPUTS(!h->used && h->next, "BUG: empty heap");
   304          hs = (Heapstack) zalloc(sizeof(*hs));
   305          hs->next = h->sp;
(lldb) p queueing_enabled
(int) $3 = 2

    frame #0: 0x000000010efa513d zsh-5.2-dev-2`zalloc(size=16) + 45 at mem.c:956
   953      if (!size)
   954          size = 1;
   955      queue_signals();
-> 956      if (!(ptr = (void *) malloc(size))) {
   957          zerr("fatal error: out of memory");
   958          exit(1);
   959      }
(lldb) p queueing_enabled
(int) $4 = 3

    frame #0: 0x000000010efa51a2 zsh-5.2-dev-2`zalloc(size=16) + 146 at mem.c:960
   957          zerr("fatal error: out of memory");
   958          exit(1);
   959      }
-> 960      unqueue_signals();
   961
   962      return ptr;
   963  }
(lldb) p queueing_enabled
(int) $5 = 2

    frame #0: 0x000000010efa505d zsh-5.2-dev-2`pushheap + 221 at mem.c:318
   315          }
   316  #endif
   317      }
-> 318      unqueue_signals();
   319  }
   320
   321  /* reset heaps to previous state */
(lldb) p queueing_enabled
(int) $6 = 1

    frame #0: 0x000000010f2887ed zle.so`getbyte(do_keytmout=0, timeout=0x0000000000000000) + 237 at zle_main.c:903
   900      else {
   901          for (;;) {
   902              int q = queue_signal_level();
-> 903              dont_queue_signals();
   904              r = raw_getbyte(do_keytmout, &cc);
   905              restore_queue_signals(q);
   906              if (r == -2) {
(lldb) p queueing_enabled
(int) $7 = 0

    frame #0: 0x000000010efa4f96 zsh-5.2-dev-2`pushheap + 22 at mem.c:302
   299      h_push++;
   300  #endif
   301
-> 302      for (h = heaps; h; h = h->next) {
   303          DPUTS(!h->used && h->next, "BUG: empty heap");
   304          hs = (Heapstack) zalloc(sizeof(*hs));
   305          hs->next = h->sp;
(lldb) p queueing_enabled
(int) $8 = 1

    frame #0: 0x000000010efa513d zsh-5.2-dev-2`zalloc(size=16) + 45 at mem.c:956
   953      if (!size)
   954          size = 1;
   955      queue_signals();
-> 956      if (!(ptr = (void *) malloc(size))) {
   957          zerr("fatal error: out of memory");
   958          exit(1);
   959      }
(lldb) p queueing_enabled
(int) $9 = 2

    frame #0: 0x000000010efa51a2 zsh-5.2-dev-2`zalloc(size=16) + 146 at mem.c:960
   957          zerr("fatal error: out of memory");
   958          exit(1);
   959      }
-> 960      unqueue_signals();
   961
   962      return ptr;
   963  }
(lldb) p queueing_enabled
(int) $10 = 1

    frame #0: 0x000000010efa505d zsh-5.2-dev-2`pushheap + 221 at mem.c:318
   315          }
   316  #endif
   317      }
-> 318      unqueue_signals();
   319  }
   320
   321  /* reset heaps to previous state */
(lldb) p queueing_enabled
(int) $11 = 0

    frame #0: 0x000000010ef60200 zsh-5.2-dev-2`zcontext_save_partial(parts=7) + 32 at context.c:58
   55
   56       queue_signals();
   57
-> 58       cs = (struct context_stack *)malloc(sizeof(struct context_stack));
   59
   60       if (parts & ZCONTEXT_HIST) {
   61           hist_context_save(&cs->hist_stack, !cstack);
(lldb) p queueing_enabled
(int) $12 = 1

    frame #0: 0x000000010efa513d zsh-5.2-dev-2`zalloc(size=256) + 45 at mem.c:956
   953      if (!size)
   954          size = 1;
   955      queue_signals();
-> 956      if (!(ptr = (void *) malloc(size))) {
   957          zerr("fatal error: out of memory");
   958          exit(1);
   959      }
(lldb) p queueing_enabled
(int) $13 = 2

    frame #0: 0x000000010efa51a2 zsh-5.2-dev-2`zalloc(size=256) + 146 at mem.c:960
   957          zerr("fatal error: out of memory");
   958          exit(1);
   959      }
-> 960      unqueue_signals();
   961
   962      return ptr;
   963  }
(lldb) p queueing_enabled
(int) $14 = 1

    frame #0: 0x000000010ef602f1 zsh-5.2-dev-2`zcontext_save_partial(parts=7) + 273 at context.c:73
   70       cs->next = cstack;
   71       cstack = cs;
   72
-> 73       unqueue_signals();
   74   }
   75
   76   /* save context in full */
(lldb) p queueing_enabled
(int) $15 = 0

    frame #0: 0x000000010ef90974 zsh-5.2-dev-2`inputsetline(str="fun", flags=64) + 36 at input.c:386
   383  {
   384      queue_signals();
   385
-> 386      if ((inbufflags & INP_FREE) && inbuf) {
   387          free(inbuf);
   388      }
   389      inbuf = inbufptr = str;
(lldb) p queueing_enabled
(int) $16 = 1

    frame #0: 0x000000010ef90a33 zsh-5.2-dev-2`inputsetline(str="fun", flags=64) + 227 at input.c:404
   401          inbufct = inbufleft;
   402      inbufflags = flags;
   403
-> 404      unqueue_signals();
   405  }
   406
(lldb) p queueing_enabled
(int) $17 = 0

   frame #0: 0x000000010efbe9ed zsh-5.2-dev-2`init_parse + 29 at parse.c:469
   466  {
   467      queue_signals();
   468
-> 469      if (ecbuf) zfree(ecbuf, eclen);
   470
   471      ecbuf = (Wordcode) zalloc((eclen = EC_INIT_SIZE) * sizeof(wordcode));
   472      ecused = 0;
(lldb) p queueing_enabled
(int) $18 = 1

    frame #0: 0x000000010efa513d zsh-5.2-dev-2`zalloc(size=1024) + 45 at mem.c:956
   953      if (!size)
   954          size = 1;
   955      queue_signals();
-> 956      if (!(ptr = (void *) malloc(size))) {
   957          zerr("fatal error: out of memory");
   958          exit(1);
   959      }
(lldb) p queueing_enabled
(int) $19 = 2

    frame #0: 0x000000010efa51a2 zsh-5.2-dev-2`zalloc(size=1024) + 146 at mem.c:960
   957          zerr("fatal error: out of memory");
   958          exit(1);
   959      }
-> 960      unqueue_signals();
   961
   962      return ptr;
   963  }
(lldb) p queueing_enabled
(int) $20 = 1

    frame #0: 0x000000010efbeabb zsh-5.2-dev-2`init_parse + 235 at parse.c:480
   477
   478      init_parse_status();
   479
-> 480      unqueue_signals();
   481  }
   482
   483  /* Build eprog. */
(lldb) p queueing_enabled
(int) $21 = 0

    frame #0: 0x000000010efa5a96 zsh-5.2-dev-2`zhalloc(size=32) + 54 at mem.c:604
   601       * but we think that nothing upstream of fheap has more free space,
   602       * so why start over at heaps just because fheap has too little?
   603       */
-> 604      for (h = (fheap ? fheap : heaps); h; h = h->next) {
   605          hp = h;
   606          if (ARENA_SIZEOF(h) >= (n = size + h->used)) {
   607              void *ret;
(lldb) p queueing_enabled
(int) $22 = 1

    frame #0: 0x000000010efa5b57 zsh-5.2-dev-2`zhalloc(size=32) + 247 at mem.c:611
   608
   609              h->used = n;
   610              ret = arena(h) + n - size;
-> 611              unqueue_signals();
   612  #ifdef ZSH_HEAP_DEBUG
   613              last_heap_id = h->heap_id;
   614              if (heap_debug_verbosity & HDV_ALLOC) {
(lldb) p queueing_enabled
(int) $23 = 0

    frame #0: 0x000000010efa5a96 zsh-5.2-dev-2`zhalloc(size=8) + 54 at mem.c:604
   601       * but we think that nothing upstream of fheap has more free space,
   602       * so why start over at heaps just because fheap has too little?
   603       */
-> 604      for (h = (fheap ? fheap : heaps); h; h = h->next) {
   605          hp = h;
   606          if (ARENA_SIZEOF(h) >= (n = size + h->used)) {
   607              void *ret;
(lldb) p queueing_enabled
(int) $24 = 1

    frame #0: 0x000000010efa5b57 zsh-5.2-dev-2`zhalloc(size=8) + 247 at mem.c:611
   608
   609              h->used = n;
   610              ret = arena(h) + n - size;
-> 611              unqueue_signals();
   612  #ifdef ZSH_HEAP_DEBUG
   613              last_heap_id = h->heap_id;
   614              if (heap_debug_verbosity & HDV_ALLOC) {
(lldb) p queueing_enabled
(int) $25 = 0

    frame #0: 0x000000010efbef8b zsh-5.2-dev-2`bld_eprog(heap=1) + 27 at parse.c:505
   502
   503      queue_signals();
   504
-> 505      ecadd(WCB_END());
   506
   507      ret = heap ? (Eprog) zhalloc(sizeof(*ret)) : (Eprog) zalloc(sizeof(*ret));
   508      ret->len = ((ecnpats * sizeof(Patprog)) +
(lldb) p queueing_enabled
(int) $26 = 1

    frame #0: 0x000000010efa5a96 zsh-5.2-dev-2`zhalloc(size=56) + 54 at mem.c:604
   601       * but we think that nothing upstream of fheap has more free space,
   602       * so why start over at heaps just because fheap has too little?
   603       */
-> 604      for (h = (fheap ? fheap : heaps); h; h = h->next) {
   605          hp = h;
   606          if (ARENA_SIZEOF(h) >= (n = size + h->used)) {
   607              void *ret;
(lldb) p queueing_enabled
(int) $27 = 2

    frame #0: 0x000000010efa5b57 zsh-5.2-dev-2`zhalloc(size=56) + 247 at mem.c:611
   608
   609              h->used = n;
   610              ret = arena(h) + n - size;
-> 611              unqueue_signals();
   612  #ifdef ZSH_HEAP_DEBUG
   613              last_heap_id = h->heap_id;
   614              if (heap_debug_verbosity & HDV_ALLOC) {
(lldb) p queueing_enabled
(int) $28 = 1

    frame #0: 0x000000010efa5a96 zsh-5.2-dev-2`zhalloc(size=24) + 54 at mem.c:604
   601       * but we think that nothing upstream of fheap has more free space,
   602       * so why start over at heaps just because fheap has too little?
   603       */
-> 604      for (h = (fheap ? fheap : heaps); h; h = h->next) {
   605          hp = h;
   606          if (ARENA_SIZEOF(h) >= (n = size + h->used)) {
   607              void *ret;
(lldb) p queueing_enabled
(int) $29 = 2

    frame #0: 0x000000010efa5b57 zsh-5.2-dev-2`zhalloc(size=24) + 247 at mem.c:611
   608
   609              h->used = n;
   610              ret = arena(h) + n - size;
-> 611              unqueue_signals();
   612  #ifdef ZSH_HEAP_DEBUG
   613              last_heap_id = h->heap_id;
   614              if (heap_debug_verbosity & HDV_ALLOC) {
(lldb) p queueing_enabled
(int) $30 = 1

    frame #0: 0x000000010efbf1bc zsh-5.2-dev-2`bld_eprog(heap=1) + 588 at parse.c:528
   525      zfree(ecbuf, eclen);
   526      ecbuf = NULL;
   527
-> 528      unqueue_signals();
   529
   530      return ret;
   531  }
(lldb) p queueing_enabled
(int) $31 = 0

    frame #0: 0x000000010ef8106d zsh-5.2-dev-2`hend(prog=0x0000000000000000) + 125 at hist.c:1395
   1392     DPUTS(stophist != 2 && !(inbufflags & INP_ALIAS) && !chline,
   1393           "BUG: chline is NULL in hend()");
   1394     queue_signals();
-> 1395     if (histdone & HISTFLAG_SETTY)
   1396         settyinfo(&shttyinfo);
   1397     if (!(histactive & HA_NOINC))
   1398         unlinkcurline();
(lldb) p queueing_enabled
(int) $32 = 1

    frame #0: 0x000000010ef81158 zsh-5.2-dev-2`hend(prog=0x0000000000000000) + 360 at hist.c:1405
   1402         chline = hptr = NULL;
   1403         chwords = NULL;
   1404         histactive = 0;
-> 1405         unqueue_signals();
   1406         return 1;
   1407     }
   1408     if (hist_ignore_all_dups != isset(HISTIGNOREALLDUPS)
(lldb) p queueing_enabled
(int) $33 = 0

    frame #0: 0x000000010ef603f5 zsh-5.2-dev-2`zcontext_restore_partial(parts=7) + 69 at context.c:96
   93       DPUTS(!cstack, "BUG: zcontext_restore() without zcontext_save()");
   94
   95       queue_signals();
-> 96       cstack = cstack->next;
   97
   98       if (parts & ZCONTEXT_HIST) {
   99           hist_context_restore(&cs->hist_stack, !cstack);
(lldb) p queueing_enabled
(int) $34 = 1

    frame #0: 0x000000010ef604de zsh-5.2-dev-2`zcontext_restore_partial(parts=7) + 302 at context.c:110
   107
   108      free(cs);
   109
-> 110      unqueue_signals();
   111  }
   112
   113  /* restore full context */
(lldb) p queueing_enabled
(int) $35 = 0

    frame #0: 0x000000010ef61727 zsh-5.2-dev-2`execlist(state=0x00007fff50cb51f0, dont_change_job=0, exiting=0) + 55 at exec.c:1205
   1202
   1203     queue_signals();
   1204
-> 1205     cj = thisjob;
   1206     old_pline_level = pline_level;
   1207     old_list_pipe = list_pipe;
   1208     old_list_pipe_job = list_pipe_job;
(lldb) p queueing_enabled
(int) $36 = 1

    frame #0: 0x000000010ef62892 zsh-5.2-dev-2`execpline(state=0x00007fff50cb51f0, slcode=3074, how=18, last1=0) + 194 at exec.c:1514
   1511      */
   1512     queue_signals();
   1513
-> 1514     pj = thisjob;
   1515     ipipe[0] = ipipe[1] = opipe[0] = opipe[1] = 0;
   1516     child_block();
   1517
(lldb) p queueing_enabled
(int) $37 = 2

    frame #0: 0x000000010efa5a96 zsh-5.2-dev-2`zhalloc(size=48) + 54 at mem.c:604
   601       * but we think that nothing upstream of fheap has more free space,
   602       * so why start over at heaps just because fheap has too little?
   603       */
-> 604      for (h = (fheap ? fheap : heaps); h; h = h->next) {
   605          hp = h;
   606          if (ARENA_SIZEOF(h) >= (n = size + h->used)) {
   607              void *ret;
(lldb) p queueing_enabled
(int) $38 = 3

    frame #0: 0x000000010efa5b57 zsh-5.2-dev-2`zhalloc(size=48) + 247 at mem.c:611
   608
   609              h->used = n;
   610              ret = arena(h) + n - size;
-> 611              unqueue_signals();
   612  #ifdef ZSH_HEAP_DEBUG
   613              last_heap_id = h->heap_id;
   614              if (heap_debug_verbosity & HDV_ALLOC) {
(lldb) p queueing_enabled
(int) $39 = 2

    frame #0: 0x000000010efa5a96 zsh-5.2-dev-2`zhalloc(size=8) + 54 at mem.c:604
   601       * but we think that nothing upstream of fheap has more free space,
   602       * so why start over at heaps just because fheap has too little?
   603       */
-> 604      for (h = (fheap ? fheap : heaps); h; h = h->next) {
   605          hp = h;
   606          if (ARENA_SIZEOF(h) >= (n = size + h->used)) {
   607              void *ret;
(lldb) p queueing_enabled
(int) $40 = 3

    frame #0: 0x000000010efa5b57 zsh-5.2-dev-2`zhalloc(size=8) + 247 at mem.c:611
   608
   609              h->used = n;
   610              ret = arena(h) + n - size;
-> 611              unqueue_signals();
   612  #ifdef ZSH_HEAP_DEBUG
   613              last_heap_id = h->heap_id;
   614              if (heap_debug_verbosity & HDV_ALLOC) {
(lldb) p queueing_enabled
(int) $41 = 2

    frame #0: 0x000000010ef63daa zsh-5.2-dev-2`setunderscore(str="fun") + 26 at exec.c:2468
   2465 setunderscore(char *str)
   2466 {
   2467     queue_signals();
-> 2468     if (str && *str) {
   2469         int l = strlen(str) + 1, nl = (l + 31) & ~31;
   2470
   2471         if (nl > underscorelen || (underscorelen - nl) > 64) {
(lldb) p queueing_enabled
(int) $42 = 3

    frame #0: 0x000000010ef63f15 zsh-5.2-dev-2`setunderscore(str="fun") + 389 at exec.c:2485
   2482         *zunderscore = '\0';
   2483         underscoreused = 1;
   2484     }
-> 2485     unqueue_signals();
   2486 }
   2487
(lldb) p queueing_enabled
(int) $43 = 2

me #0: 0x000000010ef68bbd zsh-5.2-dev-2`execshfunc(shf=0x00007fa7ec8d3940, args=0x000000010f38b120) + 413 at exec.c:4993
   4990         fflush(xtrerr);
   4991     }
   4992     queue_signals();
-> 4993     ocs = cmdstack;
   4994     ocsp = cmdsp;
   4995     cmdstack = (unsigned char *) zalloc(CMDSTACKSZ);
   4996     cmdsp = 0;
(lldb) p queueing_enabled
(int) $44 = 3

    frame #0: 0x000000010efa513d zsh-5.2-dev-2`zalloc(size=256) + 45 at mem.c:956
   953      if (!size)
   954          size = 1;
   955      queue_signals();
-> 956      if (!(ptr = (void *) malloc(size))) {
   957          zerr("fatal error: out of memory");
   958          exit(1);
   959      }
(lldb) p queueing_enabled
(int) $45 = 4

    frame #0: 0x000000010efa51a2 zsh-5.2-dev-2`zalloc(size=256) + 146 at mem.c:960
   957          zerr("fatal error: out of memory");
   958          exit(1);
   959      }
-> 960      unqueue_signals();
   961
   962      return ptr;
   963  }
(lldb) p queueing_enabled
(int) $46 = 3

    frame #0: 0x000000010efa5a96 zsh-5.2-dev-2`zhalloc(size=8) + 54 at mem.c:604
   601       * but we think that nothing upstream of fheap has more free space,
   602       * so why start over at heaps just because fheap has too little?
   603       */
-> 604      for (h = (fheap ? fheap : heaps); h; h = h->next) {
   605          hp = h;
   606          if (ARENA_SIZEOF(h) >= (n = size + h->used)) {
   607              void *ret;
(lldb) p queueing_enabled
(int) $47 = 4

    frame #0: 0x000000010efa5b57 zsh-5.2-dev-2`zhalloc(size=8) + 247 at mem.c:611
   608
   609              h->used = n;
   610              ret = arena(h) + n - size;
-> 611              unqueue_signals();
   612  #ifdef ZSH_HEAP_DEBUG
   613              last_heap_id = h->heap_id;
   614              if (heap_debug_verbosity & HDV_ALLOC) {
(lldb) p queueing_enabled
(int) $48 = 3

    frame #0: 0x000000010ef667a7 zsh-5.2-dev-2`doshfunc(shfunc=0x00007fa7ec8d3940, doshargs=0x000000010f38b120, noreturnval=0) + 151 at exec.c:5221
   5218
   5219     queue_signals();    /* Lots of memory and global state changes coming */
   5220
-> 5221     NEWHEAPS(funcheap) {
   5222         oargv0 = NULL;
   5223         obreaks = breaks;
   5224         ocontflag = contflag;
(lldb) p queueing_enabled
(int) $49 = 4

    frame #0: 0x000000010efa4be6 zsh-5.2-dev-2`new_heaps + 22 at mem.c:199
   196      Heap h;
   197
   198      queue_signals();
-> 199      h = heaps;
   200
   201      fheap = heaps = NULL;
   202      unqueue_signals();
(lldb) p queueing_enabled
(int) $50 = 5
(lldb)

    frame #0: 0x000000010efa4c38 zsh-5.2-dev-2`new_heaps + 104 at mem.c:202
   199      h = heaps;
   200
   201      fheap = heaps = NULL;
-> 202      unqueue_signals();
   203
   204  #ifdef ZSH_HEAP_DEBUG
   205      if (heap_debug_verbosity & HDV_NEW) {
(lldb) p queueing_enabled
(int) $51 = 4

    frame #0: 0x000000010efa513d zsh-5.2-dev-2`zalloc(size=16) + 45 at mem.c:956
   953      if (!size)
   954          size = 1;
   955      queue_signals();
-> 956      if (!(ptr = (void *) malloc(size))) {
   957          zerr("fatal error: out of memory");
   958          exit(1);
   959      }
(lldb) p queueing_enabled
(int) $52 = 5

    frame #0: 0x000000010efa51a2 zsh-5.2-dev-2`zalloc(size=16) + 146 at mem.c:960
   957          zerr("fatal error: out of memory");
   958          exit(1);
   959      }
-> 960      unqueue_signals();
   961
   962      return ptr;
   963  }
(lldb) p queueing_enabled
(int) $53 = 4

    frame #0: 0x000000010efa5a96 zsh-5.2-dev-2`zhalloc(size=8) + 54 at mem.c:604
   601       * but we think that nothing upstream of fheap has more free space,
   602       * so why start over at heaps just because fheap has too little?
   603       */
-> 604      for (h = (fheap ? fheap : heaps); h; h = h->next) {
   605          hp = h;
   606          if (ARENA_SIZEOF(h) >= (n = size + h->used)) {
   607              void *ret;
(lldb) p queueing_enabled
(int) $54 = 5

    frame #0: 0x000000010efa5d17 zsh-5.2-dev-2`zhalloc(size=8) + 695 at mem.c:672
   669              heaps = h;
   670          fheap = h;
   671
-> 672          unqueue_signals();
   673  #ifdef ZSH_HEAP_DEBUG
   674          last_heap_id = h->heap_id;
   675          if (heap_debug_verbosity & HDV_ALLOC) {
(lldb) p queueing_enabled
(int) $55 = 4

    frame #0: 0x000000010efa690d zsh-5.2-dev-2`zshcalloc(size=16) + 45 at mem.c:974
   971      if (!size)
   972          size = 1;
   973      queue_signals();
-> 974      if (!(ptr = (void *) malloc(size))) {
   975          zerr("fatal error: out of memory");
   976          exit(1);
   977      }
(lldb) p queueing_enabled
(int) $56 = 5

    frame #0: 0x000000010efa6972 zsh-5.2-dev-2`zshcalloc(size=16) + 146 at mem.c:978
   975          zerr("fatal error: out of memory");
   976          exit(1);
   977      }
-> 978      unqueue_signals();
   979      memset(ptr, 0, size);
   980
   981      return ptr;
(lldb) p queueing_enabled
(int) $57 = 4

    frame #0: 0x000000010efa513d zsh-5.2-dev-2`zalloc(size=4) + 45 at mem.c:956
   953      if (!size)
   954          size = 1;
   955      queue_signals();
-> 956      if (!(ptr = (void *) malloc(size))) {
   957          zerr("fatal error: out of memory");
   958          exit(1);
   959      }
(lldb) p queueing_enabled
(int) $58 = 5

    frame #0: 0x000000010efa51a2 zsh-5.2-dev-2`zalloc(size=4) + 146 at mem.c:960
   957          zerr("fatal error: out of memory");
   958          exit(1);
   959      }
-> 960      unqueue_signals();
   961
   962      return ptr;
   963  }
(lldb) p queueing_enabled
(int) $59 = 4

    frame #0: 0x000000010efa5a96 zsh-5.2-dev-2`zhalloc(size=8) + 54 at mem.c:604
   601       * but we think that nothing upstream of fheap has more free space,
   602       * so why start over at heaps just because fheap has too little?
   603       */
-> 604      for (h = (fheap ? fheap : heaps); h; h = h->next) {
   605          hp = h;
   606          if (ARENA_SIZEOF(h) >= (n = size + h->used)) {
   607              void *ret;
(lldb) p queueing_enabled
(int) $60 = 5

    frame #0: 0x000000010efa5b57 zsh-5.2-dev-2`zhalloc(size=8) + 247 at mem.c:611
   608
   609              h->used = n;
   610              ret = arena(h) + n - size;
-> 611              unqueue_signals();
   612  #ifdef ZSH_HEAP_DEBUG
   613              last_heap_id = h->heap_id;
   614              if (heap_debug_verbosity & HDV_ALLOC) {
(lldb) p queueing_enabled
(int) $61 = 4

    frame #0: 0x000000010ef673ec zsh-5.2-dev-2`runshfunc(prog=0x00007fa7ec8c3b00, wrap=0x000000010f306300, name="fun") + 44 at exec.c:5488
   5485
   5486     queue_signals();
   5487
-> 5488     ou = zalloc(ouu = underscoreused);
   5489     if (ou)
   5490         memcpy(ou, zunderscore, underscoreused);
   5491
(lldb) p queueing_enabled
(int) $62 = 5

    frame #0: 0x000000010efa513d zsh-5.2-dev-2`zalloc(size=4) + 45 at mem.c:956
   953      if (!size)
   954          size = 1;
   955      queue_signals();
-> 956      if (!(ptr = (void *) malloc(size))) {
   957          zerr("fatal error: out of memory");
   958          exit(1);
   959      }
(lldb) p queueing_enabled
(int) $63 = 6

    frame #0: 0x000000010efa51a2 zsh-5.2-dev-2`zalloc(size=4) + 146 at mem.c:960
   957          zerr("fatal error: out of memory");
   958          exit(1);
   959      }
-> 960      unqueue_signals();
   961
   962      return ptr;
   963  }
(lldb) p queueing_enabled
(int) $64 = 5

    frame #0: 0x000000010ef61727 zsh-5.2-dev-2`execlist(state=0x00007fff50cb4430, dont_change_job=1, exiting=0) + 55 at exec.c:1205
   1202
   1203     queue_signals();
   1204
-> 1205     cj = thisjob;
   1206     old_pline_level = pline_level;
   1207     old_list_pipe = list_pipe;
   1208     old_list_pipe_job = list_pipe_job;
(lldb) p queueing_enabled
(int) $65 = 6

    frame #0: 0x000000010ef62892 zsh-5.2-dev-2`execpline(state=0x00007fff50cb4430, slcode=5122, how=18, last1=0) + 194 at exec.c:1514
   1511      */
   1512     queue_signals();
   1513
-> 1514     pj = thisjob;
   1515     ipipe[0] = ipipe[1] = opipe[0] = opipe[1] = 0;
   1516     child_block();
   1517
(lldb) p queueing_enabled
(int) $66 = 7

    frame #0: 0x000000010efa5a96 zsh-5.2-dev-2`zhalloc(size=96) + 54 at mem.c:604
   601       * but we think that nothing upstream of fheap has more free space,
   602       * so why start over at heaps just because fheap has too little?
   603       */
-> 604      for (h = (fheap ? fheap : heaps); h; h = h->next) {
   605          hp = h;
   606          if (ARENA_SIZEOF(h) >= (n = size + h->used)) {
   607              void *ret;
(lldb) p queueing_enabled
(int) $67 = 8

    frame #0: 0x000000010efa5b57 zsh-5.2-dev-2`zhalloc(size=96) + 247 at mem.c:611
   608
   609              h->used = n;
   610              ret = arena(h) + n - size;
-> 611              unqueue_signals();
   612  #ifdef ZSH_HEAP_DEBUG
   613              last_heap_id = h->heap_id;
   614              if (heap_debug_verbosity & HDV_ALLOC) {
(lldb) p queueing_enabled
(int) $68 = 7

    frame #0: 0x000000010efa5a96 zsh-5.2-dev-2`zhalloc(size=8) + 54 at mem.c:604
   601       * but we think that nothing upstream of fheap has more free space,
   602       * so why start over at heaps just because fheap has too little?
   603       */
-> 604      for (h = (fheap ? fheap : heaps); h; h = h->next) {
   605          hp = h;
   606          if (ARENA_SIZEOF(h) >= (n = size + h->used)) {
   607              void *ret;
(lldb) p queueing_enabled
(int) $69 = 8

    frame #0: 0x000000010efa5b57 zsh-5.2-dev-2`zhalloc(size=8) + 247 at mem.c:611
   608
   609              h->used = n;
   610              ret = arena(h) + n - size;
-> 611              unqueue_signals();
   612  #ifdef ZSH_HEAP_DEBUG
   613              last_heap_id = h->heap_id;
   614              if (heap_debug_verbosity & HDV_ALLOC) {
(lldb) p queueing_enabled
(int) $70 = 7

    frame #0: 0x000000010efa5a96 zsh-5.2-dev-2`zhalloc(size=8) + 54 at mem.c:604
   601       * but we think that nothing upstream of fheap has more free space,
   602       * so why start over at heaps just because fheap has too little?
   603       */
-> 604      for (h = (fheap ? fheap : heaps); h; h = h->next) {
   605          hp = h;
   606          if (ARENA_SIZEOF(h) >= (n = size + h->used)) {
   607              void *ret;
(lldb) p queueing_enabled
(int) $71 = 8

    frame #0: 0x000000010efa5b57 zsh-5.2-dev-2`zhalloc(size=8) + 247 at mem.c:611
   608
   609              h->used = n;
   610              ret = arena(h) + n - size;
-> 611              unqueue_signals();
   612  #ifdef ZSH_HEAP_DEBUG
   613              last_heap_id = h->heap_id;
   614              if (heap_debug_verbosity & HDV_ALLOC) {
(lldb) p queueing_enabled
(int) $72 = 7

    frame #0: 0x000000010efa5a96 zsh-5.2-dev-2`zhalloc(size=8) + 54 at mem.c:604
   601       * but we think that nothing upstream of fheap has more free space,
   602       * so why start over at heaps just because fheap has too little?
   603       */
-> 604      for (h = (fheap ? fheap : heaps); h; h = h->next) {
   605          hp = h;
   606          if (ARENA_SIZEOF(h) >= (n = size + h->used)) {
   607              void *ret;
(lldb) p queueing_enabled
(int) $73 = 8
(lldb)

    frame #0: 0x000000010efa5b57 zsh-5.2-dev-2`zhalloc(size=8) + 247 at mem.c:611
   608
   609              h->used = n;
   610              ret = arena(h) + n - size;
-> 611              unqueue_signals();
   612  #ifdef ZSH_HEAP_DEBUG
   613              last_heap_id = h->heap_id;
   614              if (heap_debug_verbosity & HDV_ALLOC) {
(lldb) p queueing_enabled
(int) $74 = 7

    frame #0: 0x000000010ef63daa zsh-5.2-dev-2`setunderscore(str="fun") + 26 at exec.c:2468
   2465 setunderscore(char *str)
   2466 {
   2467     queue_signals();
-> 2468     if (str && *str) {
   2469         int l = strlen(str) + 1, nl = (l + 31) & ~31;
   2470
   2471         if (nl > underscorelen || (underscorelen - nl) > 64) {
(lldb) p queueing_enabled
(int) $75 = 8

    frame #0: 0x000000010ef63f15 zsh-5.2-dev-2`setunderscore(str="fun") + 389 at exec.c:2485
   2482         *zunderscore = '\0';
   2483         underscoreused = 1;
   2484     }
-> 2485     unqueue_signals();
   2486 }
   2487
(lldb) p queueing_enabled
(int) $76 = 7

    frame #0: 0x000000010ef6e1c6 zsh-5.2-dev-2`execcmd(state=0x00007fff50cb4430, input=0, output=0, how=18, last1=2) + 18918 at exec.c:3782
   3779                     }
   3780                     state->pc = opc;
   3781                 }
-> 3782                 dont_queue_signals();
   3783                 if (!errflag)
   3784                     lastval = execbuiltin(args, assigns, (Builtin) hn);
   3785                 if (do_save & BINF_COMMAND)
(lldb) p queueing_enabled
(int) $77 = 0

    frame #0: 0x000000010efa5a96 zsh-5.2-dev-2`zhalloc(size=24) + 54 at mem.c:604
   601       * but we think that nothing upstream of fheap has more free space,
   602       * so why start over at heaps just because fheap has too little?
   603       */
-> 604      for (h = (fheap ? fheap : heaps); h; h = h->next) {
   605          hp = h;
   606          if (ARENA_SIZEOF(h) >= (n = size + h->used)) {
   607              void *ret;
(lldb) p queueing_enabled
(int) $78 = 1

    frame #0: 0x000000010efa5b57 zsh-5.2-dev-2`zhalloc(size=24) + 247 at mem.c:611
   608
   609              h->used = n;
   610              ret = arena(h) + n - size;
-> 611              unqueue_signals();
   612  #ifdef ZSH_HEAP_DEBUG
   613              last_heap_id = h->heap_id;
   614              if (heap_debug_verbosity & HDV_ALLOC) {
(lldb) p queueing_enabled
(int) $79 = 0

    frame #0: 0x000000010efa513d zsh-5.2-dev-2`zalloc(size=32) + 45 at mem.c:956
   953      if (!size)
   954          size = 1;
   955      queue_signals();
-> 956      if (!(ptr = (void *) malloc(size))) {
   957          zerr("fatal error: out of memory");
   958          exit(1);
   959      }
(lldb) p queueing_enabled
(int) $80 = 1

    frame #0: 0x000000010efa51a2 zsh-5.2-dev-2`zalloc(size=32) + 146 at mem.c:960
   957          zerr("fatal error: out of memory");
   958          exit(1);
   959      }
-> 960      unqueue_signals();
   961
   962      return ptr;
   963  }
(lldb) p queueing_enabled
(int) $81 = 0

    frame #0: 0x000000010efa513d zsh-5.2-dev-2`zalloc(size=4) + 45 at mem.c:956
   953      if (!size)
   954          size = 1;
   955      queue_signals();
-> 956      if (!(ptr = (void *) malloc(size))) {
   957          zerr("fatal error: out of memory");
   958          exit(1);
   959      }
(lldb) p queueing_enabled
(int) $82 = 1

    frame #0: 0x000000010efa51a2 zsh-5.2-dev-2`zalloc(size=4) + 146 at mem.c:960
   957          zerr("fatal error: out of memory");
   958          exit(1);
   959      }
-> 960      unqueue_signals();
   961
   962      return ptr;
   963  }
(lldb) p queueing_enabled
(int) $83 = 0

    frame #0: 0x000000010efa513d zsh-5.2-dev-2`zalloc(size=16) + 45 at mem.c:956
   953      if (!size)
   954          size = 1;
   955      queue_signals();
-> 956      if (!(ptr = (void *) malloc(size))) {
   957          zerr("fatal error: out of memory");
   958          exit(1);
   959      }
(lldb) p queueing_enabled
(int) $84 = 1

    frame #0: 0x000000010efa51a2 zsh-5.2-dev-2`zalloc(size=16) + 146 at mem.c:960
   957          zerr("fatal error: out of memory");
   958          exit(1);
   959      }
-> 960      unqueue_signals();
   961
   962      return ptr;
   963  }
(lldb) p queueing_enabled
(int) $85 = 0

    frame #0: 0x000000010efa513d zsh-5.2-dev-2`zalloc(size=24) + 45 at mem.c:956
   953      if (!size)
   954          size = 1;
   955      queue_signals();
-> 956      if (!(ptr = (void *) malloc(size))) {
   957          zerr("fatal error: out of memory");
   958          exit(1);
   959      }
(lldb) p queueing_enabled
(int) $86 = 1

    frame #0: 0x000000010efa51a2 zsh-5.2-dev-2`zalloc(size=24) + 146 at mem.c:960
   957          zerr("fatal error: out of memory");
   958          exit(1);
   959      }
-> 960      unqueue_signals();
   961
   962      return ptr;
   963  }
(lldb) p queueing_enabled
(int) $87 = 0

    frame #0: 0x000000010ef6e2d9 zsh-5.2-dev-2`execcmd(state=0x00007fff50cb4430, input=0, output=0, how=18, last1=2) + 19193 at exec.c:3788
   3785                 if (do_save & BINF_COMMAND)
   3786                     errflag &= ~ERRFLAG_ERROR;
   3787                 restore_queue_signals(q);
-> 3788                 fflush(stdout);
   3789                 if (save[1] == -2) {
   3790                     if (ferror(stdout)) {
   3791                         zwarn("write error: %e", errno);
(lldb) p queueing_enabled
(int) $88 = 7

    frame #0: 0x000000010ef6376e zsh-5.2-dev-2`execpline(state=0x00007fff50cb4430, slcode=5122, how=18, last1=0) + 3998 at exec.c:1779
   1776                     break;
   1777             }
   1778             child_unblock();
-> 1779             unqueue_signals();
   1780
   1781             if (list_pipe && (lastval & 0200) && pj >= 0 &&
   1782                 (!(jn->stat & STAT_INUSE) || (jn->stat & STAT_DONE))) {
(lldb) p queueing_enabled
(int) $89 = 6

    frame #0: 0x000000010ef62439 zsh-5.2-dev-2`execlist(state=0x00007fff50cb4430, dont_change_job=1, exiting=0) + 3401 at exec.c:1477
   1474         sigtrapped[SIGEXIT] = 0;
   1475     }
   1476
-> 1477     unqueue_signals();
   1478 }
   1479
(lldb) p queueing_enabled
(int) $90 = 5

    frame #0: 0x000000010ef63daa zsh-5.2-dev-2`setunderscore(str="fun") + 26 at exec.c:2468
   2465 setunderscore(char *str)
   2466 {
   2467     queue_signals();
-> 2468     if (str && *str) {
   2469         int l = strlen(str) + 1, nl = (l + 31) & ~31;
   2470
   2471         if (nl > underscorelen || (underscorelen - nl) > 64) {
(lldb) p queueing_enabled
(int) $91 = 6

    frame #0: 0x000000010ef63f15 zsh-5.2-dev-2`setunderscore(str="fun") + 389 at exec.c:2485
   2482         *zunderscore = '\0';
   2483         underscoreused = 1;
   2484     }
-> 2485     unqueue_signals();
   2486 }
   2487
(lldb) p queueing_enabled
(int) $92 = 5

    frame #0: 0x000000010efbd9a4 zsh-5.2-dev-2`endparamscope + 36 at params.c:5087
   5084 endparamscope(void)
   5085 {
   5086     queue_signals();
-> 5087     locallevel--;
   5088     /* This pops anything from a higher locallevel */
   5089     saveandpophiststack(0, HFILE_USE_OPTIONS);
   5090     scanhashtable(paramtab, 0, 0, 0, scanendscope, 0);
(lldb) p queueing_enabled
(int) $93 = 6

    frame #0: 0x000000010efbda0b zsh-5.2-dev-2`endparamscope + 139 at params.c:5091
   5088     /* This pops anything from a higher locallevel */
   5089     saveandpophiststack(0, HFILE_USE_OPTIONS);
   5090     scanhashtable(paramtab, 0, 0, 0, scanendscope, 0);
-> 5091     unqueue_signals();
   5092 }
   5093
   5094 /**/
(lldb) p queueing_enabled
(int) $94 = 5

    frame #0: 0x000000010ef67561 zsh-5.2-dev-2`runshfunc(prog=0x00007fa7ec8c3b00, wrap=0x0000000000000000, name="fun") + 417 at exec.c:5516
   5513     }
   5514     endparamscope();
   5515
-> 5516     unqueue_signals();
   5517 }
   5518
(lldb) p queueing_enabled
(int) $95 = 4

    frame #0: 0x000000010efa4d0a zsh-5.2-dev-2`old_heaps(old=0x000000010f38b000) + 26 at mem.c:225
   222      Heap h, n;
   223
   224      queue_signals();
-> 225      for (h = heaps; h; h = n) {
   226          n = h->next;
   227          DPUTS(h->sp, "BUG: old_heaps() with pushed heaps");
   228  #ifdef ZSH_HEAP_DEBUG
(lldb) p queueing_enabled
(int) $96 = 5

    frame #0: 0x000000010efa4db8 zsh-5.2-dev-2`old_heaps(old=0x000000010f38b000) + 200 at mem.c:260
   257      }
   258  #endif
   259      fheap = NULL;
-> 260      unqueue_signals();
   261  }
   262
   263  /* Temporarily switch to other heaps (or back again). */
(lldb) p queueing_enabled
(int) $97 = 4

    frame #0: 0x000000010ef67239 zsh-5.2-dev-2`doshfunc(shfunc=0x00007fa7ec8d3940, doshargs=0x000000010f38b120, noreturnval=0) + 2857 at exec.c:5446
   5443         }
   5444     } OLDHEAPS;
   5445
-> 5446     unqueue_signals();
   5447
(lldb) p queueing_enabled
(int) $98 = 3

    frame #0: 0x000000010ef68cb3 zsh-5.2-dev-2`execshfunc(shf=0x00007fa7ec8d3940, args=0x000000010f38b120) + 659 at exec.c:5010
   5007
   5008     if (!list_pipe)
   5009         deletefilelist(last_file_list, 0);
-> 5010     unqueue_signals();
   5011 }
   5012
(lldb) p queueing_enabled
(int) $99 = 2

    frame #0: 0x000000010ef62439 zsh-5.2-dev-2`execlist(state=0x00007fff50cb51f0, dont_change_job=0, exiting=0) + 3401 at exec.c:1477
   1474         sigtrapped[SIGEXIT] = 0;
   1475     }
   1476
-> 1477     unqueue_signals();
   1478 }
   1479
(lldb) p queueing_enabled
(int) $100 = 1

    frame #0: 0x000000010efa55ce zsh-5.2-dev-2`popheap + 30 at mem.c:454
   451      h_pop++;
   452  #endif
   453
-> 454      fheap = NULL;
   455      for (h = heaps; h; h = hn) {
   456          hn = h->next;
   457          if ((hs = h->sp)) {
(lldb) p queueing_enabled
(int) $101 = 2

    frame #0: 0x000000010efa57f4 zsh-5.2-dev-2`popheap + 580 at mem.c:516
   513      else
   514          heaps = NULL;
   515
-> 516      unqueue_signals();
   517  }
   518
   519  #ifdef USE_MMAP
(lldb) p queueing_enabled
(int) $102 = 1

    frame #0: 0x000000010efa4f96 zsh-5.2-dev-2`pushheap + 22 at mem.c:302
   299      h_push++;
   300  #endif
   301
-> 302      for (h = heaps; h; h = h->next) {
   303          DPUTS(!h->used && h->next, "BUG: empty heap");
   304          hs = (Heapstack) zalloc(sizeof(*hs));
   305          hs->next = h->sp;
(lldb) p queueing_enabled
(int) $103 = 2

    frame #0: 0x000000010efa513d zsh-5.2-dev-2`zalloc(size=16) + 45 at mem.c:956
   953      if (!size)
   954          size = 1;
   955      queue_signals();
-> 956      if (!(ptr = (void *) malloc(size))) {
   957          zerr("fatal error: out of memory");
   958          exit(1);
   959      }
(lldb) p queueing_enabled
(int) $104 = 3

    frame #0: 0x000000010efa51a2 zsh-5.2-dev-2`zalloc(size=16) + 146 at mem.c:960
   957          zerr("fatal error: out of memory");
   958          exit(1);
   959      }
-> 960      unqueue_signals();
   961
   962      return ptr;
   963  }
(lldb) p queueing_enabled
(int) $105 = 2

    frame #0: 0x000000010efa505d zsh-5.2-dev-2`pushheap + 221 at mem.c:318
   315          }
   316  #endif
   317      }
-> 318      unqueue_signals();
   319  }
   320
   321  /* reset heaps to previous state */
(lldb) p queueing_enabled
(int) $106 = 1

    frame #0: 0x000000010ef60200 zsh-5.2-dev-2`zcontext_save_partial(parts=7) + 32 at context.c:58
   55
   56       queue_signals();
   57
-> 58       cs = (struct context_stack *)malloc(sizeof(struct context_stack));
   59
   60       if (parts & ZCONTEXT_HIST) {
   61           hist_context_save(&cs->hist_stack, !cstack);
(lldb) p queueing_enabled
(int) $107 = 2

    frame #0: 0x000000010efa513d zsh-5.2-dev-2`zalloc(size=256) + 45 at mem.c:956
   953      if (!size)
   954          size = 1;
   955      queue_signals();
-> 956      if (!(ptr = (void *) malloc(size))) {
   957          zerr("fatal error: out of memory");
   958          exit(1);
   959      }
(lldb) p queueing_enabled
(int) $108 = 3

    frame #0: 0x000000010efa51a2 zsh-5.2-dev-2`zalloc(size=256) + 146 at mem.c:960
   957          zerr("fatal error: out of memory");
   958          exit(1);
   959      }
-> 960      unqueue_signals();
   961
   962      return ptr;
   963  }
(lldb) p queueing_enabled
(int) $109 = 2

    frame #0: 0x000000010ef602f1 zsh-5.2-dev-2`zcontext_save_partial(parts=7) + 273 at context.c:73
   70       cs->next = cstack;
   71       cstack = cs;
   72
-> 73       unqueue_signals();
   74   }
   75
   76   /* save context in full */
(lldb) p queueing_enabled
(int) $110 = 1

    frame #0: 0x000000010ef90974 zsh-5.2-dev-2`inputsetline(str="fun", flags=64) + 36 at input.c:386
   383  {
   384      queue_signals();
   385
-> 386      if ((inbufflags & INP_FREE) && inbuf) {
   387          free(inbuf);
   388      }
   389      inbuf = inbufptr = str;
(lldb) p queueing_enabled
(int) $111 = 2

    frame #0: 0x000000010ef90a33 zsh-5.2-dev-2`inputsetline(str="fun", flags=64) + 227 at input.c:404
   401          inbufct = inbufleft;
   402      inbufflags = flags;
   403
-> 404      unqueue_signals();
   405  }
   406
(lldb) p queueing_enabled
(int) $112 = 1

    frame #0: 0x000000010efbe9ed zsh-5.2-dev-2`init_parse + 29 at parse.c:469
   466  {
   467      queue_signals();
   468
-> 469      if (ecbuf) zfree(ecbuf, eclen);
   470
   471      ecbuf = (Wordcode) zalloc((eclen = EC_INIT_SIZE) * sizeof(wordcode));
   472      ecused = 0;
(lldb) p queueing_enabled
(int) $113 = 2

    frame #0: 0x000000010efa513d zsh-5.2-dev-2`zalloc(size=1024) + 45 at mem.c:956
   953      if (!size)
   954          size = 1;
   955      queue_signals();
-> 956      if (!(ptr = (void *) malloc(size))) {
   957          zerr("fatal error: out of memory");
   958          exit(1);
   959      }
(lldb) p queueing_enabled
(int) $114 = 3

    frame #0: 0x000000010efa51a2 zsh-5.2-dev-2`zalloc(size=1024) + 146 at mem.c:960
   957          zerr("fatal error: out of memory");
   958          exit(1);
   959      }
-> 960      unqueue_signals();
   961
   962      return ptr;
   963  }
(lldb) p queueing_enabled
(int) $115 = 2

    frame #0: 0x000000010efbeabb zsh-5.2-dev-2`init_parse + 235 at parse.c:480
   477
   478      init_parse_status();
   479
-> 480      unqueue_signals();
   481  }
   482
   483  /* Build eprog. */
(lldb) p queueing_enabled
(int) $116 = 1

    frame #0: 0x000000010efa5a96 zsh-5.2-dev-2`zhalloc(size=32) + 54 at mem.c:604
   601       * but we think that nothing upstream of fheap has more free space,
   602       * so why start over at heaps just because fheap has too little?
   603       */
-> 604      for (h = (fheap ? fheap : heaps); h; h = h->next) {
   605          hp = h;
   606          if (ARENA_SIZEOF(h) >= (n = size + h->used)) {
   607              void *ret;
(lldb) p queueing_enabled
(int) $117 = 2

    frame #0: 0x000000010efa5b57 zsh-5.2-dev-2`zhalloc(size=32) + 247 at mem.c:611
   608
   609              h->used = n;
   610              ret = arena(h) + n - size;
-> 611              unqueue_signals();
   612  #ifdef ZSH_HEAP_DEBUG
   613              last_heap_id = h->heap_id;
   614              if (heap_debug_verbosity & HDV_ALLOC) {
(lldb) p queueing_enabled
(int) $118 = 1

    frame #0: 0x000000010efa5a96 zsh-5.2-dev-2`zhalloc(size=8) + 54 at mem.c:604
   601       * but we think that nothing upstream of fheap has more free space,
   602       * so why start over at heaps just because fheap has too little?
   603       */
-> 604      for (h = (fheap ? fheap : heaps); h; h = h->next) {
   605          hp = h;
   606          if (ARENA_SIZEOF(h) >= (n = size + h->used)) {
   607              void *ret;
(lldb) p queueing_enabled
(int) $119 = 2

    frame #0: 0x000000010efa5b57 zsh-5.2-dev-2`zhalloc(size=8) + 247 at mem.c:611
   608
   609              h->used = n;
   610              ret = arena(h) + n - size;
-> 611              unqueue_signals();
   612  #ifdef ZSH_HEAP_DEBUG
   613              last_heap_id = h->heap_id;
   614              if (heap_debug_verbosity & HDV_ALLOC) {
(lldb) p queueing_enabled
(int) $120 = 1

    frame #0: 0x000000010efbef8b zsh-5.2-dev-2`bld_eprog(heap=1) + 27 at parse.c:505
   502
   503      queue_signals();
   504
-> 505      ecadd(WCB_END());
   506
   507      ret = heap ? (Eprog) zhalloc(sizeof(*ret)) : (Eprog) zalloc(sizeof(*ret));
   508      ret->len = ((ecnpats * sizeof(Patprog)) +
(lldb) p queueing_enabled
(int) $121 = 2

    frame #0: 0x000000010efa5a96 zsh-5.2-dev-2`zhalloc(size=56) + 54 at mem.c:604
   601       * but we think that nothing upstream of fheap has more free space,
   602       * so why start over at heaps just because fheap has too little?
   603       */
-> 604      for (h = (fheap ? fheap : heaps); h; h = h->next) {
   605          hp = h;
   606          if (ARENA_SIZEOF(h) >= (n = size + h->used)) {
   607              void *ret;
(lldb) p queueing_enabled
(int) $122 = 3

    frame #0: 0x000000010efa5b57 zsh-5.2-dev-2`zhalloc(size=56) + 247 at mem.c:611
   608
   609              h->used = n;
   610              ret = arena(h) + n - size;
-> 611              unqueue_signals();
   612  #ifdef ZSH_HEAP_DEBUG
   613              last_heap_id = h->heap_id;
   614              if (heap_debug_verbosity & HDV_ALLOC) {
(lldb) p queueing_enabled
(int) $123 = 2

    frame #0: 0x000000010efa5a96 zsh-5.2-dev-2`zhalloc(size=24) + 54 at mem.c:604
   601       * but we think that nothing upstream of fheap has more free space,
   602       * so why start over at heaps just because fheap has too little?
   603       */
-> 604      for (h = (fheap ? fheap : heaps); h; h = h->next) {
   605          hp = h;
   606          if (ARENA_SIZEOF(h) >= (n = size + h->used)) {
   607              void *ret;
(lldb) p queueing_enabled
(int) $124 = 3

    frame #0: 0x000000010efa5b57 zsh-5.2-dev-2`zhalloc(size=24) + 247 at mem.c:611
   608
   609              h->used = n;
   610              ret = arena(h) + n - size;
-> 611              unqueue_signals();
   612  #ifdef ZSH_HEAP_DEBUG
   613              last_heap_id = h->heap_id;
   614              if (heap_debug_verbosity & HDV_ALLOC) {
(lldb) p queueing_enabled
(int) $125 = 2

    frame #0: 0x000000010efbf1bc zsh-5.2-dev-2`bld_eprog(heap=1) + 588 at parse.c:528
   525      zfree(ecbuf, eclen);
   526      ecbuf = NULL;
   527
-> 528      unqueue_signals();
   529
   530      return ret;
   531  }
(lldb) p queueing_enabled
(int) $126 = 1

    frame #0: 0x000000010ef8106d zsh-5.2-dev-2`hend(prog=0x0000000000000000) + 125 at hist.c:1395
   1392     DPUTS(stophist != 2 && !(inbufflags & INP_ALIAS) && !chline,
   1393           "BUG: chline is NULL in hend()");
   1394     queue_signals();
-> 1395     if (histdone & HISTFLAG_SETTY)
   1396         settyinfo(&shttyinfo);
   1397     if (!(histactive & HA_NOINC))
   1398         unlinkcurline();
(lldb) p queueing_enabled
(int) $127 = 2

    frame #0: 0x000000010ef81158 zsh-5.2-dev-2`hend(prog=0x0000000000000000) + 360 at hist.c:1405
   1402         chline = hptr = NULL;
   1403         chwords = NULL;
   1404         histactive = 0;
-> 1405         unqueue_signals();
   1406         return 1;
   1407     }
   1408     if (hist_ignore_all_dups != isset(HISTIGNOREALLDUPS)
(lldb) p queueing_enabled
(int) $128 = 1

    frame #0: 0x000000010ef603f5 zsh-5.2-dev-2`zcontext_restore_partial(parts=7) + 69 at context.c:96
   93       DPUTS(!cstack, "BUG: zcontext_restore() without zcontext_save()");
   94
   95       queue_signals();
-> 96       cstack = cstack->next;
   97
   98       if (parts & ZCONTEXT_HIST) {
   99           hist_context_restore(&cs->hist_stack, !cstack);
(lldb) p queueing_enabled
(int) $129 = 2

    frame #0: 0x000000010ef604de zsh-5.2-dev-2`zcontext_restore_partial(parts=7) + 302 at context.c:110
   107
   108      free(cs);
   109
-> 110      unqueue_signals();
   111  }
   112
   113  /* restore full context */
(lldb) p queueing_enabled
(int) $130 = 1

    frame #0: 0x000000010ef61727 zsh-5.2-dev-2`execlist(state=0x00007fff50cb51f0, dont_change_job=0, exiting=0) + 55 at exec.c:1205
   1202
   1203     queue_signals();
   1204
-> 1205     cj = thisjob;
   1206     old_pline_level = pline_level;
   1207     old_list_pipe = list_pipe;
   1208     old_list_pipe_job = list_pipe_job;
(lldb) p queueing_enabled
(int) $131 = 2

    frame #0: 0x000000010ef62892 zsh-5.2-dev-2`execpline(state=0x00007fff50cb51f0, slcode=3074, how=18, last1=0) + 194 at exec.c:1514
   1511      */
   1512     queue_signals();
   1513
-> 1514     pj = thisjob;
   1515     ipipe[0] = ipipe[1] = opipe[0] = opipe[1] = 0;
   1516     child_block();
   1517
(lldb) p queueing_enabled
(int) $132 = 3

    frame #0: 0x000000010efa5a96 zsh-5.2-dev-2`zhalloc(size=48) + 54 at mem.c:604
   601       * but we think that nothing upstream of fheap has more free space,
   602       * so why start over at heaps just because fheap has too little?
   603       */
-> 604      for (h = (fheap ? fheap : heaps); h; h = h->next) {
   605          hp = h;
   606          if (ARENA_SIZEOF(h) >= (n = size + h->used)) {
   607              void *ret;
(lldb) p queueing_enabled
(int) $133 = 4

    frame #0: 0x000000010efa5b57 zsh-5.2-dev-2`zhalloc(size=48) + 247 at mem.c:611
   608
   609              h->used = n;
   610              ret = arena(h) + n - size;
-> 611              unqueue_signals();
   612  #ifdef ZSH_HEAP_DEBUG
   613              last_heap_id = h->heap_id;
   614              if (heap_debug_verbosity & HDV_ALLOC) {
(lldb) p queueing_enabled
(int) $134 = 3

    frame #0: 0x000000010efa5a96 zsh-5.2-dev-2`zhalloc(size=8) + 54 at mem.c:604
   601       * but we think that nothing upstream of fheap has more free space,
   602       * so why start over at heaps just because fheap has too little?
   603       */
-> 604      for (h = (fheap ? fheap : heaps); h; h = h->next) {
   605          hp = h;
   606          if (ARENA_SIZEOF(h) >= (n = size + h->used)) {
   607              void *ret;
(lldb) p queueing_enabled
(int) $135 = 4

    frame #0: 0x000000010efa5b57 zsh-5.2-dev-2`zhalloc(size=8) + 247 at mem.c:611
   608
   609              h->used = n;
   610              ret = arena(h) + n - size;
-> 611              unqueue_signals();
   612  #ifdef ZSH_HEAP_DEBUG
   613              last_heap_id = h->heap_id;
   614              if (heap_debug_verbosity & HDV_ALLOC) {
(lldb) p queueing_enabled
(int) $136 = 3

   frame #0: 0x000000010ef63daa zsh-5.2-dev-2`setunderscore(str="fun") + 26 at exec.c:2468
   2465 setunderscore(char *str)
   2466 {
   2467     queue_signals();
-> 2468     if (str && *str) {
   2469         int l = strlen(str) + 1, nl = (l + 31) & ~31;
   2470
   2471         if (nl > underscorelen || (underscorelen - nl) > 64) {
(lldb) p queueing_enabled
(int) $137 = 4

    frame #0: 0x000000010ef63f15 zsh-5.2-dev-2`setunderscore(str="fun") + 389 at exec.c:2485
   2482         *zunderscore = '\0';
   2483         underscoreused = 1;
   2484     }
-> 2485     unqueue_signals();
   2486 }
   2487
(lldb) p queueing_enabled
(int) $138 = 3

    frame #0: 0x000000010ef68bbd zsh-5.2-dev-2`execshfunc(shf=0x00007fa7ec8d3940, args=0x000000010f38b120) + 413 at exec.c:4993
   4990         fflush(xtrerr);
   4991     }
   4992     queue_signals();
-> 4993     ocs = cmdstack;
   4994     ocsp = cmdsp;
   4995     cmdstack = (unsigned char *) zalloc(CMDSTACKSZ);
   4996     cmdsp = 0;
(lldb) p queueing_enabled
(int) $139 = 4

    frame #0: 0x000000010efa513d zsh-5.2-dev-2`zalloc(size=256) + 45 at mem.c:956
   953      if (!size)
   954          size = 1;
   955      queue_signals();
-> 956      if (!(ptr = (void *) malloc(size))) {
   957          zerr("fatal error: out of memory");
   958          exit(1);
   959      }
(lldb) p queueing_enabled
(int) $140 = 5

    frame #0: 0x000000010efa51a2 zsh-5.2-dev-2`zalloc(size=256) + 146 at mem.c:960
   957          zerr("fatal error: out of memory");
   958          exit(1);
   959      }
-> 960      unqueue_signals();
   961
   962      return ptr;
   963  }
(lldb) p queueing_enabled
(int) $141 = 4

    frame #0: 0x000000010efa5a96 zsh-5.2-dev-2`zhalloc(size=8) + 54 at mem.c:604
   601       * but we think that nothing upstream of fheap has more free space,
   602       * so why start over at heaps just because fheap has too little?
   603       */
-> 604      for (h = (fheap ? fheap : heaps); h; h = h->next) {
   605          hp = h;
   606          if (ARENA_SIZEOF(h) >= (n = size + h->used)) {
   607              void *ret;
(lldb) p queueing_enabled
(int) $142 = 5

    frame #0: 0x000000010efa5b57 zsh-5.2-dev-2`zhalloc(size=8) + 247 at mem.c:611
   608
   609              h->used = n;
   610              ret = arena(h) + n - size;
-> 611              unqueue_signals();
   612  #ifdef ZSH_HEAP_DEBUG
   613              last_heap_id = h->heap_id;
   614              if (heap_debug_verbosity & HDV_ALLOC) {
(lldb) p queueing_enabled
(int) $143 = 4

    frame #0: 0x000000010ef667a7 zsh-5.2-dev-2`doshfunc(shfunc=0x00007fa7ec8d3940, doshargs=0x000000010f38b120, noreturnval=0) + 151 at exec.c:5221
   5218
   5219     queue_signals();    /* Lots of memory and global state changes coming */
   5220
-> 5221     NEWHEAPS(funcheap) {
   5222         oargv0 = NULL;
   5223         obreaks = breaks;
   5224         ocontflag = contflag;
(lldb) p queueing_enabled
(int) $144 = 5

    frame #0: 0x000000010efa4be6 zsh-5.2-dev-2`new_heaps + 22 at mem.c:199
   196      Heap h;
   197
   198      queue_signals();
-> 199      h = heaps;
   200
   201      fheap = heaps = NULL;
   202      unqueue_signals();
(lldb) p queueing_enabled
(int) $145 = 6

// vim:ft=c

^ permalink raw reply	[flat|nested] 14+ messages in thread

* Re: [BUG] queueing_enabled grows infinitely when in .recursive-edit
  2016-10-02 23:21 ` Bart Schaefer
  2016-10-03 10:00   ` Sebastian Gniazdowski
@ 2016-10-03 10:18   ` Peter Stephenson
  2016-10-03 11:55     ` Sebastian Gniazdowski
  2016-10-03 15:20     ` Bart Schaefer
  2016-10-03 16:33   ` Bart Schaefer
  2 siblings, 2 replies; 14+ messages in thread
From: Peter Stephenson @ 2016-10-03 10:18 UTC (permalink / raw)
  To: Zsh hackers list

On Sun, 02 Oct 2016 16:21:45 -0700
Bart Schaefer <schaefer@brasslantern.com> wrote:
> The third is that somewhere below execstring() from checksched(),
> the signal queueing level is being incremented but not decremented.

Here are some missing unqueue_signals() (this was quite boring, by the
way, just in case you were thinking "wow, wish I'd done that").  Most of
these look minor but the one in execpline() looks like it could be
hairy because most things in execpline() are hairy.  I think they're all
uncontroversial once you've seen them, but I could have slipped up as
there are quite a few.

pws

diff --git a/Src/Zle/computil.c b/Src/Zle/computil.c
index 27b78cd..e9bad1c 100644
--- a/Src/Zle/computil.c
+++ b/Src/Zle/computil.c
@@ -4865,6 +4865,7 @@ bin_compfiles(char *nam, char **args, UNUSED(Options ops), UNUSED(int func))
 	    }
 	    queue_signals();
 	    if (!(tmp = getaparam(args[1]))) {
+		unqueue_signals();
 		zwarnnam(nam, "unknown parameter: %s", args[1]);
 		return 0;
 	    }
diff --git a/Src/Zle/zle_main.c b/Src/Zle/zle_main.c
index 0bdd82b..0b3b1fc 100644
--- a/Src/Zle/zle_main.c
+++ b/Src/Zle/zle_main.c
@@ -1631,6 +1631,7 @@ bin_vared(char *name, char **args, Options ops, UNUSED(int func))
 	return 1;
     } else if (v) {
 	if (*s) {
+	    unqueue_signals();
 	    zwarnnam(name, "not an identifier: `%s'", args[0]);
 	    return 1;
 	}
diff --git a/Src/builtin.c b/Src/builtin.c
index 60dc07f..a274ff7 100644
--- a/Src/builtin.c
+++ b/Src/builtin.c
@@ -1489,6 +1489,7 @@ bin_fc(char *nam, char **argv, Options ops, int func)
     }
 
     if (zleactive) {
+	unqueue_signals();
 	zwarnnam(nam, "no interactive history within ZLE");
 	return 1;
     }
@@ -2808,6 +2809,7 @@ bin_typeset(char *name, char **argv, LinkList assigns, Options ops, int func)
 	return 0;
     }
     if (off & PM_TIED) {
+	unqueue_signals();
 	zerrnam(name, "use unset to remove tied variables");
 	return 1;
     }
@@ -3138,6 +3140,7 @@ bin_functions(char *name, char **argv, Options ops, int func)
 	    queue_signals();
 	    for (q = mathfuncs; q; q = q->next) {
 		if (!strcmp(q->name, funcname)) {
+		    unqueue_signals();
 		    zwarnnam(name, "-M %s: function already exists",
 			     funcname);
 		    zsfree(p->name);
diff --git a/Src/exec.c b/Src/exec.c
index a429428..9890286 100644
--- a/Src/exec.c
+++ b/Src/exec.c
@@ -1795,6 +1795,8 @@ execpline(Estate state, wordcode slcode, int how, int last1)
 		deletejob(jn, 0);
 	    thisjob = pj;
 	}
+	else
+	    unqueue_signals();
 	if ((slflags & WC_SUBLIST_NOT) && !errflag)
 	    lastval = !lastval;
     }
@@ -5556,6 +5558,7 @@ runshfunc(Eprog prog, FuncWrap wrap, char *name)
 	if (!cont) {
 	    if (ou)
 		zfree(ou, ouu);
+	    unqueue_signals();
 	    return;
 	}
 	wrap = wrap->next;
diff --git a/Src/hist.c b/Src/hist.c
index 5fc40bd..eebd7dc 100644
--- a/Src/hist.c
+++ b/Src/hist.c
@@ -653,6 +653,7 @@ histsubchar(int c)
 		(c == '}' ||  c == ';' || c == '\'' || c == '"' || c == '`')) {
 	      /* Neither event nor word designator, no expansion */
 	      safeinungetc(c);
+	      unqueue_signals();
 	      return bangchar;
 	    }
 	    *ptr = 0;
diff --git a/Src/init.c b/Src/init.c
index 3dea179..c12043b 100644
--- a/Src/init.c
+++ b/Src/init.c
@@ -1442,8 +1442,10 @@ sourcehome(char *s)
     queue_signals();
     if (EMULATION(EMULATE_SH|EMULATE_KSH) || !(h = getsparam_u("ZDOTDIR"))) {
 	h = home;
-	if (!h)
+	if (!h) {
+	    unqueue_signals();
 	    return;
+	}
     }
 
     {
diff --git a/Src/mem.c b/Src/mem.c
index 021dad5..db311ef 100644
--- a/Src/mem.c
+++ b/Src/mem.c
@@ -903,11 +903,15 @@ memory_validate(Heapid heap_id)
 
     queue_signals();
     for (h = heaps; h; h = h->next) {
-	if (h->heap_id == heap_id)
+	if (h->heap_id == heap_id) {
+	    unqueue_signals();
 	    return 0;
+	}
 	for (hs = heaps->sp; hs; hs = hs->next) {
-	    if (hs->heap_id == heap_id)
+	    if (hs->heap_id == heap_id) {
+		unqueue_signals();
 		return 0;
+	    }
 	}
     }
 
diff --git a/Src/module.c b/Src/module.c
index 46a7d77..41f142a 100644
--- a/Src/module.c
+++ b/Src/module.c
@@ -2242,6 +2242,7 @@ load_module(char const *name, Feature_enables enablesarr, int silent)
 	return 0;
     }
     if (m->node.flags & MOD_BUSY) {
+	unqueue_signals();
 	zerr("circular dependencies for module ;%s", name);
 	return 1;
     }
diff --git a/Src/params.c b/Src/params.c
index e115102..1418021 100644
--- a/Src/params.c
+++ b/Src/params.c
@@ -2803,6 +2803,7 @@ assignsparam(char *s, char *val, int flags)
 		zerr("read-only variable: %s", v->pm->node.nam);
 		*ss = '[';
 		zsfree(val);
+		unqueue_signals();
 		return NULL;
 	    }
 	    flags &= ~ASSPM_WARN_CREATE;
@@ -3117,6 +3118,7 @@ setnparam(char *s, mnumber val)
 	if (!(v = getvalue(&vbuf, &t, 1))) {
 	    DPUTS(!v, "BUG: value not found for new parameter");
 	    /* errflag |= ERRFLAG_ERROR; */
+	    unqueue_signals();
 	    return NULL;
 	}
 	if (!was_unset && isset(WARNCREATEGLOBAL) && locallevel > forklevel)
diff --git a/Src/prompt.c b/Src/prompt.c
index d4f3898..ee77c8b 100644
--- a/Src/prompt.c
+++ b/Src/prompt.c
@@ -491,8 +491,10 @@ putpromptchar(int doprint, int endchar, unsigned int *txtchangep)
 		if (!arg)
 		    arg++;
 		queue_signals();
-		if (!(hostnam = getsparam("HOST")))
+		if (!(hostnam = getsparam("HOST"))) {
+		    unqueue_signals();
 		    break;
+		}
 		if (arg < 0) {
 		    for (ss = hostnam + strlen(hostnam); ss > hostnam; ss--)
 			if (ss[-1] == '.' && !++arg)


^ permalink raw reply	[flat|nested] 14+ messages in thread

* Re: [BUG] queueing_enabled grows infinitely when in .recursive-edit
  2016-10-03 10:18   ` Peter Stephenson
@ 2016-10-03 11:55     ` Sebastian Gniazdowski
  2016-10-03 15:49       ` Bart Schaefer
  2016-10-03 15:20     ` Bart Schaefer
  1 sibling, 1 reply; 14+ messages in thread
From: Sebastian Gniazdowski @ 2016-10-03 11:55 UTC (permalink / raw)
  To: Peter Stephenson; +Cc: Zsh hackers list

Thanks for the patch I've devoted much time to this. I've tested the
patch and it works. Noted one thing. Initial calc_timeout() in
raw_getbyte() can return with ZTM_NONE while there are scheduled
functions, often after Ctrl-C in .recursive-edit. Added debug prints:

            } else if (diff > 0) {
                exp100ths = diff * 100;
                if (tmoutp->tp != ZTM_KEY ||
                    exp100ths < tmoutp->exp100ths) {
                    tmoutp->exp100ths = exp100ths;
                    tmoutp->tp = ZTM_FUNC;
                } else {
                    // MY DEBUG
                    _F = fopen("/tmp/recursive.txt", "a+");
                    fprintf( _F, "-- calc_timeout ZTM_FUNC condition not meet"
                     " tmoutp->tp[%d], exp100ths[%d] < tmoutp->exp100ths[%d]\n",
                     tmoutp->tp, exp100ths, tmoutp->exp100ths);
                    fclose(_F);
                }
            } else {
                _F = fopen("/tmp/recursive.txt", "a+");
                fprintf( _F, "-- calc_timeout diff[%d] > 0 condition
not meet\n", diff);
                fclose(_F);
            }

And obtain following logs:

raw_getbyte() - queueing_enabled (0)
-- calc_timeout do_keytmout[0], keytimeout[40]
-- calc_timeout timedfns != NULL
-- calc_timeout diff[0] > 0 condition not meet
-- ^^^ INIT calc_timeout tmout.tp(0) ZTM_NONE(0) ZTM_KEY(1) / BEGIN
RAW_GETBYTE() zle_main.c

It looks like calc_timeout handles diff < 0 and diff > 0 but not diff
== 0? in the "INIT" log "tmout.tp(0)" is the value (0 == ZTM_NONE)
returned from ^^^ up calc_timeout(), and it's not ZTM_FUNC like it
rather should be (timedfns != NULL).

Best regards,
Sebastian Gniazdowski


^ permalink raw reply	[flat|nested] 14+ messages in thread

* Re: [BUG] queueing_enabled grows infinitely when in .recursive-edit
  2016-10-03 10:18   ` Peter Stephenson
  2016-10-03 11:55     ` Sebastian Gniazdowski
@ 2016-10-03 15:20     ` Bart Schaefer
  2016-10-03 16:07       ` Bart Schaefer
  1 sibling, 1 reply; 14+ messages in thread
From: Bart Schaefer @ 2016-10-03 15:20 UTC (permalink / raw)
  To: Zsh hackers list

On Oct 3, 11:18am, Peter Stephenson wrote:
}
} Here are some missing unqueue_signals() (this was quite boring, by the
} way, just in case you were thinking "wow, wish I'd done that").

Actually I was thinking "Thank you, I expected to need to do that."

} Most of these look minor but the one in execpline() looks like it could
} be hairy because most things in execpline() are hairy.

Fixing all of these is valuable/necessary, of course; but or purposes of
this specific bug report we can discount any that had an error message
nearby, because no such errors were displayed.  It also doesn't have
anything to do with history, sourcing files, or prompts, and I wasn't
using heap validation.  That narrows it down to these:

} diff --git a/Src/exec.c b/Src/exec.c
} index a429428..9890286 100644
} --- a/Src/exec.c
} +++ b/Src/exec.c
} @@ -1795,6 +1795,8 @@ execpline(Estate state, wordcode slcode, int how, int last1)
}  		deletejob(jn, 0);
}  	    thisjob = pj;
}  	}
} +	else
} +	    unqueue_signals();
}  	if ((slflags & WC_SUBLIST_NOT) && !errflag)
}  	    lastval = !lastval;
}      }
} @@ -5556,6 +5558,7 @@ runshfunc(Eprog prog, FuncWrap wrap, char *name)
}  	if (!cont) {
}  	    if (ou)
}  		zfree(ou, ouu);
} +	    unqueue_signals();
}  	    return;
}  	}
}  	wrap = wrap->next;

It's quite possible that both of these were in play, because the
restore_queue_signals() debugging that I tried reported queueing_enabled
to be 2 when it was expected to be 0 even in cases where it did not then
continue growing.

I will clean up that additional debugging for signals.h to make it
suitable for commit, so that we have a better chance of catching these
problems in new code.


^ permalink raw reply	[flat|nested] 14+ messages in thread

* Re: [BUG] queueing_enabled grows infinitely when in .recursive-edit
  2016-10-03 11:55     ` Sebastian Gniazdowski
@ 2016-10-03 15:49       ` Bart Schaefer
  2016-10-03 16:43         ` Sebastian Gniazdowski
  2016-10-05  5:56         ` Sebastian Gniazdowski
  0 siblings, 2 replies; 14+ messages in thread
From: Bart Schaefer @ 2016-10-03 15:49 UTC (permalink / raw)
  To: Zsh hackers list

On Oct 3, 12:00pm, Sebastian Gniazdowski wrote:
} 
} On 3 October 2016 at 01:21, Bart Schaefer <schaefer@brasslantern.com> wrote:
} >
} > The first is that recursiveedit() calls zlecore() which calls
} > getkeycmd() which cascades into raw_getbyte() with do_keytmout = 0
} > which in some circumstances means that raw_getbyte() effectively
} > does a blocking read on its first call and only runs the sched
} > after a key is pressed.
} 
} It could be either way, if this is to be resolved. I mean, stopping
} sched when in .recursive-edit has some, if not total sense. It's like
} if user would be in an application.

I think that's probably best left up to the person implementing the
scheduled function.  Also minimally complicated if the code below
zlecore() doesn't have too many special cases for recursive-edit.

On Oct 3,  1:55pm, Sebastian Gniazdowski wrote:
}
} raw_getbyte() can return with ZTM_NONE while there are scheduled
} functions, often after Ctrl-C in .recursive-edit.
}  [...]
} 
} It looks like calc_timeout handles diff < 0 and diff > 0 but not diff
} == 0?

Indeed, try this:

diff --git a/Src/Zle/zle_main.c b/Src/Zle/zle_main.c
index 0b3b1fc..04b9357 100644
--- a/Src/Zle/zle_main.c
+++ b/Src/Zle/zle_main.c
@@ -471,7 +471,7 @@ calc_timeout(struct ztmout *tmoutp, long do_keytmout)
 
 	    tfdat = (Timedfn)getdata(tfnode);
 	    diff = tfdat->when - time(NULL);
-	    if (diff < 0) {
+	    if (diff <= 0) {
 		/* Already due; call it and rescan. */
 		tfdat->func();
 		continue;


^ permalink raw reply	[flat|nested] 14+ messages in thread

* Re: [BUG] queueing_enabled grows infinitely when in .recursive-edit
  2016-10-03 15:20     ` Bart Schaefer
@ 2016-10-03 16:07       ` Bart Schaefer
  0 siblings, 0 replies; 14+ messages in thread
From: Bart Schaefer @ 2016-10-03 16:07 UTC (permalink / raw)
  To: Zsh hackers list

On Oct 3,  8:20am, Bart Schaefer wrote:
}
} I will clean up that additional debugging for signals.h to make it
} suitable for commit, so that we have a better chance of catching these
} problems in new code.

Here is that.

diff --git a/Src/signals.c b/Src/signals.c
index e2587dc..9e05add 100644
--- a/Src/signals.c
+++ b/Src/signals.c
@@ -72,6 +72,10 @@ mod_export int queueing_enabled, queue_front, queue_rear;
 mod_export int signal_queue[MAX_QUEUE_SIZE];
 /**/
 mod_export sigset_t signal_mask_queue[MAX_QUEUE_SIZE];
+#ifdef DEBUG
+/**/
+mod_export int queue_in;
+#endif
 
 /* Variables used by trap queueing */
 
diff --git a/Src/signals.h b/Src/signals.h
index d680968..1904f43 100644
--- a/Src/signals.h
+++ b/Src/signals.h
@@ -82,8 +82,6 @@
 
 #define MAX_QUEUE_SIZE 128
 
-#define queue_signals()    (queueing_enabled++)
-
 #define run_queued_signals() do { \
     while (queue_front != queue_rear) {      /* while signals in queue */ \
 	sigset_t oset; \
@@ -94,12 +92,35 @@
     } \
 } while (0)
 
+#ifdef DEBUG
+
+#define queue_signals()    (queue_in++, queueing_enabled++)
+
 #define unqueue_signals()  do { \
     DPUTS(!queueing_enabled, "BUG: unqueue_signals called but not queueing"); \
+    --queue_in; \
     if (!--queueing_enabled) run_queued_signals(); \
 } while (0)
 
-#define queue_signal_level() queueing_enabled
+#define dont_queue_signals() do { \
+    queue_in = queueing_enabled; \
+    queueing_enabled = 0; \
+    run_queued_signals(); \
+} while (0)
+
+#define restore_queue_signals(q) do { \
+    DPUTS2(queueing_enabled && queue_in != q, \
+         "BUG: q = %d != queue_in = %d", q, queue_in); \
+    queue_in = (queueing_enabled = (q)); \
+} while (0)
+
+#else /* !DEBUG */
+
+#define queue_signals()    (queueing_enabled++)
+
+#define unqueue_signals()  do { \
+    if (!--queueing_enabled) run_queued_signals(); \
+} while (0)
 
 #define dont_queue_signals() do { \
     queueing_enabled = 0; \
@@ -108,6 +129,10 @@
 
 #define restore_queue_signals(q) (queueing_enabled = (q))
 
+#endif /* DEBUG */
+
+#define queue_signal_level() queueing_enabled
+
 #ifdef BSD_SIGNALS
 #define signal_block(S) sigblock(S)
 #else


^ permalink raw reply	[flat|nested] 14+ messages in thread

* Re: [BUG] queueing_enabled grows infinitely when in .recursive-edit
  2016-10-02 23:21 ` Bart Schaefer
  2016-10-03 10:00   ` Sebastian Gniazdowski
  2016-10-03 10:18   ` Peter Stephenson
@ 2016-10-03 16:33   ` Bart Schaefer
  2 siblings, 0 replies; 14+ messages in thread
From: Bart Schaefer @ 2016-10-03 16:33 UTC (permalink / raw)
  To: Zsh hackers list

On Oct 2,  4:21pm, Bart Schaefer wrote:
}
} The second is that zlecore() expects to be entered with the signal
} queue disabled, but zle widgets are called with queueing enabled, so
} recursiveedit() needs save/zero/restore the queue level around the
} call to zlecore().

Here's the patch for this.  Unchanged from 39543, but reposting because
I explicitly said 39543 was not for commit.


diff --git a/Src/Zle/zle_main.c b/Src/Zle/zle_main.c
index 0b3b1fc..09581b5 100644
--- a/Src/Zle/zle_main.c
+++ b/Src/Zle/zle_main.c
@@ -1868,11 +1868,17 @@ int
 recursiveedit(UNUSED(char **args))
 {
     int locerror;
+    int q = queue_signal_level();
+
+    /* zlecore() expects to be entered with signal queue disabled */
+    dont_queue_signals();
 
     redrawhook();
     zrefresh();
     zlecore();
 
+    restore_queue_signals(q);
+
     locerror = errflag ? 1 : 0;
     errflag = done = eofsent = 0;
 

-- 
Barton E. Schaefer


^ permalink raw reply	[flat|nested] 14+ messages in thread

* Re: [BUG] queueing_enabled grows infinitely when in .recursive-edit
  2016-10-03 15:49       ` Bart Schaefer
@ 2016-10-03 16:43         ` Sebastian Gniazdowski
  2016-10-03 18:11           ` Bart Schaefer
  2016-10-05  5:56         ` Sebastian Gniazdowski
  1 sibling, 1 reply; 14+ messages in thread
From: Sebastian Gniazdowski @ 2016-10-03 16:43 UTC (permalink / raw)
  To: Bart Schaefer; +Cc: Zsh hackers list

On 3 October 2016 at 17:49, Bart Schaefer <schaefer@brasslantern.com> wrote:
> On Oct 3, 12:00pm, Sebastian Gniazdowski wrote:
> }
> } On 3 October 2016 at 01:21, Bart Schaefer <schaefer@brasslantern.com> wrote:
> I think that's probably best left up to the person implementing the
> scheduled function.  Also minimally complicated if the code below
> zlecore() doesn't have too many special cases for recursive-edit.

So being in .recursive-edit is detectable from scheduled function? How?

> Indeed, try this:
>
> diff --git a/Src/Zle/zle_main.c b/Src/Zle/zle_main.c
> index 0b3b1fc..04b9357 100644
> --- a/Src/Zle/zle_main.c
> +++ b/Src/Zle/zle_main.c
> @@ -471,7 +471,7 @@ calc_timeout(struct ztmout *tmoutp, long do_keytmout)
>
>             tfdat = (Timedfn)getdata(tfnode);
>             diff = tfdat->when - time(NULL);
> -           if (diff < 0) {
> +           if (diff <= 0) {
>                 /* Already due; call it and rescan. */
>                 tfdat->func();
>                 continue;

It works, I have following debug messages generated after lucky Ctrl-C
issued when in .recursive-edit:

raw_getbyte() - queueing_enabled (0)
CALLING diff[0] queueing_enabled (0)
N-O-T CALLING diff[1] queueing_enabled (0)
-- INIT tmout.tp(2) ZTM_NONE(0) ZTM_KEY(1) / BEGIN RAW_GETBYTE() zle_main.c

So, calc_timeout() returns tmout.tp == 2, which is ZTM_FUNC, and does
that because the diff[0] inside-calc_timeout call of scheduled
function did apparently reschedule – the "N-O-T CALLING" entry,
generated in else{} of if(diff <=0).

Best regards,
Sebastian Gniazdowski


^ permalink raw reply	[flat|nested] 14+ messages in thread

* Re: [BUG] queueing_enabled grows infinitely when in .recursive-edit
  2016-10-03 16:43         ` Sebastian Gniazdowski
@ 2016-10-03 18:11           ` Bart Schaefer
  0 siblings, 0 replies; 14+ messages in thread
From: Bart Schaefer @ 2016-10-03 18:11 UTC (permalink / raw)
  To: Zsh hackers list

On Oct 3,  6:43pm, Sebastian Gniazdowski wrote:
}
} So being in .recursive-edit is detectable from scheduled function? How?

Specifically being in recursive-edit isn't directly detectable, but
it is unlikely someone would bind a key directly to recursive-edit.  
So either the user-defined widget that runs "zle .recursive-edit" can
define a parameter through which it communicates with the scheduled
function, or the scheduled function can examine state ($funcstack or
$zsh_eval_context) to find out whether it is "inside" a widget.


^ permalink raw reply	[flat|nested] 14+ messages in thread

* Re: [BUG] queueing_enabled grows infinitely when in .recursive-edit
  2016-10-03 15:49       ` Bart Schaefer
  2016-10-03 16:43         ` Sebastian Gniazdowski
@ 2016-10-05  5:56         ` Sebastian Gniazdowski
  2016-10-05  6:03           ` Sebastian Gniazdowski
  1 sibling, 1 reply; 14+ messages in thread
From: Sebastian Gniazdowski @ 2016-10-05  5:56 UTC (permalink / raw)
  To: Bart Schaefer; +Cc: Zsh hackers list

On 3 October 2016 at 17:49, Bart Schaefer <schaefer@brasslantern.com> wrote:
> On Oct 3, 12:00pm, Sebastian Gniazdowski wrote:
> }
> } It looks like calc_timeout handles diff < 0 and diff > 0 but not diff
> } == 0?
>
> Indeed, try this:
>
...
> -           if (diff < 0) {
> +           if (diff <= 0) {

It now appeared to me what the intention of diff < 0 could be. If one
schedules at integer SECONDS + float 0.1, then after 100 ms time(NULL)
will return different value and diff will be 0. So sched +1 scheduled
function will run after 100ms. However next call will run as expected,
near 1 second. I think this can be as it is, however maybe others
would rather see ZTM_FUNC being returned for diff=0, so that select()
still doesn't block, but sched +1 function will be ran when diff=-1.
So, it's a matter of "scheduled function will run not earlier than
scheduled time, up to 1 second late" vs. "scheduled function will run
not later than scheduled time, up to 1 second early", both an effect
of limited resolution of time(NULL).

Best regards,
Sebastian Gniazdowski


^ permalink raw reply	[flat|nested] 14+ messages in thread

* Re: [BUG] queueing_enabled grows infinitely when in .recursive-edit
  2016-10-05  5:56         ` Sebastian Gniazdowski
@ 2016-10-05  6:03           ` Sebastian Gniazdowski
  0 siblings, 0 replies; 14+ messages in thread
From: Sebastian Gniazdowski @ 2016-10-05  6:03 UTC (permalink / raw)
  To: Bart Schaefer; +Cc: Zsh hackers list

Or maybe this isn't problem at all. Any call not exactly at integer
SECONDS will be fixed to be near integer SECONDS as early as at second
scheduled call. Then, any possible drift away from integer SECONDS
will be always quickly fixed to integer SECONDS when diff == 0 is
checked at time(NULL) value change. So it's constant pull toward
integer SECONDS, nothing to worry about.


On 5 October 2016 at 07:56, Sebastian Gniazdowski
<sgniazdowski@gmail.com> wrote:
> On 3 October 2016 at 17:49, Bart Schaefer <schaefer@brasslantern.com> wrote:
>> On Oct 3, 12:00pm, Sebastian Gniazdowski wrote:
>> }
>> } It looks like calc_timeout handles diff < 0 and diff > 0 but not diff
>> } == 0?
>>
>> Indeed, try this:
>>
> ...
>> -           if (diff < 0) {
>> +           if (diff <= 0) {
>
> It now appeared to me what the intention of diff < 0 could be. If one
> schedules at integer SECONDS + float 0.1, then after 100 ms time(NULL)
> will return different value and diff will be 0. So sched +1 scheduled
> function will run after 100ms. However next call will run as expected,
> near 1 second. I think this can be as it is, however maybe others
> would rather see ZTM_FUNC being returned for diff=0, so that select()
> still doesn't block, but sched +1 function will be ran when diff=-1.
> So, it's a matter of "scheduled function will run not earlier than
> scheduled time, up to 1 second late" vs. "scheduled function will run
> not later than scheduled time, up to 1 second early", both an effect
> of limited resolution of time(NULL).
>
> Best regards,
> Sebastian Gniazdowski


^ permalink raw reply	[flat|nested] 14+ messages in thread

end of thread, other threads:[~2016-10-05  6:04 UTC | newest]

Thread overview: 14+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2016-10-02 19:00 [BUG] queueing_enabled grows infinitely when in .recursive-edit Sebastian Gniazdowski
2016-10-02 19:02 ` Sebastian Gniazdowski
2016-10-02 23:21 ` Bart Schaefer
2016-10-03 10:00   ` Sebastian Gniazdowski
2016-10-03 10:18   ` Peter Stephenson
2016-10-03 11:55     ` Sebastian Gniazdowski
2016-10-03 15:49       ` Bart Schaefer
2016-10-03 16:43         ` Sebastian Gniazdowski
2016-10-03 18:11           ` Bart Schaefer
2016-10-05  5:56         ` Sebastian Gniazdowski
2016-10-05  6:03           ` Sebastian Gniazdowski
2016-10-03 15:20     ` Bart Schaefer
2016-10-03 16:07       ` Bart Schaefer
2016-10-03 16:33   ` Bart Schaefer

Code repositories for project(s) associated with this public inbox

	https://git.vuxu.org/mirror/zsh/

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).