Add time and a command count to control mode guards, based on code from
[tmux-openbsd.git] / cmd-wait-for.c
blob3a8d8ea451387d29c6f2b33b4993f874a29a7479
1 /* $OpenBSD$ */
3 /*
4 * Copyright (c) 2013 Nicholas Marriott <nicm@users.sourceforge.net>
5 * Copyright (c) 2013 Thiago de Arruda <tpadilha84@gmail.com>
7 * Permission to use, copy, modify, and distribute this software for any
8 * purpose with or without fee is hereby granted, provided that the above
9 * copyright notice and this permission notice appear in all copies.
11 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
12 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
13 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
14 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
15 * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER
16 * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
17 * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
20 #include <sys/types.h>
22 #include <stdlib.h>
23 #include <string.h>
25 #include "tmux.h"
28 * Block or wake a client on a named wait channel.
31 enum cmd_retval cmd_wait_for_exec(struct cmd *, struct cmd_q *);
33 const struct cmd_entry cmd_wait_for_entry = {
34 "wait-for", "wait",
35 "LSU", 1, 1,
36 "[-LSU] channel",
38 NULL,
39 NULL,
40 cmd_wait_for_exec
43 struct wait_channel {
44 const char *name;
45 int locked;
47 TAILQ_HEAD(, cmd_q) waiters;
48 TAILQ_HEAD(, cmd_q) lockers;
50 RB_ENTRY(wait_channel) entry;
52 RB_HEAD(wait_channels, wait_channel);
53 struct wait_channels wait_channels = RB_INITIALIZER(wait_channels);
55 int wait_channel_cmp(struct wait_channel *, struct wait_channel *);
56 RB_PROTOTYPE(wait_channels, wait_channel, entry, wait_channel_cmp);
57 RB_GENERATE(wait_channels, wait_channel, entry, wait_channel_cmp);
59 int
60 wait_channel_cmp(struct wait_channel *wc1, struct wait_channel *wc2)
62 return (strcmp(wc1->name, wc2->name));
65 enum cmd_retval cmd_wait_for_signal(struct cmd_q *, const char *,
66 struct wait_channel *);
67 enum cmd_retval cmd_wait_for_wait(struct cmd_q *, const char *,
68 struct wait_channel *);
69 enum cmd_retval cmd_wait_for_lock(struct cmd_q *, const char *,
70 struct wait_channel *);
71 enum cmd_retval cmd_wait_for_unlock(struct cmd_q *, const char *,
72 struct wait_channel *);
74 enum cmd_retval
75 cmd_wait_for_exec(struct cmd *self, struct cmd_q *cmdq)
77 struct args *args = self->args;
78 const char *name = args->argv[0];
79 struct wait_channel *wc, wc0;
81 wc0.name = name;
82 wc = RB_FIND(wait_channels, &wait_channels, &wc0);
84 if (args_has(args, 'S'))
85 return (cmd_wait_for_signal(cmdq, name, wc));
86 if (args_has(args, 'L'))
87 return (cmd_wait_for_lock(cmdq, name, wc));
88 if (args_has(args, 'U'))
89 return (cmd_wait_for_unlock(cmdq, name, wc));
90 return (cmd_wait_for_wait(cmdq, name, wc));
93 enum cmd_retval
94 cmd_wait_for_signal(struct cmd_q *cmdq, const char *name,
95 struct wait_channel *wc)
97 struct cmd_q *wq, *wq1;
99 if (wc == NULL || TAILQ_EMPTY(&wc->waiters)) {
100 cmdq_error(cmdq, "no waiting clients on %s", name);
101 return (CMD_RETURN_ERROR);
104 TAILQ_FOREACH_SAFE(wq, &wc->waiters, waitentry, wq1) {
105 TAILQ_REMOVE(&wc->waiters, wq, waitentry);
106 if (!cmdq_free(wq))
107 cmdq_continue(wq);
110 if (!wc->locked) {
111 RB_REMOVE(wait_channels, &wait_channels, wc);
112 free((void*) wc->name);
113 free(wc);
116 return (CMD_RETURN_NORMAL);
119 enum cmd_retval
120 cmd_wait_for_wait(struct cmd_q *cmdq, const char *name,
121 struct wait_channel *wc)
123 if (cmdq->client == NULL || cmdq->client->session != NULL) {
124 cmdq_error(cmdq, "not able to wait");
125 return (CMD_RETURN_ERROR);
128 if (wc == NULL) {
129 wc = xmalloc(sizeof *wc);
130 wc->name = xstrdup(name);
131 wc->locked = 0;
132 TAILQ_INIT(&wc->waiters);
133 TAILQ_INIT(&wc->lockers);
134 RB_INSERT(wait_channels, &wait_channels, wc);
137 TAILQ_INSERT_TAIL(&wc->waiters, cmdq, waitentry);
138 cmdq->references++;
140 return (CMD_RETURN_WAIT);
143 enum cmd_retval
144 cmd_wait_for_lock(struct cmd_q *cmdq, const char *name,
145 struct wait_channel *wc)
147 if (cmdq->client == NULL || cmdq->client->session != NULL) {
148 cmdq_error(cmdq, "not able to lock");
149 return (CMD_RETURN_ERROR);
152 if (wc == NULL) {
153 wc = xmalloc(sizeof *wc);
154 wc->name = xstrdup(name);
155 wc->locked = 0;
156 TAILQ_INIT(&wc->waiters);
157 TAILQ_INIT(&wc->lockers);
158 RB_INSERT(wait_channels, &wait_channels, wc);
161 if (wc->locked) {
162 TAILQ_INSERT_TAIL(&wc->lockers, cmdq, waitentry);
163 cmdq->references++;
164 return (CMD_RETURN_WAIT);
166 wc->locked = 1;
168 return (CMD_RETURN_NORMAL);
171 enum cmd_retval
172 cmd_wait_for_unlock(struct cmd_q *cmdq, const char *name,
173 struct wait_channel *wc)
175 struct cmd_q *wq;
177 if (wc == NULL || !wc->locked) {
178 cmdq_error(cmdq, "channel %s not locked", name);
179 return (CMD_RETURN_ERROR);
182 if ((wq = TAILQ_FIRST(&wc->lockers)) != NULL) {
183 TAILQ_REMOVE(&wc->lockers, wq, waitentry);
184 if (!cmdq_free(wq))
185 cmdq_continue(wq);
186 } else {
187 wc->locked = 0;
188 if (TAILQ_EMPTY(&wc->waiters)) {
189 RB_REMOVE(wait_channels, &wait_channels, wc);
190 free((void*) wc->name);
191 free(wc);
195 return (CMD_RETURN_NORMAL);