When the data we have buffered to write to a terminal grows beyond a
authornicm <nicm@openbsd.org>
Wed, 19 Apr 2017 06:52:27 +0000 (06:52 +0000)
committernicm <nicm@openbsd.org>
Wed, 19 Apr 2017 06:52:27 +0000 (06:52 +0000)
reasonable amount (currently width * height * 8 bytes), discard all
output to the terminal and start trying to redraw periodically
instead. Continue with this until the amount of data we are trying to
write falls to a low level again.

This helps to prevent tmux sitting on a huge buffer of data when there
are processes with fast output running inside tmux but the outside
terminal is slow.

A new client_discarded format holds the amount of data that has been
discarded due to this mechanism.

The three variables (when to start this, when to stop, and how often to
redraw) are basically "works for me" at the moment, this is going in to
see how it goes and if it causes problems for anyone else.

usr.bin/tmux/format.c
usr.bin/tmux/server-client.c
usr.bin/tmux/tmux.1
usr.bin/tmux/tmux.h
usr.bin/tmux/tty.c

index 80dda83..fb43f42 100644 (file)
@@ -1,4 +1,4 @@
-/* $OpenBSD: format.c,v 1.125 2017/04/18 15:44:17 nicm Exp $ */
+/* $OpenBSD: format.c,v 1.126 2017/04/19 06:52:27 nicm Exp $ */
 
 /*
  * Copyright (c) 2011 Nicholas Marriott <nicholas.marriott@gmail.com>
@@ -1154,7 +1154,9 @@ format_defaults_client(struct format_tree *ft, struct client *c)
 
        format_add_tv(ft, "client_created", &c->creation_time);
        format_add_tv(ft, "client_activity", &c->activity_time);
-       format_add(ft, "client_written", "%zu", tty->written);
+
+       format_add(ft, "client_written", "%zu", c->written);
+       format_add(ft, "client_discarded", "%zu", c->discarded);
 
        name = server_client_get_key_table(c);
        if (strcmp(c->keytable->name, name) == 0)
index ef3fc1a..e053b5d 100644 (file)
@@ -1,4 +1,4 @@
-/* $OpenBSD: server-client.c,v 1.218 2017/04/18 20:37:49 nicm Exp $ */
+/* $OpenBSD: server-client.c,v 1.219 2017/04/19 06:52:27 nicm Exp $ */
 
 /*
  * Copyright (c) 2009 Nicholas Marriott <nicholas.marriott@gmail.com>
@@ -1269,8 +1269,8 @@ server_client_check_redraw(struct client *c)
                screen_redraw_update(c); /* will adjust flags */
        }
 
-       flags = tty->flags & (TTY_FREEZE|TTY_NOCURSOR);
-       tty->flags = (tty->flags & ~TTY_FREEZE) | TTY_NOCURSOR;
+       flags = tty->flags & (TTY_BLOCK|TTY_FREEZE|TTY_NOCURSOR);
+       tty->flags = (tty->flags & ~(TTY_BLOCK|TTY_FREEZE)) | TTY_NOCURSOR;
 
        if (c->flags & CLIENT_REDRAW) {
                tty_update_mode(tty, tty->mode, NULL);
index efb0ef8..8f18660 100644 (file)
@@ -1,4 +1,4 @@
-.\" $OpenBSD: tmux.1,v 1.541 2017/04/18 15:44:17 nicm Exp $
+.\" $OpenBSD: tmux.1,v 1.542 2017/04/19 06:52:27 nicm Exp $
 .\"
 .\" Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com>
 .\"
@@ -14,7 +14,7 @@
 .\" IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
 .\" OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 .\"
-.Dd $Mdocdate: April 18 2017 $
+.Dd $Mdocdate: April 19 2017 $
 .Dt TMUX 1
 .Os
 .Sh NAME
@@ -3508,6 +3508,7 @@ The following variables are available, where appropriate:
 .It Li "client_activity" Ta "" Ta "Integer time client last had activity"
 .It Li "client_created" Ta "" Ta "Integer time client created"
 .It Li "client_control_mode" Ta "" Ta "1 if client is in control mode"
+.It Li "client_discarded" Ta "" Ta "Bytes discarded when client behind"
 .It Li "client_height" Ta "" Ta "Height of client"
 .It Li "client_key_table" Ta "" Ta "Current key table"
 .It Li "client_last_session" Ta "" Ta "Name of the client's last session"
index 26b90c0..87b9a86 100644 (file)
@@ -1,4 +1,4 @@
-/* $OpenBSD: tmux.h,v 1.738 2017/04/18 21:41:42 nicm Exp $ */
+/* $OpenBSD: tmux.h,v 1.739 2017/04/19 06:52:27 nicm Exp $ */
 
 /*
  * Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com>
@@ -1043,7 +1043,8 @@ struct tty {
        struct evbuffer *in;
        struct event     event_out;
        struct evbuffer *out;
-       size_t           written;
+       struct event     timer;
+       size_t           discarded;
 
        struct termios   tio;
 
@@ -1059,6 +1060,7 @@ struct tty {
 #define TTY_STARTED 0x10
 #define TTY_OPENED 0x20
 #define TTY_FOCUS 0x40
+#define TTY_BLOCK 0x80
        int              flags;
 
        struct tty_term *term;
@@ -1306,6 +1308,9 @@ struct client {
        char            *ttyname;
        struct tty       tty;
 
+       size_t           written;
+       size_t           discarded;
+
        void            (*stdin_callback)(struct client *, int, void *);
        void            *stdin_callback_data;
        struct evbuffer *stdin_data;
index 9497943..31fab6f 100644 (file)
@@ -1,4 +1,4 @@
-/* $OpenBSD: tty.c,v 1.265 2017/04/18 21:41:42 nicm Exp $ */
+/* $OpenBSD: tty.c,v 1.266 2017/04/19 06:52:27 nicm Exp $ */
 
 /*
  * Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com>
@@ -79,6 +79,10 @@ static void  tty_default_attributes(struct tty *, const struct window_pane *,
 #define tty_pane_full_width(tty, ctx) \
        ((ctx)->xoff == 0 && screen_size_x((ctx)->wp->screen) >= (tty)->sx)
 
+#define TTY_BLOCK_INTERVAL (100000 /* 100 milliseconds */)
+#define TTY_BLOCK_START(tty) (1 + ((tty)->sx * (tty)->sy) * 8)
+#define TTY_BLOCK_STOP(tty) (1 + ((tty)->sx * (tty)->sy) / 8)
+
 void
 tty_create_log(void)
 {
@@ -171,6 +175,51 @@ tty_read_callback(__unused int fd, __unused short events, void *data)
                ;
 }
 
+static void
+tty_timer_callback(__unused int fd, __unused short events, void *data)
+{
+       struct tty      *tty = data;
+       struct client   *c = tty->client;
+       struct timeval   tv = { .tv_usec = TTY_BLOCK_INTERVAL };
+
+       log_debug("%s: %zu discarded", c->name, tty->discarded);
+
+       c->flags |= CLIENT_REDRAW;
+       c->discarded += tty->discarded;
+
+       if (tty->discarded < TTY_BLOCK_STOP(tty)) {
+               tty->flags &= ~TTY_BLOCK;
+               tty_invalidate(tty);
+               return;
+       }
+       tty->discarded = 0;
+       evtimer_add(&tty->timer, &tv);
+}
+
+static int
+tty_block_maybe(struct tty *tty)
+{
+       struct client   *c = tty->client;
+       size_t           size = EVBUFFER_LENGTH(tty->out);
+       struct timeval   tv = { .tv_usec = TTY_BLOCK_INTERVAL };
+
+       if (size < TTY_BLOCK_START(tty))
+               return (0);
+
+       if (tty->flags & TTY_BLOCK)
+               return (1);
+       tty->flags |= TTY_BLOCK;
+
+       log_debug("%s: can't keep up, %zu discarded", c->name, size);
+
+       evbuffer_drain(tty->out, size);
+       c->discarded += size;
+
+       tty->discarded = 0;
+       evtimer_add(&tty->timer, &tv);
+       return (1);
+}
+
 static void
 tty_write_callback(__unused int fd, __unused short events, void *data)
 {
@@ -184,6 +233,9 @@ tty_write_callback(__unused int fd, __unused short events, void *data)
                return;
        log_debug("%s: wrote %d bytes (of %zu)", c->name, nwrite, size);
 
+       if (tty_block_maybe(tty))
+               return;
+
        if (EVBUFFER_LENGTH(tty->out) != 0)
                event_add(&tty->event_out, NULL);
 }
@@ -198,7 +250,7 @@ tty_open(struct tty *tty, char **cause)
        }
        tty->flags |= TTY_OPENED;
 
-       tty->flags &= ~(TTY_NOCURSOR|TTY_FREEZE);
+       tty->flags &= ~(TTY_NOCURSOR|TTY_FREEZE|TTY_BLOCK|TTY_TIMER);
 
        event_set(&tty->event_in, tty->fd, EV_PERSIST|EV_READ,
            tty_read_callback, tty);
@@ -207,6 +259,8 @@ tty_open(struct tty *tty, char **cause)
        event_set(&tty->event_out, tty->fd, EV_WRITE, tty_write_callback, tty);
        tty->out = evbuffer_new();
 
+       evtimer_set(&tty->timer, tty_timer_callback, tty);
+
        tty_start_tty(tty);
 
        tty_keys_build(tty);
@@ -273,6 +327,9 @@ tty_stop_tty(struct tty *tty)
                return;
        tty->flags &= ~TTY_STARTED;
 
+       event_del(&tty->timer);
+       tty->flags &= ~TTY_BLOCK;
+
        event_del(&tty->event_in);
        event_del(&tty->event_out);
 
@@ -425,9 +482,14 @@ tty_add(struct tty *tty, const char *buf, size_t len)
 {
        struct client   *c = tty->client;
 
+       if (tty->flags & TTY_BLOCK) {
+               tty->discarded += len;
+               return;
+       }
+
        evbuffer_add(tty->out, buf, len);
        log_debug("%s: %.*s", c->name, (int)len, (const char *)buf);
-       tty->written += len;
+       c->written += len;
 
        if (tty_log_fd != -1)
                write(tty_log_fd, buf, len);