From 7fd4d49d562d2f57e21d9198bc28914891478f7c Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Wed, 6 Mar 2013 09:57:26 +0000 Subject: Add a wait-for command which blocks a client on a named channel until it is wokrn up again (with wait-for -S). From Thiago Padilha. --- cmd-wait-for.c | 124 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 124 insertions(+) create mode 100644 cmd-wait-for.c (limited to 'cmd-wait-for.c') diff --git a/cmd-wait-for.c b/cmd-wait-for.c new file mode 100644 index 00000000..6313358a --- /dev/null +++ b/cmd-wait-for.c @@ -0,0 +1,124 @@ +/* $Id$ */ + +/* + * Copyright (c) 2013 Nicholas Marriott + * Copyright (c) 2013 Thiago de Arruda + * + * 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 + +#include +#include + +#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); +} -- cgit From bc3580fa066ee38fd752b8414cb72af7bf3861b8 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Wed, 6 Mar 2013 11:00:55 +0000 Subject: Add wait-for -L and -U for lock and unlock, from Thiago Padilha. --- cmd-wait-for.c | 83 ++++++++++++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 78 insertions(+), 5 deletions(-) (limited to 'cmd-wait-for.c') diff --git a/cmd-wait-for.c b/cmd-wait-for.c index 6313358a..d40ba49e 100644 --- a/cmd-wait-for.c +++ b/cmd-wait-for.c @@ -32,8 +32,8 @@ 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", + "LSU", 1, 1, + "[-LSU] channel", 0, NULL, NULL, @@ -42,7 +42,10 @@ const struct cmd_entry cmd_wait_for_entry = { struct wait_channel { const char *name; + int locked; + TAILQ_HEAD(, cmd_q) waiters; + TAILQ_HEAD(, cmd_q) lockers; RB_ENTRY(wait_channel) entry; }; @@ -63,6 +66,10 @@ 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_lock(struct cmd_q *, const char *, + struct wait_channel *); +enum cmd_retval cmd_wait_for_unlock(struct cmd_q *, const char *, + struct wait_channel *); enum cmd_retval cmd_wait_for_exec(struct cmd *self, struct cmd_q *cmdq) @@ -76,6 +83,10 @@ cmd_wait_for_exec(struct cmd *self, struct cmd_q *cmdq) if (args_has(args, 'S')) return (cmd_wait_for_signal(cmdq, name, wc)); + if (args_has(args, 'L')) + return (cmd_wait_for_lock(cmdq, name, wc)); + if (args_has(args, 'U')) + return (cmd_wait_for_unlock(cmdq, name, wc)); return (cmd_wait_for_wait(cmdq, name, wc)); } @@ -95,9 +106,12 @@ cmd_wait_for_signal(struct cmd_q *cmdq, const char *name, if (!cmdq_free(wq)) cmdq_continue(wq); } - RB_REMOVE(wait_channels, &wait_channels, wc); - free((void*) wc->name); - free(wc); + + if (!wc->locked) { + RB_REMOVE(wait_channels, &wait_channels, wc); + free((void*) wc->name); + free(wc); + } return (CMD_RETURN_NORMAL); } @@ -114,11 +128,70 @@ cmd_wait_for_wait(struct cmd_q *cmdq, const char *name, 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); } + TAILQ_INSERT_TAIL(&wc->waiters, cmdq, waitentry); cmdq->references++; return (CMD_RETURN_WAIT); } + +enum cmd_retval +cmd_wait_for_lock(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 lock"); + 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->locked) { + TAILQ_INSERT_TAIL(&wc->lockers, cmdq, waitentry); + cmdq->references++; + return (CMD_RETURN_WAIT); + } + wc->locked = 1; + + return (CMD_RETURN_NORMAL); +} + +enum cmd_retval +cmd_wait_for_unlock(struct cmd_q *cmdq, const char *name, + struct wait_channel *wc) +{ + struct cmd_q *wq; + + if (wc == NULL || !wc->locked) { + cmdq_error(cmdq, "channel %s not locked", name); + return (CMD_RETURN_ERROR); + } + + if ((wq = TAILQ_FIRST(&wc->lockers)) != NULL) { + TAILQ_REMOVE(&wc->lockers, wq, waitentry); + if (!cmdq_free(wq)) + 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); + } + } + + return (CMD_RETURN_NORMAL); +} + -- cgit