mirror of
https://github.com/tmux/tmux.git
synced 2025-01-06 07:48:48 +00:00
When the data we have buffered to write to a terminal grows beyond a
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.
This commit is contained in:
parent
f731ae4a2d
commit
fa6deb5866
4
format.c
4
format.c
@ -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)
|
||||
|
@ -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);
|
||||
|
1
tmux.1
1
tmux.1
@ -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"
|
||||
|
7
tmux.h
7
tmux.h
@ -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;
|
||||
|
66
tty.c
66
tty.c
@ -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);
|
||||
|
Loading…
Reference in New Issue
Block a user