Greetings list, The attached patch adds options to new-window, new-session, and split-window (commands which create panes) to signal a wait_channel when that pane is destroyed, for example after the contained process exits. Towards this end, it changes the life-cycle management code in cmd-wait-for.c to allow external holds on a wait_channel structure; these are only taken, at the moment, by the aforementioned commands.
It's something of an afternoon hack-job, so it might not be ideal. Comments and criticisms welcome; rotten tomatoes possibly understood but less welcome. ;) Cheers! --nwf;
diff --git a/cmd-new-session.c b/cmd-new-session.c index b36de70..4cf72fc 100644 --- a/cmd-new-session.c +++ b/cmd-new-session.c @@ -38,7 +38,7 @@ const struct cmd_entry cmd_new_session_entry = { "Ac:dDF:n:Ps:t:x:y:", 0, -1, "[-AdDP] [-c start-directory] [-F format] [-n window-name] " "[-s session-name] " CMD_TARGET_SESSION_USAGE " [-x width] " - "[-y height] [command]", + "[-y height] [-W channel] [command]", CMD_STARTSERVER|CMD_CANTNEST, NULL, cmd_new_session_exec @@ -61,6 +61,7 @@ cmd_new_session_exec(struct cmd *self, struct cmd_q *cmdq) u_int sx, sy; struct format_tree *ft; struct environ_entry *envent; + struct wait_channel *wc = NULL; if (args_has(args, 't') && (args->argc != 0 || args_has(args, 'n'))) { cmdq_error(cmdq, "command or window name given with target"); @@ -214,10 +215,13 @@ cmd_new_session_exec(struct cmd *self, struct cmd_q *cmdq) if (c != NULL) environ_update(update, &c->environ, &env); + if (args_has(args, 'W')) + wc = wait_channel_hold_by_name(args_get(args,'W')); + /* Create the new session. */ idx = -1 - options_get_number(&global_s_options, "base-index"); s = session_create(newname, argc, argv, path, cwd, &env, tiop, idx, sx, - sy, &cause); + sy, wc, &cause); if (s == NULL) { cmdq_error(cmdq, "create session failed: %s", cause); free(cause); diff --git a/cmd-new-window.c b/cmd-new-window.c index cd0042e..d5987e8 100644 --- a/cmd-new-window.c +++ b/cmd-new-window.c @@ -34,8 +34,8 @@ enum cmd_retval cmd_new_window_exec(struct cmd *, struct cmd_q *); const struct cmd_entry cmd_new_window_entry = { "new-window", "neww", - "ac:dF:kn:Pt:", 0, -1, - "[-adkP] [-c start-directory] [-F format] [-n window-name] " + "ac:dF:kn:Pt:W:", 0, -1, + "[-adkP] [-c start-directory] [-F format] [-n window-name] [-W channel]" CMD_TARGET_WINDOW_USAGE " [command]", 0, NULL, @@ -54,6 +54,7 @@ cmd_new_window_exec(struct cmd *self, struct cmd_q *cmdq) int argc, idx, last, detached, cwd, fd = -1; struct format_tree *ft; struct environ_entry *envent; + struct wait_channel *wc = NULL; if (args_has(args, 'a')) { wl = cmd_find_window(cmdq, args_get(args, 't'), &s); @@ -152,10 +153,13 @@ cmd_new_window_exec(struct cmd *self, struct cmd_q *cmdq) } } + if (args_has(args, 'W')) + wc = wait_channel_hold_by_name(args_get(args, 'W')); + if (idx == -1) idx = -1 - options_get_number(&s->options, "base-index"); wl = session_new(s, args_get(args, 'n'), argc, argv, path, cwd, idx, - &cause); + wc, &cause); if (wl == NULL) { cmdq_error(cmdq, "create window failed: %s", cause); free(cause); diff --git a/cmd-split-window.c b/cmd-split-window.c index ea047a3..9a4fc15 100644 --- a/cmd-split-window.c +++ b/cmd-split-window.c @@ -37,6 +37,7 @@ const struct cmd_entry cmd_split_window_entry = { "split-window", "splitw", "c:dF:l:hp:Pt:v", 0, -1, "[-dhvP] [-c start-directory] [-F format] [-p percentage|-l size] " + "[-W channel] " CMD_TARGET_PANE_USAGE " [command]", 0, cmd_split_window_key_binding, @@ -158,6 +159,10 @@ cmd_split_window_exec(struct cmd *self, struct cmd_q *cmdq) } new_wp = window_add_pane(w, hlimit); + if (args_has(args, 'W')) + new_wp->signal_on_destroy = + wait_channel_hold_by_name(args_get(args, 'W')); + path = NULL; if (cmdq->client != NULL && cmdq->client->session == NULL) envent = environ_find(&cmdq->client->environ, "PATH"); diff --git a/cmd-wait-for.c b/cmd-wait-for.c index e251863..f0dd92b 100644 --- a/cmd-wait-for.c +++ b/cmd-wait-for.c @@ -43,6 +43,7 @@ struct wait_channel { const char *name; int locked; + u_int holds; TAILQ_HEAD(, cmd_q) waiters; TAILQ_HEAD(, cmd_q) lockers; @@ -70,6 +71,70 @@ enum cmd_retval cmd_wait_for_lock(struct cmd_q *, const char *, enum cmd_retval cmd_wait_for_unlock(struct cmd_q *, const char *, struct wait_channel *); +static struct wait_channel * +wait_channel_find_by_name(const char *name) { + struct wait_channel wc0; + + wc0.name = name; + return RB_FIND(wait_channels, &wait_channels, &wc0); +} + +static struct wait_channel * +wait_channel_alloc(const char *name) { + struct wait_channel *wc = xmalloc(sizeof *wc); + wc->name = xstrdup(name); + wc->locked = 0; + TAILQ_INIT(&wc->waiters); + TAILQ_INIT(&wc->lockers); + RB_INSERT(wait_channels, &wait_channels, wc); + + return wc; +} + +static void +wait_channel_try_free(struct wait_channel *wc) { + if (wc->holds > 0) + return; + + if (!TAILQ_EMPTY(&wc->waiters)) + return; + + if (!TAILQ_EMPTY(&wc->lockers)) { + /* Should we assert that wc->locked == 1 ? */ + return; + } + + /* All references to this wait_channel are gone */ + RB_REMOVE(wait_channels, &wait_channels, wc); + free((void*) wc->name); + free(wc); +} + +static void +wait_channel_hold(struct wait_channel *wc) { + wc->holds++; +} + +struct wait_channel * +wait_channel_hold_by_name(const char *name) { + struct wait_channel *wc = wait_channel_find_by_name(name); + + if(!wc) + wc = wait_channel_alloc(name); + + wait_channel_hold(wc); + + return wc; +} + +void +wait_channel_unhold(struct wait_channel *wc) { + if(--wc->holds != 0) + return; + + wait_channel_try_free(wc); +} + enum cmd_retval cmd_wait_for_exec(struct cmd *self, struct cmd_q *cmdq) { @@ -89,28 +154,29 @@ cmd_wait_for_exec(struct cmd *self, struct cmd_q *cmdq) return (cmd_wait_for_wait(cmdq, name, wc)); } +void +wait_channel_signal(struct wait_channel *wc) { + struct cmd_q *wq, *wq1; + + TAILQ_FOREACH_SAFE(wq, &wc->waiters, waitentry, wq1) { + TAILQ_REMOVE(&wc->waiters, wq, waitentry); + if (!cmdq_free(wq)) + cmdq_continue(wq); + } +} + enum cmd_retval cmd_wait_for_signal(struct cmd_q *cmdq, const char *name, struct wait_channel *wc) { - struct cmd_q *wq, *wq1; - if (wc == NULL || TAILQ_EMPTY(&wc->waiters)) { cmdq_error(cmdq, "no waiting clients on %s", name); return (CMD_RETURN_ERROR); } - TAILQ_FOREACH_SAFE(wq, &wc->waiters, waitentry, wq1) { - TAILQ_REMOVE(&wc->waiters, wq, waitentry); - if (!cmdq_free(wq)) - cmdq_continue(wq); - } + wait_channel_signal(wc); - if (!wc->locked) { - RB_REMOVE(wait_channels, &wait_channels, wc); - free((void*) wc->name); - free(wc); - } + wait_channel_try_free(wc); return (CMD_RETURN_NORMAL); } @@ -124,14 +190,8 @@ cmd_wait_for_wait(struct cmd_q *cmdq, const char *name, return (CMD_RETURN_ERROR); } - if (wc == NULL) { - wc = xmalloc(sizeof *wc); - wc->name = xstrdup(name); - wc->locked = 0; - TAILQ_INIT(&wc->waiters); - TAILQ_INIT(&wc->lockers); - RB_INSERT(wait_channels, &wait_channels, wc); - } + if (wc == NULL) + wc = wait_channel_alloc(name); TAILQ_INSERT_TAIL(&wc->waiters, cmdq, waitentry); cmdq->references++; @@ -184,11 +244,7 @@ cmd_wait_for_unlock(struct cmd_q *cmdq, const char *name, cmdq_continue(wq); } else { wc->locked = 0; - if (TAILQ_EMPTY(&wc->waiters)) { - RB_REMOVE(wait_channels, &wait_channels, wc); - free((void*) wc->name); - free(wc); - } + wait_channel_try_free(wc); } return (CMD_RETURN_NORMAL); diff --git a/session.c b/session.c index 2bcb1b9..bd50021 100644 --- a/session.c +++ b/session.c @@ -86,7 +86,7 @@ session_find_by_id(u_int id) struct session * session_create(const char *name, int argc, char **argv, const char *path, int cwd, struct environ *env, struct termios *tio, int idx, u_int sx, - u_int sy, char **cause) + u_int sy, struct wait_channel *wc, char **cause) { struct session *s; struct winlink *wl; @@ -133,7 +133,7 @@ session_create(const char *name, int argc, char **argv, const char *path, RB_INSERT(sessions, &sessions, s); if (argc >= 0) { - wl = session_new(s, NULL, argc, argv, path, cwd, idx, cause); + wl = session_new(s, NULL, argc, argv, path, cwd, idx, wc, cause); if (wl == NULL) { session_destroy(s); return (NULL); @@ -229,7 +229,7 @@ session_previous_session(struct session *s) /* Create a new window on a session. */ struct winlink * session_new(struct session *s, const char *name, int argc, char **argv, - const char *path, int cwd, int idx, char **cause) + const char *path, int cwd, int idx, struct wait_channel *wc, char **cause) { struct window *w; struct winlink *wl; @@ -253,7 +253,7 @@ session_new(struct session *s, const char *name, int argc, char **argv, hlimit = options_get_number(&s->options, "history-limit"); w = window_create(name, argc, argv, path, shell, cwd, &env, s->tio, - s->sx, s->sy, hlimit, cause); + s->sx, s->sy, hlimit, wc, cause); if (w == NULL) { winlink_remove(&s->windows, wl); environ_free(&env); diff --git a/tmux.1 b/tmux.1 index 924157d..1b51cb9 100644 --- a/tmux.1 +++ b/tmux.1 @@ -709,6 +709,7 @@ Lock all clients attached to .Op Fl t Ar target-session .Op Fl x Ar width .Op Fl y Ar height +.Op Fl W Ar channel .Op Ar shell-command .Xc .D1 (alias: Ic new ) @@ -776,6 +777,15 @@ By default, it uses the format .Ql #{session_name}: but a different format may be specified with .Fl F . +.Pp +The +.Fl W +option specifies a channel name which will be signaled when the created pane +is destroyed. Clients may use the +.Ic wait-for +command to suspend until this happens. +.Ic new-session +does not lock the channel. .It Xo Ic refresh-client .Op Fl S .Op Fl t Ar target-client @@ -1498,6 +1508,7 @@ option. .Op Fl F Ar format .Op Fl n Ar window-name .Op Fl t Ar target-window +.Op Fl W Ar channel .Op Ar shell-command .Xc .D1 (alias: Ic neww ) @@ -1553,6 +1564,15 @@ By default, it uses the format .Ql #{session_name}:#{window_index} but a different format may be specified with .Fl F . +.Pp +The +.Fl W +option specifies a channel name which will be signaled when the created pane +is destroyed. Clients may use the +.Ic wait-for +command to suspend until this happens. +.Ic new-window +does not lock the channel. .It Ic next-layout Op Fl t Ar target-window .D1 (alias: Ic nextl ) Move a window to the next layout and rearrange the panes to fit. @@ -1754,6 +1774,7 @@ the command behaves like .Ar size | .Fl p Ar percentage Oc .Op Fl t Ar target-pane +.Op Fl W Ar channel .Op Ar shell-command .Op Fl F Ar format .Xc diff --git a/tmux.h b/tmux.h index c4c5236..2c51baa 100644 --- a/tmux.h +++ b/tmux.h @@ -954,6 +954,8 @@ struct window_pane { TAILQ_ENTRY(window_pane) entry; RB_ENTRY(window_pane) tree_entry; + + struct wait_channel *signal_on_destroy; }; TAILQ_HEAD(window_panes, window_pane); RB_HEAD(window_pane_tree, window_pane); @@ -1884,6 +1886,11 @@ void cmdq_flush(struct cmd_q *); int cmd_string_parse(const char *, struct cmd_list **, const char *, u_int, char **); +/* cmd-wait-for.c */ +struct wait_channel *wait_channel_hold_by_name(const char *); +void wait_channel_signal(struct wait_channel *); +void wait_channel_unhold(struct wait_channel *); + /* client.c */ int client_main(int, char **, int); @@ -2148,7 +2155,7 @@ struct window *window_find_by_id(u_int); struct window *window_create1(u_int, u_int); struct window *window_create(const char *, int, char **, const char *, const char *, int, struct environ *, struct termios *, - u_int, u_int, u_int, char **); + u_int, u_int, u_int, struct wait_channel *, char **); void window_destroy(struct window *); struct window_pane *window_get_active_at(struct window *, u_int, u_int); void window_set_active_at(struct window *, u_int, u_int); @@ -2312,14 +2319,14 @@ struct session *session_find(const char *); struct session *session_find_by_id(u_int); struct session *session_create(const char *, int, char **, const char *, int, struct environ *, struct termios *, int, u_int, - u_int, char **); + u_int, struct wait_channel *, char **); void session_destroy(struct session *); int session_check_name(const char *); void session_update_activity(struct session *); struct session *session_next_session(struct session *); struct session *session_previous_session(struct session *); struct winlink *session_new(struct session *, const char *, int, char **, - const char *, int, int, char **); + const char *, int, int, struct wait_channel *, char **); struct winlink *session_attach(struct session *, struct window *, int, char **); int session_detach(struct session *, struct winlink *); diff --git a/window.c b/window.c index 5b93f93..eecacd9 100644 --- a/window.c +++ b/window.c @@ -308,13 +308,14 @@ window_create1(u_int sx, u_int sy) struct window * window_create(const char *name, int argc, char **argv, const char *path, const char *shell, int cwd, struct environ *env, struct termios *tio, - u_int sx, u_int sy, u_int hlimit, char **cause) + u_int sx, u_int sy, u_int hlimit, struct wait_channel *wc, char **cause) { struct window *w; struct window_pane *wp; w = window_create1(sx, sy); wp = window_add_pane(w, hlimit); + wp->signal_on_destroy = wc; layout_init(w, wp); if (window_pane_spawn(wp, argc, argv, path, shell, cwd, env, tio, @@ -712,6 +713,12 @@ window_pane_create(struct window *w, u_int sx, u_int sy, u_int hlimit) void window_pane_destroy(struct window_pane *wp) { + if(wp->signal_on_destroy) { + wait_channel_signal(wp->signal_on_destroy); + wait_channel_unhold(wp->signal_on_destroy); + wp->signal_on_destroy = NULL; + } + window_pane_reset_mode(wp); if (event_initialized(&wp->changes_timer))
pgphmPZRGTO4f.pgp
Description: PGP signature
------------------------------------------------------------------------------ Infragistics Professional Build stunning WinForms apps today! Reboot your WinForms applications with our WinForms controls. Build a bridge from your legacy apps to the future. http://pubads.g.doubleclick.net/gampad/clk?id=153845071&iu=/4140/ostg.clktrk
_______________________________________________ tmux-users mailing list tmux-users@lists.sourceforge.net https://lists.sourceforge.net/lists/listinfo/tmux-users