[git commit] less: do not retry reads of stdin if got EAGAIN
Denys Vlasenko
vda.linux at googlemail.com
Fri Feb 27 07:09:30 UTC 2026
commit: https://git.busybox.net/busybox/commit/?id=f51819afa57b085e55776ca4b9dc29215384e1ec
branch: https://git.busybox.net/busybox/log/?h=master
Also: fewer syscalls to set/clear O_NONBLOCK, skip trying to read keyboard
if poll() told us it's not ready.
function old new delta
reinitialize 184 198 +14
getch_nowait 251 258 +7
sched_yield 25 - -25
read_lines 815 702 -113
------------------------------------------------------------------------------
(add/remove: 0/2 grow/shrink: 2/1 up/down: 21/-138) Total: -117 bytes
Signed-off-by: Denys Vlasenko <vda.linux at googlemail.com>
---
miscutils/less.c | 70 ++++++++++++++++++++++++++++----------------------------
1 file changed, 35 insertions(+), 35 deletions(-)
diff --git a/miscutils/less.c b/miscutils/less.c
index e24500591..65a8c4bed 100644
--- a/miscutils/less.c
+++ b/miscutils/less.c
@@ -201,9 +201,10 @@ struct globals {
#if ENABLE_FEATURE_LESS_WINCH
unsigned winch_counter;
#endif
- ssize_t eof_error; /* eof if 0, error if < 0 */
- ssize_t readpos;
- ssize_t readeof; /* must be signed */
+ int stdin_GETFL_flags;
+ ssize_t eof_error; /* eof if 0, error if < 0, > 0 if last read got some chars */
+ size_t readpos;
+ size_t read_size;
const char **buffer;
const char **flines;
const char *empty_line_marker;
@@ -253,7 +254,7 @@ struct globals {
#define WINCH_COUNTER (*(volatile unsigned *)&winch_counter)
#define eof_error (G.eof_error )
#define readpos (G.readpos )
-#define readeof (G.readeof )
+#define read_size (G.read_size )
#define buffer (G.buffer )
#define flines (G.flines )
#define empty_line_marker (G.empty_line_marker )
@@ -458,7 +459,7 @@ static int at_end(void)
* that it was seen])
* max_lineno - last line's number, this one doesn't increment
* on line wrap, only on "real" new lines.
- * readbuf[0..readeof-1] - small preliminary buffer.
+ * readbuf[0..read_size-1] - small preliminary buffer.
* readbuf[readpos] - next character to add to current line.
* last_line_pos - screen line position of next char to be read
* (takes into account tabs and backspaces)
@@ -472,8 +473,6 @@ static void read_lines(void)
char *current_line, *p;
int w = width;
char last_terminated = terminated;
- time_t last_time = 0;
- int retry_EAGAIN = 2;
#if ENABLE_FEATURE_LESS_REGEXP
unsigned old_max_fline = max_fline;
#endif
@@ -505,30 +504,23 @@ static void read_lines(void)
while (1) { /* read chars until we have a line */
char c;
/* if no unprocessed chars left, eat more */
- if (readpos >= readeof) {
- int flags = ndelay_on(0);
+ if (readpos >= read_size) {
+ // Read stdin, temporarily making it nonblocking (if it's not already).
+ // NB: we do NOT check eof_error status before reading.
+ // This has the effect that e.g. PageDown on a regular file
+ // where we already reached EOF *will try reading anyway*,
+ // if the file is a growing log file, less *will* show the new data.
+ int flags = G.stdin_GETFL_flags;
+ if (!(flags & O_NONBLOCK))
+ fcntl(STDIN_FILENO, F_SETFL, flags|O_NONBLOCK);
+ eof_error = safe_read(STDIN_FILENO, readbuf, COMMON_BUFSIZE);
+ if (!(flags & O_NONBLOCK))
+ fcntl(STDIN_FILENO, F_SETFL, flags);
- while (1) {
- time_t t;
-
- errno = 0;
- eof_error = safe_read(STDIN_FILENO, readbuf, COMMON_BUFSIZE);
- if (errno != EAGAIN)
- break;
- t = time(NULL);
- if (t != last_time) {
- last_time = t;
- if (--retry_EAGAIN < 0)
- break;
- }
- sched_yield();
- }
- fcntl(0, F_SETFL, flags); /* ndelay_off(0) */
readpos = 0;
- readeof = eof_error;
+ read_size = (eof_error < 0 ? 0 : eof_error);
if (eof_error <= 0)
goto reached_eof;
- retry_EAGAIN = 1;
}
c = readbuf[readpos];
/* backspace? [needed for manpages] */
@@ -632,7 +624,7 @@ static void read_lines(void)
if (eof_error < 0) {
if (errno == EAGAIN) {
- eof_error = 1;
+ eof_error = 1; /* "neither EOF nor error" */
} else {
print_statusline(bb_msg_read_error);
}
@@ -914,7 +906,7 @@ static void buffer_print(void)
if ((option_mask32 & (FLAG_E|FLAG_F))
&& eof_error <= 0
) {
- i = option_mask32 & FLAG_F ? 0 : cur_fline;
+ i = (option_mask32 & FLAG_F) ? 0 : cur_fline;
if (max_fline - i <= max_displayed_line)
less_exit();
}
@@ -1063,8 +1055,9 @@ static void open_file_and_read_lines(void)
num_lines = REOPEN_STDIN;
#endif
}
+ G.stdin_GETFL_flags = fcntl(STDIN_FILENO, F_GETFL);
readpos = 0;
- readeof = 0;
+ read_size = 0;
last_line_pos = 0;
terminated = 1;
read_lines();
@@ -1093,9 +1086,13 @@ static void reinitialize(void)
buffer_fill_and_print();
}
+/* Poll stdin and keyboard.
+ * If stdin has more data, redraw and repeat.
+ * Return keycode when a key is pressed.
+ */
static int64_t getch_nowait(void)
{
- int rd;
+ int dont_poll_stdin;
int64_t key64;
struct pollfd pfd[2];
@@ -1111,11 +1108,11 @@ static int64_t getch_nowait(void)
* Even if select/poll says that input is available, read CAN block
* (switch fd into O_NONBLOCK'ed mode to avoid it)
*/
- rd = 1;
+ dont_poll_stdin = 1;
/* Are we interested in stdin? */
if (at_end()) {
if (eof_error > 0) /* did NOT reach eof yet */
- rd = 0; /* yes, we are interested in stdin */
+ dont_poll_stdin = 0; /* yes, we are interested in stdin */
}
/* Position cursor if line input is done */
if (less_gets_pos >= 0)
@@ -1127,21 +1124,24 @@ static int64_t getch_nowait(void)
while (1) {
int r;
/* NB: SIGWINCH interrupts poll() */
- r = poll(pfd + rd, 2 - rd, -1);
+ r = poll(pfd + dont_poll_stdin, 2 - dont_poll_stdin, -1);
if (/*r < 0 && errno == EINTR &&*/ winch_counter)
return '\\'; /* anything which has no defined function */
if (r) break;
}
#else
- safe_poll(pfd + rd, 2 - rd, -1);
+ safe_poll(pfd + dont_poll_stdin, 2 - dont_poll_stdin, -1);
#endif
}
+ if (pfd[1].revents == 0)
+ goto no_kbd_input;
/* We have kbd_fd in O_NONBLOCK mode, read inside safe_read_key()
* would not block even if there is no input available */
key64 = safe_read_key(kbd_fd, kbd_input, /*do not poll:*/ -2);
if ((int)key64 == -1) {
if (errno == EAGAIN) {
+ no_kbd_input:
/* No keyboard input available. Since poll() did return,
* we should have input on stdin */
read_lines();
More information about the busybox-cvs
mailing list