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

Reply via email to