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

Reply via email to