From: Herbert Xu <herbert@gondor.apana.org.au>
To: DASH Mailing List <dash@vger.kernel.org>
Subject: [PATCH 3/3] input: Use tee(2) for stdin pipe
Date: Sun, 05 May 2024 17:24:12 +0800 [thread overview]
Message-ID: <45fc23774f1f20d430fa9228782b9faf73452ae5.1714900988.git.herbert@gondor.apana.org.au> (raw)
In-Reply-To: <cover.1714900988.git.herbert@gondor.apana.org.au>
use tee(2) to peek at pipes in order to avoid reading one byte at
a time.
Signed-off-by: Herbert Xu <herbert@gondor.apana.org.au>
---
configure.ac | 2 +-
src/input.c | 71 ++++++++++++++++++++++++++++++++++++++++++++++------
src/system.h | 7 ++++++
3 files changed, 72 insertions(+), 8 deletions(-)
diff --git a/configure.ac b/configure.ac
index cb55c3f..50effc0 100644
--- a/configure.ac
+++ b/configure.ac
@@ -89,7 +89,7 @@ dnl Checks for library functions.
AC_CHECK_FUNCS(bsearch faccessat getpwnam getrlimit isalpha killpg \
memfd_create mempcpy \
sigsetmask stpcpy strchrnul strsignal strtod strtoimax \
- strtoumax sysconf)
+ strtoumax sysconf tee)
dnl Check whether it's worth working around FreeBSD PR kern/125009.
dnl The traditional behavior of access/faccessat is crazy, but
diff --git a/src/input.c b/src/input.c
index b84ecec..8f8c173 100644
--- a/src/input.c
+++ b/src/input.c
@@ -57,14 +57,18 @@
#include "redir.h"
#include "shell.h"
#include "syntax.h"
+#include "system.h"
#include "trap.h"
#define IBUFSIZ (BUFSIZ + PUNGETC_MAX + 1)
+MKINIT
struct stdin_state {
tcflag_t canon;
off_t seekable;
struct termios tios;
+ int pip[2];
+ int pending;
};
MKINIT struct parsefile basepf; /* top level input file */
@@ -85,6 +89,7 @@ static int preadbuffer(void);
#ifdef mkinit
INCLUDE <stdio.h>
+INCLUDE <string.h>
INCLUDE <termios.h>
INCLUDE <unistd.h>
INCLUDE "input.h"
@@ -117,6 +122,11 @@ FORKRESET {
close(parsefile->fd);
parsefile->fd = 0;
}
+ if (stdin_state.pip[0]) {
+ close(stdin_state.pip[0]);
+ close(stdin_state.pip[1]);
+ memset(stdin_state.pip, 0, sizeof(stdin_state.pip));
+ }
}
POSTEXITRESET {
@@ -145,6 +155,43 @@ static bool stdin_bufferable(void)
return st->canon || st->seekable;
}
+static void flush_tee(void *buf, int nr, int pending)
+{
+ while (pending > 0) {
+ int err;
+
+ err = read(0, buf, nr > pending ? pending : nr);
+ if (err > 0)
+ pending -= err;
+ }
+}
+
+static int stdin_tee(void *buf, int nr)
+{
+ int err;
+
+ if (stdin_istty)
+ return 0;
+
+ if (!stdin_state.pip[0]) {
+ err = pipe(stdin_state.pip);
+ if (err < 0)
+ return err;
+ if (stdin_state.pip[0] < 10)
+ stdin_state.pip[0] = savefd(stdin_state.pip[0],
+ stdin_state.pip[0]);
+ if (stdin_state.pip[1] < 10)
+ stdin_state.pip[1] = savefd(stdin_state.pip[1],
+ stdin_state.pip[1]);
+ }
+
+ flush_tee(buf, nr, stdin_state.pending);
+
+ err = tee(0, stdin_state.pip[1], nr, 0);
+ stdin_state.pending = err;
+ return err;
+}
+
static void freestrings(struct strpush *sp)
{
INTOFF;
@@ -280,10 +327,17 @@ retry:
}
#endif
- if (!fd && !stdin_bufferable())
- nr = 1;
+ if (!fd && !stdin_bufferable()) {
+ nr = stdin_tee(buf, nr);
+ fd = stdin_state.pip[0];
+ if (nr <= 0) {
+ fd = 0;
+ nr = 1;
+ }
+ }
- nr = read(fd, buf, nr);
+ if (nr >= 0)
+ nr = read(fd, buf, nr);
if (nr < 0) {
if (errno == EINTR && !(basepf.prev && pending_sig))
@@ -621,12 +675,15 @@ void __attribute__((noinline)) flush_input(void)
{
int left = basepf.nleft + input_get_lleft(&basepf);
- if (stdin_state.seekable && left) {
- INTOFF;
+ INTOFF;
+ if (stdin_state.seekable && left)
lseek(0, -left, SEEK_CUR);
- input_set_lleft(&basepf, basepf.nleft = 0);
- INTON;
+ else if (stdin_state.pending > left) {
+ flush_tee(basebuf, BUFSIZ, stdin_state.pending - left);
+ stdin_state.pending = 0;
}
+ input_set_lleft(&basepf, basepf.nleft = 0);
+ INTON;
}
void reset_input(void)
diff --git a/src/system.h b/src/system.h
index 371c64b..371bb20 100644
--- a/src/system.h
+++ b/src/system.h
@@ -118,6 +118,13 @@ long sysconf(int) __attribute__((__noreturn__));
int isblank(int c);
#endif
+#ifndef HAVE_TEE
+static inline ssize_t tee(int fd_in, int fd_out, size_t len, unsigned int flags)
+{
+ return -1;
+}
+#endif
+
/*
* A trick to suppress uninitialized variable warning without generating any
* code
--
2.39.2
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 ` [PATCH 2/3] input: Use lseek on stdin when possible Herbert Xu
2024-05-05 9:24 ` Herbert Xu [this message]
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=45fc23774f1f20d430fa9228782b9faf73452ae5.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 read-only IMAP folder(s) and NNTP newsgroup(s).