ash: [SHELL] Optimize dash -c "command" to avoid a fork
authorDenys Vlasenko <vda.linux@googlemail.com>
Tue, 25 Jul 2017 18:31:14 +0000 (20:31 +0200)
committerDenys Vlasenko <vda.linux@googlemail.com>
Tue, 25 Jul 2017 18:31:14 +0000 (20:31 +0200)
Upstream commit:

    Date: Thu, 7 Jul 2011 13:58:48 +0800
    [SHELL] Optimize dash -c "command" to avoid a fork

    On Sun, Apr 10, 2011 at 07:36:49AM +0000, Jonathan Nieder wrote:
    > From: Jilles Tjoelker <jilles@stack.nl>
    > Date: Sat, 13 Jun 2009 16:17:45 -0500
    >
    > This change only affects strings passed to -c, when the -s option is
    > not used.
    >
    > Use the EV_EXIT flag to inform the eval machinery that the string
    > being passed is the entirety of input.  This way, a fork may be
    > omitted in many special cases.
    >
    > If there are empty lines after the last command, the evalcmd will not
    > see the end early enough and forks will not be omitted. The same thing
    > seems to happen in bash.
    >
    > Example:
    >   sh -c 'ps lT'
    > No longer shows a shell process waiting for ps to finish.
    >
    > [jn: ported from FreeBSD SVN r194128.  Bugs are mine.]
    >
    > Signed-off-by: Jonathan Nieder <jrnieder@gmail.com>

    Instead of detecting EOF using the input layer, I'm going to
    use the parser instead.  In either case, we always have to read
    ahead in order to complete the parsing of the previous node.
    Therefore we always know whether there is more to come, except
    in the case where we see a newline/semicolon or similar.

    For the purposes of sh -c, this should be sufficient.

Signed-off-by: Herbert Xu <herbert@gondor.apana.org.au>
function                                             old     new   delta
evalstring                                           190     224     +34
ash_main                                            1014    1022      +8
------------------------------------------------------------------------------
(add/remove: 0/0 grow/shrink: 2/0 up/down: 42/0)               Total: 42 bytes

Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
shell/ash.c

index 42e14cbc8d2e737e2072a16c910e7ebc3765ddae..c96ec939e30983012135e8c41a0d19fb87ce8845 100644 (file)
@@ -6128,7 +6128,9 @@ struct backcmd {                /* result of evalbackcmd */
 };
 
 /* These forward decls are needed to use "eval" code for backticks handling: */
-#define EV_EXIT 01              /* exit after evaluating tree */
+/* flags in argument to evaltree */
+#define EV_EXIT    01           /* exit after evaluating tree */
+#define EV_TESTED  02           /* exit status is checked; ignore -e flag */
 static int evaltree(union node *, int);
 
 static void FAST_FUNC
@@ -8345,10 +8347,6 @@ commandcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
 static void *funcblock;         /* block to allocate function from */
 static char *funcstring_end;    /* end of block to allocate strings from */
 
-/* flags in argument to evaltree */
-#define EV_EXIT    01           /* exit after evaluating tree */
-#define EV_TESTED  02           /* exit status is checked; ignore -e flag */
-
 static const uint8_t nodesize[N_NUMBER] ALIGN1 = {
        [NCMD     ] = SHELL_ALIGN(sizeof(struct ncmd)),
        [NPIPE    ] = SHELL_ALIGN(sizeof(struct npipe)),
@@ -12491,6 +12489,12 @@ expandstr(const char *ps)
        return stackblock();
 }
 
+static inline int
+parser_eof(void)
+{
+       return tokpushback && lasttoken == TEOF;
+}
+
 /*
  * Execute a command or commands contained in a string.
  */
@@ -12526,7 +12530,7 @@ evalstring(char *s, int flags)
        while ((n = parsecmd(0)) != NODE_EOF) {
                int i;
 
-               i = evaltree(n, flags);
+               i = evaltree(n, flags & ~(parser_eof() ? 0 : EV_EXIT));
                if (n)
                        status = i;
                popstackmark(&smark);
@@ -13671,7 +13675,7 @@ int ash_main(int argc UNUSED_PARAM, char **argv)
                // if (!sflag) g_parsefile->pf_fd = -1;
                // ^^ not necessary since now we special-case fd 0
                // in is_hidden_fd() to not be considered "hidden fd"
-               evalstring(minusc, 0);
+               evalstring(minusc, sflag ? 0 : EV_EXIT);
        }
 
        if (sflag || minusc == NULL) {