For example, try this which rate limits input from each pane down to
19200 bps.

You'll notice that interactive commands like "top" will noticeably
pause.

But it will have no real impact on a fast output command such as "yes"
- this is because scrolling is expensive so "yes" can do a lot of damage
with very little output, especially on a small terminal.

Bumping the rate in window.c from 19200 up to a more reasonable 115200
or so will reduce the effect even further.

And of course it eats CPU because it has to spin through the read
callback. Could maybe mitigate this with a timer if it was worth it
though.

Rate limiting output to the tty has different problems, for example you
are then limiting tmux itself as well, and a fast pane can still wash
out slower.


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  19 Dec 2011 22:59:49 -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     19 Dec 2011 22:58:56 -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        19 Dec 2011 22:59:55 -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     19 Dec 2011 23:00:10 -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: 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 19 Dec 2011 23:00:32 -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.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      19 Dec 2011 23:34:55 -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       19 Dec 2011 23:01:30 -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    19 Dec 2011 23:33:59 -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,81 @@ 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;
+       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");
+
+       timersub(&tv, &wp->read_last, &tvdiff);
+       last = tvdiff.tv_sec + (tvdiff.tv_usec / 1e6);
+
+#define BYTES_MAX 19200
+       size = BYTES_MAX * last;
+       log_debug("time %g, can read at most %zu bytes (of %zu)",
+           last, size, EVBUFFER_LENGTH(wp->read_buf));
+       if (size > BYTES_MAX)
+               size = BYTES_MAX;
+       if (size == 0)
+               return;
+
+       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 +809,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 Mon, Dec 19, 2011 at 11:20:29PM +0000, Nicholas Marriott wrote:
> This is actually quite a hard problem.
> 
> The issue is that it is difficult on a fast machine to rate limit vast,
> continuous amounts of data quickly enough and to a slow enough rate that
> tmux can remain responsive, without affecting interactive programs which
> need to send a lot of data quickly in bursts
> 
> If tmux takes too long to start rate limiting, then a whole lot of stuff
> is already in the terminal buffers and you have to wait. If it is too
> aggressive, you'll see full screen programs pause halfway through
> drawing the screen.
> 
> 
> 
> On Mon, Dec 19, 2011 at 11:03:59AM -0800, Robin Lee Powell wrote:
> > 
> > I have literally been watching the output of "perl -d:Trace" for
> > more than 10 minutes, waiting to be granted control of my terminal
> > again.  :(  I can't do any tmux commands at all.
> > 
> > -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/
> > 
> > ------------------------------------------------------------------------------
> > Learn Windows Azure Live!  Tuesday, Dec 13, 2011
> > Microsoft is holding a special Learn Windows Azure training event for 
> > developers. It will provide a great way to learn Windows Azure and what it 
> > provides. You can attend the event by watching it streamed LIVE online.  
> > Learn more at http://p.sf.net/sfu/ms-windowsazure
> > _______________________________________________
> > tmux-users mailing list
> > tmux-users@lists.sourceforge.net
> > https://lists.sourceforge.net/lists/listinfo/tmux-users

------------------------------------------------------------------------------
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