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