Do you want me to add the example script? On Wed, Mar 6, 2013 at 7:09 AM, Thiago Padilha <tpadilh...@gmail.com> wrote: > Yes will do in a minute > > On Wed, Mar 6, 2013 at 6:58 AM, Nicholas Marriott > <nicholas.marri...@gmail.com> wrote: >> Committed, thanks. Do you want to tweak the lock bits to fit too? >> >> >> On Wed, Mar 06, 2013 at 06:53:12AM -0300, Thiago Padilha wrote: >>> This looks good to me >>> >>> On Wed, Mar 6, 2013 at 6:37 AM, Nicholas Marriott >>> <nicholas.marri...@gmail.com> wrote: >>> > Ok this looks pretty good. I've made a few changes but mainly >>> > style/layout nits: >>> > >>> > - I don't think we need find and find_and_create functions, the find >>> > will only be done once and the create twice (when there is lock too). >>> > >>> > - A single cmdq can only be waiting once so no need to have a wrapper >>> > struct, just put the TAILQ_ENTRY in struct cmd_q itself. >>> > >>> > - channel_node -> wait_channel and similar renaming. >>> > >>> > - We always include sys/types.h, needed or not. Also errors always start >>> > with lowercase. >>> > >>> > - Move things about to sort of vaguely fit where they go in other >>> > commands. >>> > >>> > - Add to man page. >>> > >>> > I considered putting structs in tmux.h but I can't see how they would be >>> > needed outside this file so let's leave them local anyway. tmux.h is too >>> > big already. >>> > >>> > Please take a look and make sure there isn't anything stupid and then I >>> > will commit and we can modify to add locking. >>> > >>> > >>> > diff --git a/Makefile.am b/Makefile.am >>> > index 5caa498..19220d8 100644 >>> > --- a/Makefile.am >>> > +++ b/Makefile.am >>> > @@ -135,6 +135,7 @@ dist_tmux_SOURCES = \ >>> > cmd-switch-client.c \ >>> > cmd-unbind-key.c \ >>> > cmd-unlink-window.c \ >>> > + cmd-wait-for.c \ >>> > cmd.c \ >>> > colour.c \ >>> > control.c \ >>> > diff --git a/cmd-wait-for.c b/cmd-wait-for.c >>> > new file mode 100644 >>> > index 0000000..6313358 >>> > --- /dev/null >>> > +++ b/cmd-wait-for.c >>> > @@ -0,0 +1,124 @@ >>> > +/* $Id$ */ >>> > + >>> > +/* >>> > + * Copyright (c) 2013 Nicholas Marriott <n...@users.sourceforge.net> >>> > + * Copyright (c) 2013 Thiago de Arruda <tpadilh...@gmail.com> >>> > + * >>> > + * Permission to use, copy, modify, and distribute this software for any >>> > + * purpose with or without fee is hereby granted, provided that the above >>> > + * copyright notice and this permission notice appear in all copies. >>> > + * >>> > + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL >>> > WARRANTIES >>> > + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF >>> > + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE >>> > FOR >>> > + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES >>> > + * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER >>> > + * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING >>> > + * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. >>> > + */ >>> > + >>> > +#include <sys/types.h> >>> > + >>> > +#include <stdlib.h> >>> > +#include <string.h> >>> > + >>> > +#include "tmux.h" >>> > + >>> > +/* >>> > + * Block or wake a client on a named wait channel. >>> > + */ >>> > + >>> > +enum cmd_retval cmd_wait_for_exec(struct cmd *, struct cmd_q *); >>> > + >>> > +const struct cmd_entry cmd_wait_for_entry = { >>> > + "wait-for", "wait", >>> > + "S", 1, 1, >>> > + "[-S] channel", >>> > + 0, >>> > + NULL, >>> > + NULL, >>> > + cmd_wait_for_exec >>> > +}; >>> > + >>> > +struct wait_channel { >>> > + const char *name; >>> > + TAILQ_HEAD(, cmd_q) waiters; >>> > + >>> > + RB_ENTRY(wait_channel) entry; >>> > +}; >>> > +RB_HEAD(wait_channels, wait_channel); >>> > +struct wait_channels wait_channels = RB_INITIALIZER(wait_channels); >>> > + >>> > +int wait_channel_cmp(struct wait_channel *, struct wait_channel *); >>> > +RB_PROTOTYPE(wait_channels, wait_channel, entry, wait_channel_cmp); >>> > +RB_GENERATE(wait_channels, wait_channel, entry, wait_channel_cmp); >>> > + >>> > +int >>> > +wait_channel_cmp(struct wait_channel *wc1, struct wait_channel *wc2) >>> > +{ >>> > + return (strcmp(wc1->name, wc2->name)); >>> > +} >>> > + >>> > +enum cmd_retval cmd_wait_for_signal(struct cmd_q *, const char *, >>> > + struct wait_channel *); >>> > +enum cmd_retval cmd_wait_for_wait(struct cmd_q *, const char *, >>> > + struct wait_channel *); >>> > + >>> > +enum cmd_retval >>> > +cmd_wait_for_exec(struct cmd *self, struct cmd_q *cmdq) >>> > +{ >>> > + struct args *args = self->args; >>> > + const char *name = args->argv[0]; >>> > + struct wait_channel *wc, wc0; >>> > + >>> > + wc0.name = name; >>> > + wc = RB_FIND(wait_channels, &wait_channels, &wc0); >>> > + >>> > + if (args_has(args, 'S')) >>> > + return (cmd_wait_for_signal(cmdq, name, wc)); >>> > + return (cmd_wait_for_wait(cmdq, name, wc)); >>> > +} >>> > + >>> > +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); >>> > + } >>> > + RB_REMOVE(wait_channels, &wait_channels, wc); >>> > + free((void*) wc->name); >>> > + free(wc); >>> > + >>> > + return (CMD_RETURN_NORMAL); >>> > +} >>> > + >>> > +enum cmd_retval >>> > +cmd_wait_for_wait(struct cmd_q *cmdq, const char *name, >>> > + struct wait_channel *wc) >>> > +{ >>> > + if (cmdq->client == NULL || cmdq->client->session != NULL) { >>> > + cmdq_error(cmdq, "not able to wait"); >>> > + return (CMD_RETURN_ERROR); >>> > + } >>> > + >>> > + if (wc == NULL) { >>> > + wc = xmalloc(sizeof *wc); >>> > + wc->name = xstrdup(name); >>> > + TAILQ_INIT(&wc->waiters); >>> > + RB_INSERT(wait_channels, &wait_channels, wc); >>> > + } >>> > + TAILQ_INSERT_TAIL(&wc->waiters, cmdq, waitentry); >>> > + cmdq->references++; >>> > + >>> > + return (CMD_RETURN_WAIT); >>> > +} >>> > diff --git a/cmd.c b/cmd.c >>> > index 0d6a85f..20484ed 100644 >>> > --- a/cmd.c >>> > +++ b/cmd.c >>> > @@ -112,6 +112,7 @@ const struct cmd_entry *cmd_table[] = { >>> > &cmd_switch_client_entry, >>> > &cmd_unbind_key_entry, >>> > &cmd_unlink_window_entry, >>> > + &cmd_wait_for_entry, >>> > NULL >>> > }; >>> > >>> > diff --git a/tmux.1 b/tmux.1 >>> > index 1a9c058..8df7975 100644 >>> > --- a/tmux.1 >>> > +++ b/tmux.1 >>> > @@ -3553,6 +3553,19 @@ If the command doesn't return success, the exit >>> > status is also displayed. >>> > .It Ic server-info >>> > .D1 (alias: Ic info ) >>> > Show server information and terminal details. >>> > +.It Xo Ic wait-for >>> > +.Fl S >>> > +.Ar channel >>> > +.Xc >>> > +.D1 (alias: Ic wait ) >>> > +When used without >>> > +.Fl S , >>> > +prevents the client from exiting until woken using >>> > +.Ic wait-for >>> > +.Fl S >>> > +with the same channel. >>> > +This command only works from outside >>> > +.Nm . >>> > .El >>> > .Sh TERMINFO EXTENSIONS >>> > .Nm >>> > diff --git a/tmux.h b/tmux.h >>> > index c1ad662..e58c1de 100644 >>> > --- a/tmux.h >>> > +++ b/tmux.h >>> > @@ -1416,6 +1416,8 @@ struct cmd_q { >>> > void *data; >>> > >>> > struct msg_command_data *msgdata; >>> > + >>> > + TAILQ_ENTRY(cmd_q) waitentry; >>> > }; >>> > >>> > /* Command definition. */ >>> > @@ -1835,6 +1837,7 @@ extern const struct cmd_entry >>> > cmd_switch_client_entry; >>> > extern const struct cmd_entry cmd_unbind_key_entry; >>> > extern const struct cmd_entry cmd_unlink_window_entry; >>> > extern const struct cmd_entry cmd_up_pane_entry; >>> > +extern const struct cmd_entry cmd_wait_for_entry; >>> > >>> > /* cmd-attach-session.c */ >>> > enum cmd_retval cmd_attach_session(struct cmd_q *, const char*, >>> > int, int); >>> > >>> > >>> > >>> > >>> > On Tue, Mar 05, 2013 at 10:55:05PM -0300, Thiago Padilha wrote: >>> >> Here it goes: >>> >> >>> >> The first patch implements wait/signal, the second extends it with >>> >> lock/unlock. My goal this time was to make the code small and simple >>> >> as possible, let me know if you think anything needs to be refactored. >>> >> --- >>> >> Makefile.am | 1 + >>> >> cmd-wait-for.c | 145 >>> >> ++++++++++++++++++++++++++++++++++++++++++++++ >>> >> cmd.c | 1 + >>> >> examples/tmux-wait-for.sh | 109 ++++++++++++++++++++++++++++++++++ >>> >> tmux.h | 1 + >>> >> 5 files changed, 257 insertions(+) >>> >> create mode 100644 cmd-wait-for.c >>> >> create mode 100755 examples/tmux-wait-for.sh >>> > >>> >> diff --git a/Makefile.am b/Makefile.am >>> >> index 5caa498..19220d8 100644 >>> >> --- a/Makefile.am >>> >> +++ b/Makefile.am >>> >> @@ -135,6 +135,7 @@ dist_tmux_SOURCES = \ >>> >> cmd-switch-client.c \ >>> >> cmd-unbind-key.c \ >>> >> cmd-unlink-window.c \ >>> >> + cmd-wait-for.c \ >>> >> cmd.c \ >>> >> colour.c \ >>> >> control.c \ >>> >> diff --git a/cmd-wait-for.c b/cmd-wait-for.c >>> >> new file mode 100644 >>> >> index 0000000..658109b >>> >> --- /dev/null >>> >> +++ b/cmd-wait-for.c >>> >> @@ -0,0 +1,145 @@ >>> >> +/* $Id$ */ >>> >> + >>> >> +/* >>> >> + * Copyright (c) 2013 Thiago de Arruda<tpadilh...@gmail.com> >>> >> + * >>> >> + * >>> >> + * Permission to use, copy, modify, and distribute this software for any >>> >> + * purpose with or without fee is hereby granted, provided that the >>> >> above >>> >> + * copyright notice and this permission notice appear in all copies. >>> >> + * >>> >> + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL >>> >> WARRANTIES >>> >> + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF >>> >> + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE >>> >> FOR >>> >> + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY >>> >> DAMAGES >>> >> + * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER >>> >> + * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, >>> >> ARISING >>> >> + * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. >>> >> + */ >>> >> + >>> >> +#include <stdlib.h> >>> >> +#include <string.h> >>> >> + >>> >> +#include "tmux.h" >>> >> + >>> >> +struct cmdq_node { >>> >> + struct cmd_q *cmdq; >>> >> + TAILQ_ENTRY(cmdq_node) node; >>> >> +}; >>> >> + >>> >> +struct channel_node { >>> >> + char *name; >>> >> + TAILQ_HEAD(, cmdq_node) waiting_cmdqs; >>> >> + RB_ENTRY(channel_node) node; >>> >> +}; >>> >> +RB_HEAD(channels, channel_node) channels_head = >>> >> RB_INITIALIZER(&channels_head); >>> >> + >>> >> +enum cmd_retval cmd_wait_for_exec(struct cmd *, struct cmd_q *); >>> >> +enum cmd_retval channel_wait(const char *, struct cmd_q *); >>> >> +enum cmd_retval channel_signal(const char *, struct cmd_q *); >>> >> +struct channel_node *channels_find(const char *); >>> >> +struct channel_node *channels_find_or_create(const char *); >>> >> +int channel_cmp(struct channel_node *, struct channel_node *); >>> >> +RB_PROTOTYPE(channels, channel_node, node, channel_cmp); >>> >> + >>> >> +const struct cmd_entry cmd_wait_for_entry = { >>> >> + "wait-for", "wait", >>> >> + "S", 1, 1, >>> >> + "[-S] channel", >>> >> + 0, >>> >> + NULL, >>> >> + NULL, >>> >> + cmd_wait_for_exec >>> >> +}; >>> >> + >>> >> +enum cmd_retval >>> >> +cmd_wait_for_exec(struct cmd *self, struct cmd_q *cmdq) >>> >> +{ >>> >> + struct args *args = self->args; >>> >> + const char *name = args->argv[0]; >>> >> + >>> >> + if (args_has(args, 'S')) >>> >> + return channel_signal(name, cmdq); >>> >> + >>> >> + return channel_wait(name, cmdq); >>> >> +} >>> >> + >>> >> +RB_GENERATE(channels, channel_node, node, channel_cmp); >>> >> + >>> >> +int >>> >> +channel_cmp(struct channel_node *c1, struct channel_node *c2) >>> >> +{ >>> >> + return (strcmp(c1->name, c2->name)); >>> >> +} >>> >> + >>> >> +struct channel_node * >>> >> +channels_find(const char *name) >>> >> +{ >>> >> + struct channel_node c_name; >>> >> + c_name.name = (char *)name; >>> >> + return RB_FIND(channels, &channels_head, &c_name); >>> >> +} >>> >> + >>> >> +struct channel_node * >>> >> +channels_find_or_create(const char *name) >>> >> +{ >>> >> + struct channel_node *c; >>> >> + >>> >> + c = channels_find(name); >>> >> + >>> >> + if (c == NULL) { >>> >> + c = xmalloc(sizeof *c); >>> >> + c->name = xstrdup(name); >>> >> + TAILQ_INIT(&c->waiting_cmdqs); >>> >> + RB_INSERT(channels, &channels_head, c); >>> >> + } >>> >> + >>> >> + return c; >>> >> +} >>> >> + >>> >> +enum cmd_retval >>> >> +channel_wait(const char *name, struct cmd_q *cmdq) >>> >> +{ >>> >> + struct cmdq_node *wq; >>> >> + struct channel_node *c; >>> >> + >>> >> + if (cmdq->client == NULL || cmdq->client->session != NULL) { >>> >> + cmdq_error(cmdq, "Not able to wait"); >>> >> + return (CMD_RETURN_ERROR); >>> >> + } >>> >> + >>> >> + c = channels_find_or_create(name); >>> >> + wq = xmalloc(sizeof *wq); >>> >> + wq->cmdq = cmdq; >>> >> + TAILQ_INSERT_HEAD(&c->waiting_cmdqs, wq, node); >>> >> + cmdq->references++; >>> >> + >>> >> + return (CMD_RETURN_WAIT); >>> >> +} >>> >> + >>> >> +enum cmd_retval >>> >> +channel_signal(const char *name, struct cmd_q *cmdq) >>> >> +{ >>> >> + struct cmdq_node *wq; >>> >> + struct channel_node *c; >>> >> + >>> >> + c = channels_find(name); >>> >> + >>> >> + if (c == NULL || TAILQ_EMPTY(&c->waiting_cmdqs)) { >>> >> + cmdq_error(cmdq, "No waiting clients"); >>> >> + return (CMD_RETURN_ERROR); >>> >> + } >>> >> + >>> >> + while ((wq = TAILQ_FIRST(&c->waiting_cmdqs)) != NULL) { >>> >> + TAILQ_REMOVE(&c->waiting_cmdqs, wq, node); >>> >> + if (!cmdq_free(wq->cmdq)) >>> >> + cmdq_continue(wq->cmdq); >>> >> + free(wq); >>> >> + } >>> >> + >>> >> + RB_REMOVE(channels, &channels_head, c); >>> >> + free(c->name); >>> >> + free(c); >>> >> + >>> >> + return (CMD_RETURN_NORMAL); >>> >> +} >>> >> diff --git a/cmd.c b/cmd.c >>> >> index 0d6a85f..20484ed 100644 >>> >> --- a/cmd.c >>> >> +++ b/cmd.c >>> >> @@ -112,6 +112,7 @@ const struct cmd_entry *cmd_table[] = { >>> >> &cmd_switch_client_entry, >>> >> &cmd_unbind_key_entry, >>> >> &cmd_unlink_window_entry, >>> >> + &cmd_wait_for_entry, >>> >> NULL >>> >> }; >>> >> >>> >> diff --git a/examples/tmux-wait-for.sh b/examples/tmux-wait-for.sh >>> >> new file mode 100755 >>> >> index 0000000..94baa90 >>> >> --- /dev/null >>> >> +++ b/examples/tmux-wait-for.sh >>> >> @@ -0,0 +1,109 @@ >>> >> +#!/bin/bash >>> >> + >>> >> +# Shows how one can synchronize work using the 'wait' command >>> >> + >>> >> +if [ -z $TMUX ]; then >>> >> + echo "Start tmux first" >&2 >>> >> + exit 1 >>> >> +fi >>> >> + >>> >> +kill_child_panes() { >>> >> + tmux kill-pane -t $pane1 >>> >> + tmux kill-pane -t $pane2 >>> >> + tmux kill-pane -t $pane3 >>> >> + exit >>> >> +} >>> >> +abspath=$(cd ${0%/*} && echo $PWD/${0##*/}) >>> >> + >>> >> +case $1 in >>> >> + pane1) >>> >> + tmux setw -q @pane1id $TMUX_PANE >>> >> + tmux wait -S pane1 >>> >> + tmux wait pane1 >>> >> + tmux split-window -d -h "$abspath pane3" >>> >> + tmux split-window -d -h "$abspath pane2" >>> >> + tmux wait pane1 >>> >> + columns=$(tput cols) >>> >> + n=1 >>> >> + while true; do >>> >> + for i in $(seq 1 $columns); do >>> >> + sleep "0.1" >>> >> + echo -n $n >>> >> + done >>> >> + echo >>> >> + tmux wait -S pane2 >>> >> + tmux wait pane1 >>> >> + n=$(( ($n + 3) % 9 )) >>> >> + done >>> >> + ;; >>> >> + pane2) >>> >> + tmux setw -q @pane2id $TMUX_PANE >>> >> + tmux wait -S pane2 >>> >> + tmux wait pane2 >>> >> + columns=$(tput cols) >>> >> + n=2 >>> >> + while true; do >>> >> + for i in $(seq 1 $columns); do >>> >> + sleep "0.1" >>> >> + echo -n $n >>> >> + done >>> >> + echo >>> >> + tmux wait -S pane3 >>> >> + tmux wait pane2 >>> >> + n=$(( ($n + 3) % 9 )) >>> >> + done >>> >> + ;; >>> >> + pane3) >>> >> + tmux setw -q @pane3id $TMUX_PANE >>> >> + tmux wait -S pane3 >>> >> + tmux wait pane3 >>> >> + columns=$(tput cols) >>> >> + n=3 >>> >> + while true; do >>> >> + for i in $(seq 1 $columns); do >>> >> + sleep "0.1" >>> >> + echo -n $n >>> >> + done >>> >> + echo >>> >> + tmux wait -S pane1 >>> >> + tmux wait pane3 >>> >> + n=$(( ($n + 3) % 9 )) >>> >> + done >>> >> + ;; >>> >> + *) >>> >> + columns=$(tput cols) >>> >> + trap kill_child_panes SIGINT SIGTERM >>> >> + clear >>> >> + echo "This is a simple script that shows how tmux can >>> >> synchronize" >>> >> + echo "code running in different panes." >>> >> + echo >>> >> + echo "Besides recursively spliting panes, it will run a >>> >> simple animation" >>> >> + echo "demonstrating the coordination between panes" >>> >> + echo >>> >> + sleep 1 >>> >> + echo "First split horizontally" >>> >> + tmux split-window "$abspath pane1" >>> >> + tmux wait pane1 >>> >> + pane1=`tmux showw -v @pane1id` >>> >> + sleep 1 >>> >> + echo "Now we split the child pane 2 times" >>> >> + tmux wait -S pane1 >>> >> + tmux wait pane3 >>> >> + tmux wait pane2 >>> >> + pane2=`tmux showw -v @pane2id` >>> >> + pane3=`tmux showw -v @pane3id` >>> >> + column_width=$(($columns / 3)) >>> >> + sleep 1 >>> >> + echo "Resize equally" >>> >> + tmux resize-pane -t $pane1 -x $column_width >>> >> + tmux resize-pane -t $pane2 -x $column_width >>> >> + tmux resize-pane -t $pane3 -x $column_width >>> >> + sleep 1 >>> >> + echo "Start animation" >>> >> + tmux wait -S pane1 >>> >> + tmux select-pane -t $TMUX_PANE >>> >> + while true; do >>> >> + sleep 1000 >>> >> + done >>> >> + ;; >>> >> +esac >>> >> diff --git a/tmux.h b/tmux.h >>> >> index c1ad662..95dd97c 100644 >>> >> --- a/tmux.h >>> >> +++ b/tmux.h >>> >> @@ -1835,6 +1835,7 @@ extern const struct cmd_entry >>> >> cmd_switch_client_entry; >>> >> extern const struct cmd_entry cmd_unbind_key_entry; >>> >> extern const struct cmd_entry cmd_unlink_window_entry; >>> >> extern const struct cmd_entry cmd_up_pane_entry; >>> >> +extern const struct cmd_entry cmd_wait_for_entry; >>> >> >>> >> /* cmd-attach-session.c */ >>> >> enum cmd_retval cmd_attach_session(struct cmd_q *, const char*, >>> >> int, int); >>> > >>> >> diff --git a/cmd-wait-for.c b/cmd-wait-for.c >>> >> index 658109b..58e53c6 100644 >>> >> --- a/cmd-wait-for.c >>> >> +++ b/cmd-wait-for.c >>> >> @@ -29,7 +29,9 @@ struct cmdq_node { >>> >> >>> >> struct channel_node { >>> >> char *name; >>> >> + int locked; >>> >> TAILQ_HEAD(, cmdq_node) waiting_cmdqs; >>> >> + TAILQ_HEAD(, cmdq_node) locking_cmdqs; >>> >> RB_ENTRY(channel_node) node; >>> >> }; >>> >> RB_HEAD(channels, channel_node) channels_head = >>> >> RB_INITIALIZER(&channels_head); >>> >> @@ -37,6 +39,8 @@ RB_HEAD(channels, channel_node) channels_head = >>> >> RB_INITIALIZER(&channels_head); >>> >> enum cmd_retval cmd_wait_for_exec(struct cmd *, struct cmd_q *); >>> >> enum cmd_retval channel_wait(const char *, struct cmd_q *); >>> >> enum cmd_retval channel_signal(const char *, struct cmd_q *); >>> >> +enum cmd_retval channel_lock(const char *, struct cmd_q *); >>> >> +enum cmd_retval channel_unlock(const char *, struct cmd_q *); >>> >> struct channel_node *channels_find(const char *); >>> >> struct channel_node *channels_find_or_create(const char *); >>> >> int channel_cmp(struct channel_node *, struct channel_node *); >>> >> @@ -44,8 +48,8 @@ RB_PROTOTYPE(channels, channel_node, node, >>> >> channel_cmp); >>> >> >>> >> const struct cmd_entry cmd_wait_for_entry = { >>> >> "wait-for", "wait", >>> >> - "S", 1, 1, >>> >> - "[-S] channel", >>> >> + "LSU", 1, 1, >>> >> + "[-L | -S | -U] channel", >>> >> 0, >>> >> NULL, >>> >> NULL, >>> >> @@ -61,6 +65,12 @@ cmd_wait_for_exec(struct cmd *self, struct cmd_q >>> >> *cmdq) >>> >> if (args_has(args, 'S')) >>> >> return channel_signal(name, cmdq); >>> >> >>> >> + if (args_has(args, 'L')) >>> >> + return channel_lock(name, cmdq); >>> >> + >>> >> + if (args_has(args, 'U')) >>> >> + return channel_unlock(name, cmdq); >>> >> + >>> >> return channel_wait(name, cmdq); >>> >> } >>> >> >>> >> @@ -90,7 +100,9 @@ channels_find_or_create(const char *name) >>> >> if (c == NULL) { >>> >> c = xmalloc(sizeof *c); >>> >> c->name = xstrdup(name); >>> >> + c->locked = 0; >>> >> TAILQ_INIT(&c->waiting_cmdqs); >>> >> + TAILQ_INIT(&c->locking_cmdqs); >>> >> RB_INSERT(channels, &channels_head, c); >>> >> } >>> >> >>> >> @@ -137,9 +149,67 @@ channel_signal(const char *name, struct cmd_q *cmdq) >>> >> free(wq); >>> >> } >>> >> >>> >> - RB_REMOVE(channels, &channels_head, c); >>> >> - free(c->name); >>> >> - free(c); >>> >> + if (!c->locked) { >>> >> + RB_REMOVE(channels, &channels_head, c); >>> >> + free(c->name); >>> >> + free(c); >>> >> + } >>> >> >>> >> return (CMD_RETURN_NORMAL); >>> >> } >>> >> + >>> >> +enum cmd_retval >>> >> +channel_lock(const char *name, struct cmd_q *cmdq) >>> >> +{ >>> >> + struct cmdq_node *lq; >>> >> + struct channel_node *c; >>> >> + >>> >> + if (cmdq->client == NULL || cmdq->client->session != NULL) { >>> >> + cmdq_error(cmdq, "Not able to lock"); >>> >> + return (CMD_RETURN_ERROR); >>> >> + } >>> >> + >>> >> + c = channels_find_or_create(name); >>> >> + >>> >> + if (c->locked) { >>> >> + lq = xmalloc(sizeof *lq); >>> >> + lq->cmdq = cmdq; >>> >> + TAILQ_INSERT_TAIL(&c->locking_cmdqs, lq, node); >>> >> + cmdq->references++; >>> >> + return (CMD_RETURN_WAIT); >>> >> + } >>> >> + >>> >> + c->locked = 1; >>> >> + return (CMD_RETURN_NORMAL); >>> >> +} >>> >> + >>> >> +enum cmd_retval >>> >> +channel_unlock(const char *name, struct cmd_q *cmdq) >>> >> +{ >>> >> + struct cmdq_node *lq; >>> >> + struct channel_node *c; >>> >> + >>> >> + c = channels_find(name); >>> >> + >>> >> + if (c == NULL || !c->locked) { >>> >> + cmdq_error(cmdq, "Channel not locked"); >>> >> + return (CMD_RETURN_ERROR); >>> >> + } >>> >> + >>> >> + if ((lq = TAILQ_FIRST(&c->locking_cmdqs)) != NULL) { >>> >> + TAILQ_REMOVE(&c->locking_cmdqs, lq, node); >>> >> + if (!cmdq_free(lq->cmdq)) >>> >> + cmdq_continue(lq->cmdq); >>> >> + free(lq); >>> >> + } else { >>> >> + c->locked = 0; >>> >> + if (TAILQ_EMPTY(&c->waiting_cmdqs)) { >>> >> + RB_REMOVE(channels, &channels_head, c); >>> >> + free(c->name); >>> >> + free(c); >>> >> + } >>> >> + } >>> >> + >>> >> + return (CMD_RETURN_NORMAL); >>> >> +} >>> >> + >>> > >>> >> ------------------------------------------------------------------------------ >>> >> Symantec Endpoint Protection 12 positioned as A LEADER in The Forrester >>> >> Wave(TM): Endpoint Security, Q1 2013 and "remains a good choice" in the >>> >> endpoint security space. For insight on selecting the right partner to >>> >> tackle endpoint security challenges, access the full report. >>> >> http://p.sf.net/sfu/symantec-dev2dev >>> > >>> >> _______________________________________________ >>> >> tmux-users mailing list >>> >> tmux-users@lists.sourceforge.net >>> >> https://lists.sourceforge.net/lists/listinfo/tmux-users >>> >
------------------------------------------------------------------------------ Symantec Endpoint Protection 12 positioned as A LEADER in The Forrester Wave(TM): Endpoint Security, Q1 2013 and "remains a good choice" in the endpoint security space. For insight on selecting the right partner to tackle endpoint security challenges, access the full report. http://p.sf.net/sfu/symantec-dev2dev _______________________________________________ tmux-users mailing list tmux-users@lists.sourceforge.net https://lists.sourceforge.net/lists/listinfo/tmux-users