From: Herbert Xu <herbert@gondor.apana.org.au>
To: DASH Mailing List <dash@vger.kernel.org>
Subject: [PATCH 2/3] input: Use lseek on stdin when possible
Date: Sun, 05 May 2024 17:24:09 +0800 [thread overview]
Message-ID: <d0f098d40f68666aedcaa3374af8ce19dbd1e34f.1714900988.git.herbert@gondor.apana.org.au> (raw)
In-Reply-To: <cover.1714900988.git.herbert@gondor.apana.org.au>
For files that can be sought, use lseek instead of reading one
byte at a time.
Signed-off-by: Herbert Xu <herbert@gondor.apana.org.au>
---
src/eval.c | 3 ++
src/init.h | 1 +
src/input.c | 108 +++++++++++++++++++++++++++++++++++---------------
src/input.h | 4 ++
src/jobs.c | 3 ++
src/mkinit.c | 6 +++
src/options.c | 2 +-
src/redir.c | 26 +++++++-----
src/trap.c | 1 +
9 files changed, 111 insertions(+), 43 deletions(-)
diff --git a/src/eval.c b/src/eval.c
index d169eb8..bd31a76 100644
--- a/src/eval.c
+++ b/src/eval.c
@@ -895,6 +895,8 @@ bail:
goto bail;
default:
+ flush_input();
+
/* Fork off a child process if necessary. */
if (!(flags & EV_EXIT) || have_traps()) {
INTOFF;
@@ -1130,6 +1132,7 @@ execcmd(int argc, char **argv)
iflag = 0; /* exit on error */
mflag = 0;
optschanged();
+ flush_input();
shellexec(argv + 1, pathval(), 0);
}
return 0;
diff --git a/src/init.h b/src/init.h
index 4f98b5d..e117895 100644
--- a/src/init.h
+++ b/src/init.h
@@ -39,4 +39,5 @@ union node;
void init(void);
void exitreset(void);
void forkreset(union node *);
+void postexitreset(void);
void reset(void);
diff --git a/src/input.c b/src/input.c
index 193235d..b84ecec 100644
--- a/src/input.c
+++ b/src/input.c
@@ -32,11 +32,13 @@
* SUCH DAMAGE.
*/
-#include <stdio.h> /* defines BUFSIZ */
#include <fcntl.h>
-#include <unistd.h>
+#include <stdbool.h>
+#include <stdio.h> /* defines BUFSIZ */
#include <stdlib.h>
#include <string.h>
+#include <termios.h>
+#include <unistd.h>
/*
* This file implements the input routines used by the parser.
@@ -59,12 +61,21 @@
#define IBUFSIZ (BUFSIZ + PUNGETC_MAX + 1)
+struct stdin_state {
+ tcflag_t canon;
+ off_t seekable;
+ struct termios tios;
+};
MKINIT struct parsefile basepf; /* top level input file */
MKINIT char basebuf[IBUFSIZ]; /* buffer for top level input file */
MKINIT struct parsefile *toppf = &basepf;
+MKINIT struct stdin_state stdin_state;
struct parsefile *parsefile = &basepf; /* current input file */
int whichprompt; /* 1 == PS1, 2 == PS2 */
+int stdin_istty;
+
+MKINIT void input_init(void);
STATIC void pushfile(void);
static void popstring(void);
@@ -74,6 +85,7 @@ static int preadbuffer(void);
#ifdef mkinit
INCLUDE <stdio.h>
+INCLUDE <termios.h>
INCLUDE <unistd.h>
INCLUDE "input.h"
INCLUDE "error.h"
@@ -82,6 +94,8 @@ INCLUDE "syntax.h"
INIT {
basepf.nextc = basepf.buf = basebuf;
basepf.linno = 1;
+
+ input_init();
}
RESET {
@@ -104,8 +118,32 @@ FORKRESET {
parsefile->fd = 0;
}
}
+
+POSTEXITRESET {
+ flush_input();
+}
#endif
+void input_init(void)
+{
+ struct stdin_state *st = &stdin_state;
+ int istty;
+
+ istty = tcgetattr(0, &st->tios) + 1;
+ st->seekable = istty ? 0 : lseek(0, 0, SEEK_CUR) + 1;
+ st->canon = istty ? st->tios.c_lflag & ICANON : 0;
+ stdin_istty = istty;
+}
+
+static bool stdin_bufferable(void)
+{
+ struct stdin_state *st = &stdin_state;
+
+ if (stdin_istty < 0)
+ input_init();
+
+ return st->canon || st->seekable;
+}
static void freestrings(struct strpush *sp)
{
@@ -191,6 +229,7 @@ static int
preadfd(void)
{
char *buf = parsefile->buf;
+ int fd = parsefile->fd;
int unget;
int pnr;
int nr;
@@ -214,7 +253,7 @@ preadfd(void)
retry:
nr = pnr;
#ifndef SMALL
- if (parsefile->fd == 0 && el) {
+ if (fd == 0 && el) {
static const char *rl_cp;
static int el_len;
@@ -237,38 +276,23 @@ retry:
rl_cp = 0;
}
- } else
-#endif
- if (parsefile->fd)
- nr = read(parsefile->fd, buf, nr);
- else {
- nr = 0;
-
- do {
- int err;
-
- err = read(0, buf, 1);
- if (err <= 0) {
- if (nr)
- break;
-
- nr = err;
- if (errno != EWOULDBLOCK)
- break;
- if (stdin_clear_nonblock() < 0)
- break;
-
- out2str("sh: turning off NDELAY mode\n");
- goto retry;
- }
-
- nr++;
- } while (0);
+ return nr;
}
+#endif
+
+ if (!fd && !stdin_bufferable())
+ nr = 1;
+
+ nr = read(fd, buf, nr);
if (nr < 0) {
if (errno == EINTR && !(basepf.prev && pending_sig))
goto retry;
+ if (fd == 0 && errno == EWOULDBLOCK &&
+ stdin_clear_nonblock() >= 0) {
+ out2str("sh: turning off NDELAY mode\n");
+ goto retry;
+ }
}
return nr;
}
@@ -302,6 +326,8 @@ static int preadbuffer(void)
something = !first;
more = input_get_lleft(parsefile);
+
+ INTOFF;
if (more <= 0) {
int nr;
@@ -313,6 +339,7 @@ again:
input_set_lleft(parsefile, parsefile->nleft = 0);
if (!IS_DEFINED_SMALL && nr > 0)
goto save;
+ INTON;
return PEOF;
}
}
@@ -365,11 +392,10 @@ save:
if (parsefile->fd == 0 && hist && something) {
HistEvent he;
- INTOFF;
history(hist, &he, first ? H_ENTER : H_APPEND,
parsefile->nextc);
- INTON;
}
+ INTON;
if (vflag) {
out2str(parsefile->nextc);
@@ -590,3 +616,21 @@ popallfiles(void)
{
unwindfiles(toppf);
}
+
+void __attribute__((noinline)) flush_input(void)
+{
+ int left = basepf.nleft + input_get_lleft(&basepf);
+
+ if (stdin_state.seekable && left) {
+ INTOFF;
+ lseek(0, -left, SEEK_CUR);
+ input_set_lleft(&basepf, basepf.nleft = 0);
+ INTON;
+ }
+}
+
+void reset_input(void)
+{
+ flush_input();
+ stdin_istty = -1;
+}
diff --git a/src/input.h b/src/input.h
index c59d784..af1c1be 100644
--- a/src/input.h
+++ b/src/input.h
@@ -35,6 +35,7 @@
*/
#include <limits.h>
+#include <stdbool.h>
#ifdef SMALL
#define IS_DEFINED_SMALL 1
@@ -94,6 +95,7 @@ struct parsefile {
};
extern struct parsefile *parsefile;
+extern int stdin_istty;
/*
* The input line number. Input.c just defines this variable, and saves
@@ -113,6 +115,8 @@ void pushstdin(void);
void popfile(void);
void unwindfiles(struct parsefile *);
void popallfiles(void);
+void flush_input(void);
+void reset_input(void);
static inline int input_get_lleft(struct parsefile *pf)
{
diff --git a/src/jobs.c b/src/jobs.c
index 4cf6b8c..5765e6d 100644
--- a/src/jobs.c
+++ b/src/jobs.c
@@ -968,6 +968,9 @@ forkshell(struct job *jp, union node *n, int mode)
int pid;
TRACE(("forkshell(%%%d, %p, %d) called\n", jobno(jp), n, mode));
+
+ flush_input();
+
pid = fork();
if (pid == 0)
forkchild(jp, n, mode);
diff --git a/src/mkinit.c b/src/mkinit.c
index 870b64d..2514ebf 100644
--- a/src/mkinit.c
+++ b/src/mkinit.c
@@ -119,6 +119,11 @@ char forkreset[] = "\
* This routine is called when we enter a subshell.\n\
*/\n";
+char postexitreset[] = "\
+/*\n\
+ * This routine is called in exitshell.\n\
+ */\n";
+
char reset[] = "\
/*\n\
* This routine is called when an error or an interrupt occurs in an\n\
@@ -130,6 +135,7 @@ struct event event[] = {
{"INIT", "init", init},
{"EXITRESET", "exitreset", exitreset},
{"FORKRESET", "forkreset", forkreset, "union node *n"},
+ {"POSTEXITRESET", "postexitreset", postexitreset},
{"RESET", "reset", reset},
{NULL, NULL}
};
diff --git a/src/options.c b/src/options.c
index f157321..4d0a53a 100644
--- a/src/options.c
+++ b/src/options.c
@@ -142,7 +142,7 @@ procargs(int argc, char **argv)
sh_error("-c requires an argument");
sflag = 1;
}
- if (iflag == 2 && sflag == 1 && isatty(0) && isatty(1))
+ if (iflag == 2 && sflag == 1 && stdin_istty && isatty(1))
iflag = 1;
if (mflag == 2)
mflag = iflag;
diff --git a/src/redir.c b/src/redir.c
index 2505d49..6726eab 100644
--- a/src/redir.c
+++ b/src/redir.c
@@ -46,16 +46,17 @@
* Code for dealing with input/output redirection.
*/
-#include "main.h"
-#include "shell.h"
-#include "nodes.h"
-#include "jobs.h"
-#include "options.h"
-#include "expand.h"
-#include "redir.h"
-#include "output.h"
-#include "memalloc.h"
#include "error.h"
+#include "expand.h"
+#include "input.h"
+#include "jobs.h"
+#include "main.h"
+#include "memalloc.h"
+#include "nodes.h"
+#include "options.h"
+#include "output.h"
+#include "redir.h"
+#include "shell.h"
#include "trap.h"
@@ -141,6 +142,8 @@ redirect(union node *redir, int flags)
continue;
fd = n->nfile.fd;
+ if (fd == 0)
+ reset_input();
if (sv) {
int closed;
@@ -414,8 +417,11 @@ popredir(int drop)
close(i);
break;
default:
- if (!drop)
+ if (!drop) {
+ if (i == 0)
+ reset_input();
dup2(rp->renamed[i], i);
+ }
close(rp->renamed[i]);
break;
}
diff --git a/src/trap.c b/src/trap.c
index f871656..2351b61 100644
--- a/src/trap.c
+++ b/src/trap.c
@@ -426,6 +426,7 @@ exitshell(void)
}
out:
exitreset();
+ postexitreset();
/*
* Disable job control so that whoever had the foreground before we
* started can get it back.
--
2.39.2
next prev parent reply other threads:[~2024-05-05 9:24 UTC|newest]
Thread overview: 4+ messages / expand[flat|nested] mbox.gz Atom feed top
2024-05-05 9:24 [PATCH 0/3] Improve performance when reading stdin Herbert Xu
2024-05-05 9:24 ` [PATCH 1/3] input: Move newline loop into preadbuffer Herbert Xu
2024-05-05 9:24 ` Herbert Xu [this message]
2024-05-05 9:24 ` [PATCH 3/3] input: Use tee(2) for stdin pipe Herbert Xu
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=d0f098d40f68666aedcaa3374af8ce19dbd1e34f.1714900988.git.herbert@gondor.apana.org.au \
--to=herbert@gondor.apana.org.au \
--cc=dash@vger.kernel.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
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).