Hi Here is a new diff with a window option (rate-limit, 0 is off) and man page bits.
I'd like to be able to fix this in a way where we can turn it on by default but I don't want to do it now. Even with rate-limit of 100K, running "top" is visibly slower than with rate-limit disabled. I don't think that is acceptable. The lowest value before top seems to open with no delay is about 250000 but this is on a relatively small (125x33) terminal - at work for example my terminal is probably 20 times as big. This is on a LAN though so I'm not sure what impact a slow link would have. Unfortunately, the only terminal I can test right now where this option helps me with ssh and a rate-limit of 100K is gnome-terminal. In xterm and rxvt it seems to make no difference (xterm remains slow, rxvt remains fast). Also unfortunately, GNU screen behaviour matches that of tmux with rate-limit 0 in all the terminals. So I can't reproduce and figure out how screen is doing it. Anyway, can you give this a quick spin and make sure changing the option still helps you. If so, I will commit it with default disabled and see if over time some consensus about the best default appears, or I can see better behaviour from screen, or a better approach occurs to me. The disadvantage of this method is it that if you have multiple panes, they can all use the full bandwidth, so the total with 10 panes is 10 * rate-limit. Having fast producers in multiple panes would be pretty rare though. Index: cmd-paste-buffer.c =================================================================== RCS file: /cvs/src/usr.bin/tmux/cmd-paste-buffer.c,v retrieving revision 1.16 diff -u -p -r1.16 cmd-paste-buffer.c --- cmd-paste-buffer.c 4 Jan 2011 00:42:47 -0000 1.16 +++ cmd-paste-buffer.c 24 Dec 2011 07:59:45 -0000 @@ -112,11 +112,11 @@ cmd_paste_buffer_filter( seplen = strlen(sep); while ((lf = memchr(data, '\n', end - data)) != NULL) { if (lf != data) - bufferevent_write(wp->event, data, lf - data); - bufferevent_write(wp->event, sep, seplen); + window_pane_write(wp, data, lf - data); + window_pane_write(wp, sep, seplen); data = lf + 1; } if (end != data) - bufferevent_write(wp->event, data, end - data); + window_pane_write(wp, data, end - data); } Index: cmd-pipe-pane.c =================================================================== RCS file: /cvs/src/usr.bin/tmux/cmd-pipe-pane.c,v retrieving revision 1.21 diff -u -p -r1.21 cmd-pipe-pane.c --- cmd-pipe-pane.c 27 Oct 2011 22:41:03 -0000 1.21 +++ cmd-pipe-pane.c 24 Dec 2011 07:59:45 -0000 @@ -120,7 +120,7 @@ cmd_pipe_pane_exec(struct cmd *self, str close(pipe_fd[1]); wp->pipe_fd = pipe_fd[0]; - wp->pipe_off = EVBUFFER_LENGTH(wp->event->input); + wp->pipe_off = EVBUFFER_LENGTH(wp->read_buf); wp->pipe_event = bufferevent_new(wp->pipe_fd, NULL, NULL, cmd_pipe_pane_error_callback, wp); Index: input-keys.c =================================================================== RCS file: /cvs/src/usr.bin/tmux/input-keys.c,v retrieving revision 1.23 diff -u -p -r1.23 input-keys.c --- input-keys.c 30 Jul 2011 18:01:26 -0000 1.23 +++ input-keys.c 24 Dec 2011 07:59:45 -0000 @@ -151,9 +151,9 @@ input_key(struct window_pane *wp, int ke */ if (key != KEYC_NONE && (key & ~KEYC_ESCAPE) < 0x100) { if (key & KEYC_ESCAPE) - bufferevent_write(wp->event, "\033", 1); + window_pane_write(wp, "\033", 1); ch = key & ~KEYC_ESCAPE; - bufferevent_write(wp->event, &ch, 1); + window_pane_write(wp, &ch, 1); return; } @@ -163,7 +163,7 @@ input_key(struct window_pane *wp, int ke */ if (options_get_number(&wp->window->options, "xterm-keys")) { if ((out = xterm_keys_lookup(key)) != NULL) { - bufferevent_write(wp->event, out, strlen(out)); + window_pane_write(wp, out, strlen(out)); xfree(out); return; } @@ -194,8 +194,8 @@ input_key(struct window_pane *wp, int ke /* Prefix a \033 for escape. */ if (key & KEYC_ESCAPE) - bufferevent_write(wp->event, "\033", 1); - bufferevent_write(wp->event, ike->data, dlen); + window_pane_write(wp, "\033", 1); + window_pane_write(wp, ike->data, dlen); } /* Translate mouse and output. */ @@ -220,7 +220,7 @@ input_mouse(struct window_pane *wp, stru buf[len++] = m->x + 33; buf[len++] = m->y + 33; } - bufferevent_write(wp->event, buf, len); + window_pane_write(wp, buf, len); } else if ((m->b & MOUSE_BUTTON) != MOUSE_2) { value = options_get_number(&wp->window->options, "mode-mouse"); if (value == 1 && Index: input.c =================================================================== RCS file: /cvs/src/usr.bin/tmux/input.c,v retrieving revision 1.41 diff -u -p -r1.41 input.c --- input.c 23 Oct 2011 10:16:14 -0000 1.41 +++ input.c 24 Dec 2011 07:59:46 -0000 @@ -696,7 +696,7 @@ input_parse(struct window_pane *wp) { struct input_ctx *ictx = &wp->ictx; const struct input_transition *itr; - struct evbuffer *evb = wp->event->input; + struct evbuffer *evb = wp->read_buf; u_char *buf; size_t len, off; @@ -819,7 +819,7 @@ input_reply(struct input_ctx *ictx, cons vasprintf(&reply, fmt, ap); va_end(ap); - bufferevent_write(ictx->wp->event, reply, strlen(reply)); + window_pane_write(ictx->wp, reply, strlen(reply)); xfree(reply); } Index: options-table.c =================================================================== RCS file: /cvs/src/usr.bin/tmux/options-table.c,v retrieving revision 1.16 diff -u -p -r1.16 options-table.c --- options-table.c 15 Nov 2011 23:24:04 -0000 1.16 +++ options-table.c 24 Dec 2011 07:59:46 -0000 @@ -550,6 +550,13 @@ const struct options_table_entry window_ .default_num = 0 }, + { .name = "rate-limit", + .type = OPTIONS_TABLE_NUMBER, + .minimum = 0, + .maximum = UINT_MAX, + .default_num = 0 + }, + { .name = "remain-on-exit", .type = OPTIONS_TABLE_FLAG, .default_num = 0 Index: server-fn.c =================================================================== RCS file: /cvs/src/usr.bin/tmux/server-fn.c,v retrieving revision 1.52 diff -u -p -r1.52 server-fn.c --- server-fn.c 23 Oct 2011 10:16:14 -0000 1.52 +++ server-fn.c 24 Dec 2011 07:59:47 -0000 @@ -337,8 +337,10 @@ server_destroy_pane(struct window_pane * old_fd = wp->fd; if (wp->fd != -1) { + event_del(&wp->read_ev); + if (wp->flags & PANE_WRITING) + event_del(&wp->write_ev); close(wp->fd); - bufferevent_free(wp->event); wp->fd = -1; } Index: tmux.1 =================================================================== RCS file: /cvs/src/usr.bin/tmux/tmux.1,v retrieving revision 1.257 diff -u -p -r1.257 tmux.1 --- tmux.1 9 Dec 2011 16:28:18 -0000 1.257 +++ tmux.1 24 Dec 2011 07:59:48 -0000 @@ -2473,6 +2473,12 @@ Like .Ic base-index , but set the starting index for pane numbers. .Pp +.It Ic rate-limit Ar bytes +Limit output rate from any single pane to +.Ar bytes . +This option can be used to attempt to prevent excessive buffering of +fast output when the producer can be faster than the consuming terminal. +.Pp .It Xo Ic remain-on-exit .Op Ic on | off .Xc Index: tmux.h =================================================================== RCS file: /cvs/src/usr.bin/tmux/tmux.h,v retrieving revision 1.301 diff -u -p -r1.301 tmux.h --- tmux.h 9 Dec 2011 16:28:18 -0000 1.301 +++ tmux.h 24 Dec 2011 07:59:49 -0000 @@ -814,6 +814,7 @@ struct window_pane { int flags; #define PANE_REDRAW 0x1 +#define PANE_WRITING 0x2 char *cmd; char *shell; @@ -823,7 +824,11 @@ struct window_pane { char tty[TTY_NAME_MAX]; int fd; - struct bufferevent *event; + struct event read_ev; + struct evbuffer* read_buf; + struct timeval read_last; + struct event write_ev; + struct evbuffer* write_buf; struct input_ctx ictx; @@ -1933,6 +1938,8 @@ void window_set_active_pane(struct win struct window_pane *window_add_pane(struct window *, u_int); void window_resize(struct window *, u_int, u_int); void window_remove_pane(struct window *, struct window_pane *); +void window_pane_queue_write(struct window_pane *); +void window_pane_write(struct window_pane *, const void *, size_t); struct window_pane *window_pane_at_index(struct window *, u_int); struct window_pane *window_pane_next_by_number(struct window *, struct window_pane *, u_int); Index: window-copy.c =================================================================== RCS file: /cvs/src/usr.bin/tmux/window-copy.c,v retrieving revision 1.76 diff -u -p -r1.76 window-copy.c --- window-copy.c 4 Dec 2011 16:18:01 -0000 1.76 +++ window-copy.c 24 Dec 2011 07:59:50 -0000 @@ -174,8 +174,11 @@ window_copy_init(struct window_pane *wp) data->searchtype = WINDOW_COPY_OFF; data->searchstr = NULL; - if (wp->fd != -1) - bufferevent_disable(wp->event, EV_READ|EV_WRITE); + if (wp->fd != -1) { + event_del(&wp->read_ev); + if (wp->flags & PANE_WRITING) + event_del(&wp->write_ev); + } data->jumptype = WINDOW_COPY_OFF; data->jumpchar = '\0'; @@ -237,8 +240,11 @@ window_copy_free(struct window_pane *wp) { struct window_copy_mode_data *data = wp->modedata; - if (wp->fd != -1) - bufferevent_enable(wp->event, EV_READ|EV_WRITE); + if (wp->fd != -1) { + event_add(&wp->read_ev, NULL); + if (wp->flags & PANE_WRITING) + event_add(&wp->write_ev, NULL); + } if (data->searchstr != NULL) xfree(data->searchstr); Index: window.c =================================================================== RCS file: /cvs/src/usr.bin/tmux/window.c,v retrieving revision 1.69 diff -u -p -r1.69 window.c --- window.c 15 Nov 2011 23:19:51 -0000 1.69 +++ window.c 24 Dec 2011 07:59:50 -0000 @@ -60,8 +60,8 @@ struct windows windows; struct window_pane_tree all_window_panes; u_int next_window_pane; -void window_pane_read_callback(struct bufferevent *, void *); -void window_pane_error_callback(struct bufferevent *, short, void *); +void window_pane_read_callback(int, short, void *); +void window_pane_write_callback(int, short, void *); RB_GENERATE(winlinks, winlink, entry, winlink_cmp); @@ -579,7 +579,6 @@ window_pane_create(struct window *w, u_i wp->cwd = NULL; wp->fd = -1; - wp->event = NULL; wp->mode = NULL; @@ -611,8 +610,10 @@ window_pane_destroy(struct window_pane * window_pane_reset_mode(wp); if (wp->fd != -1) { + event_del(&wp->read_ev); + if (wp->flags & PANE_WRITING) + event_del(&wp->write_ev); close(wp->fd); - bufferevent_free(wp->event); } input_free(wp); @@ -647,8 +648,10 @@ window_pane_spawn(struct window_pane *wp struct termios tio2; if (wp->fd != -1) { + event_del(&wp->read_ev); + if (wp->flags & PANE_WRITING) + event_del(&wp->write_ev); close(wp->fd); - bufferevent_free(wp->event); } if (cmd != NULL) { if (wp->cmd != NULL) @@ -719,30 +722,82 @@ window_pane_spawn(struct window_pane *wp setblocking(wp->fd, 0); - wp->event = bufferevent_new(wp->fd, - window_pane_read_callback, NULL, window_pane_error_callback, wp); - bufferevent_enable(wp->event, EV_READ|EV_WRITE); + event_set(&wp->read_ev, + wp->fd, EV_READ|EV_PERSIST, window_pane_read_callback, wp); + if ((wp->read_buf = evbuffer_new ()) == NULL) + fatalx("evbuffer_new failed"); + + event_set(&wp->write_ev, + wp->fd, EV_WRITE|EV_PERSIST, window_pane_write_callback, wp); + if ((wp->write_buf = evbuffer_new ()) == NULL) + fatalx("evbuffer_new failed"); + + if (gettimeofday (&wp->read_last, NULL) != 0) + fatal("gettimeofday failed"); + event_add(&wp->read_ev, NULL); return (0); } +void +window_pane_write(struct window_pane *wp, const void *buf, size_t len) +{ + evbuffer_add(wp->write_buf, buf, len); + window_pane_queue_write(wp); +} + +void +window_pane_queue_write(struct window_pane *wp) +{ + if (wp->flags & PANE_WRITING) + return; + event_add(&wp->write_ev, NULL); + wp->flags |= PANE_WRITING; +} + /* ARGSUSED */ void -window_pane_read_callback(unused struct bufferevent *bufev, void *data) +window_pane_read_callback(unused int fd, unused short events, void* data) { struct window_pane *wp = data; + u_int limit; + struct timeval tv; + struct timeval tvdiff; + double last; + size_t size; char *new_data; size_t new_size; - new_size = EVBUFFER_LENGTH(wp->event->input) - wp->pipe_off; + if (gettimeofday(&tv, NULL) != 0) + fatal("gettimeofday failed"); + + limit = options_get_number (&wp->window->options, "rate-limit"); + if (limit != 0) { + timersub(&tv, &wp->read_last, &tvdiff); + last = tvdiff.tv_sec + (tvdiff.tv_usec / 1e6); + + size = limit * last; + if (size > limit) + size = limit; + if (size == 0) + return; + } else + size = SIZE_MAX; + memcpy(&wp->read_last, &tv, sizeof wp->read_last); + if (evbuffer_read(wp->read_buf, wp->fd, size) <= 0) { + server_destroy_pane(wp); + return; + } + + new_size = EVBUFFER_LENGTH(wp->read_buf) - wp->pipe_off; if (wp->pipe_fd != -1 && new_size > 0) { - new_data = EVBUFFER_DATA(wp->event->input); + new_data = EVBUFFER_DATA(wp->read_buf); bufferevent_write(wp->pipe_event, new_data, new_size); } input_parse(wp); - wp->pipe_off = EVBUFFER_LENGTH(wp->event->input); + wp->pipe_off = EVBUFFER_LENGTH(wp->read_buf); /* * If we get here, we're not outputting anymore, so set the silence @@ -755,12 +810,19 @@ window_pane_read_callback(unused struct /* ARGSUSED */ void -window_pane_error_callback( - unused struct bufferevent *bufev, unused short what, void *data) +window_pane_write_callback(unused int fd, unused short events, void* data) { struct window_pane *wp = data; - server_destroy_pane(wp); + if (evbuffer_write(wp->write_buf, wp->fd) <= 0) { + server_destroy_pane(wp); + return; + } + + if (EVBUFFER_LENGTH(wp->write_buf) == 0) { + event_del(&wp->write_ev); + wp->flags &= ~PANE_WRITING; + } } void On Fri, Dec 23, 2011 at 01:37:00PM -0800, Robin Lee Powell wrote: > That works wonderfully! > > I've only done basic testing with "yes $(seq 1 1000)". The highest > value I found where the response to ctrl-c was instant was > > #define BYTES_MAX 100000 > > That happens to be about the total on-screen characters of my > terminal times 5. If it was me, I'd set it to that or some similar > multiple at run time. > > I'll see about testing interactive use shortly, with that 100000 > number. > > -Robin > > On Tue, Dec 20, 2011 at 07:21:49AM +0000, Nicholas Marriott wrote: > > Ok ssh changes the game quite a lot. Can you try the diff I sent > > and see if that helps and if so what value for BYTES_MAX is > > acceptable? > > > > If it doesn't help let me know, I have another diff to rate limit > > outgoing data that might help instead. Or if not that, there is > > other stuff we can try. > > > > > > On Mon, Dec 19, 2011 at 04:47:12PM -0800, Robin Lee Powell wrote: > > > On Tue, Dec 20, 2011 at 12:04:28AM +0000, Nicholas Marriott wrote: > > > > On Mon, Dec 19, 2011 at 03:59:39PM -0800, Robin Lee Powell wrote: > > > > > I've never had this level of problem with screen, at all, and I > > > > > used it for many many years for everything. > > > > > > > > > > On Mon, Dec 19, 2011 at 11:43:04PM +0000, Nicholas Marriott > > > > > wrote: > > > > > > screen does not successfully rate limit either or if it does > > > > > > nobody has yet to clearly demonstrate a case where it does. > > > > > > It's response times for running eg "yes" and hitting ^C or > > > > > > creating a new window are roughly the same as tmux, give or > > > > > > take a few seconds. > > > > > > > > > > 0____o > > > > > > > > > > We're seeing *very* different behaviour, then. > > > > > > > > > > In a bare terminal or in a fresh screen, after running "yes" for > > > > > 30 seconds, I get my prompt back as close to instantly as makes > > > > > no difference. > > > > > > > > > > In tmux it takes 8 seconds. > > > > > > > > For me it takes 3-5 seconds for both and always has... > > > > > > Huh. > > > > > > > What terminal are you using? What size is it? What platform? > > > > > > > > Are you using tmux remotely? > > > > > > As I mentioned, I'm running PuTTY on Windows, sshing out to a Linux > > > box in the same house (over wifi). > > > > > > TERM before I run screen is "xterm". TERM in screen is "screen". > > > TERM in tmux is "screen". > > > > > > In all cases it's 132 wide and 85 high. > > > > > > -Robin > > > > > > -- > > > http://singinst.org/ : Our last, best hope for a fantastic future. > > > Lojban (http://www.lojban.org/): The language in which "this parrot > > > is dead" is "ti poi spitaki cu morsi", but "this sentence is false" > > > is "na nei". My personal page: http://www.digitalkingdom.org/rlp/ > > > > ------------------------------------------------------------------------------ > > Write once. Port to many. > > Get the SDK and tools to simplify cross-platform app development. Create > > new or port existing apps to sell to consumers worldwide. Explore the > > Intel AppUpSM program developer opportunity. appdeveloper.intel.com/join > > http://p.sf.net/sfu/intel-appdev > > _______________________________________________ > > tmux-users mailing list > > tmux-users@lists.sourceforge.net > > https://lists.sourceforge.net/lists/listinfo/tmux-users > > > > -- > http://singinst.org/ : Our last, best hope for a fantastic future. > Lojban (http://www.lojban.org/): The language in which "this parrot > is dead" is "ti poi spitaki cu morsi", but "this sentence is false" > is "na nei". My personal page: http://www.digitalkingdom.org/rlp/ ------------------------------------------------------------------------------ Write once. Port to many. Get the SDK and tools to simplify cross-platform app development. Create new or port existing apps to sell to consumers worldwide. Explore the Intel AppUpSM program developer opportunity. appdeveloper.intel.com/join http://p.sf.net/sfu/intel-appdev _______________________________________________ tmux-users mailing list tmux-users@lists.sourceforge.net https://lists.sourceforge.net/lists/listinfo/tmux-users