199e242abSchristos /* $OpenBSD$ */
2928fc495Schristos
3928fc495Schristos /*
4f26e8bc9Schristos * Copyright (c) 2013 Nicholas Marriott <nicholas.marriott@gmail.com>
5928fc495Schristos * Copyright (c) 2013 Thiago de Arruda <tpadilha84@gmail.com>
6928fc495Schristos *
7928fc495Schristos * Permission to use, copy, modify, and distribute this software for any
8928fc495Schristos * purpose with or without fee is hereby granted, provided that the above
9928fc495Schristos * copyright notice and this permission notice appear in all copies.
10928fc495Schristos *
11928fc495Schristos * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
12928fc495Schristos * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
13928fc495Schristos * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
14928fc495Schristos * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
15928fc495Schristos * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER
16928fc495Schristos * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
17928fc495Schristos * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
18928fc495Schristos */
19928fc495Schristos
20928fc495Schristos #include <sys/types.h>
21928fc495Schristos
22928fc495Schristos #include <stdlib.h>
23928fc495Schristos #include <string.h>
24928fc495Schristos
25928fc495Schristos #include "tmux.h"
26928fc495Schristos
27928fc495Schristos /*
28928fc495Schristos * Block or wake a client on a named wait channel.
29928fc495Schristos */
30928fc495Schristos
31e9a2d6faSchristos static enum cmd_retval cmd_wait_for_exec(struct cmd *, struct cmdq_item *);
32928fc495Schristos
33928fc495Schristos const struct cmd_entry cmd_wait_for_entry = {
34f26e8bc9Schristos .name = "wait-for",
35f26e8bc9Schristos .alias = "wait",
36f26e8bc9Schristos
37*46548964Swiz .args = { "LSU", 1, 1, NULL },
38f26e8bc9Schristos .usage = "[-L|-S|-U] channel",
39f26e8bc9Schristos
40f26e8bc9Schristos .flags = 0,
41f26e8bc9Schristos .exec = cmd_wait_for_exec
42928fc495Schristos };
43928fc495Schristos
44e9a2d6faSchristos struct wait_item {
45e9a2d6faSchristos struct cmdq_item *item;
46e9a2d6faSchristos TAILQ_ENTRY(wait_item) entry;
47e9a2d6faSchristos };
48e9a2d6faSchristos
49928fc495Schristos struct wait_channel {
50928fc495Schristos const char *name;
51928fc495Schristos int locked;
5299e242abSchristos int woken;
53928fc495Schristos
54e9a2d6faSchristos TAILQ_HEAD(, wait_item) waiters;
55e9a2d6faSchristos TAILQ_HEAD(, wait_item) lockers;
56928fc495Schristos
57928fc495Schristos RB_ENTRY(wait_channel) entry;
58928fc495Schristos };
59928fc495Schristos RB_HEAD(wait_channels, wait_channel);
60e9a2d6faSchristos static struct wait_channels wait_channels = RB_INITIALIZER(wait_channels);
61928fc495Schristos
62e9a2d6faSchristos static int wait_channel_cmp(struct wait_channel *, struct wait_channel *);
63e9a2d6faSchristos RB_GENERATE_STATIC(wait_channels, wait_channel, entry, wait_channel_cmp);
64928fc495Schristos
65e9a2d6faSchristos static int
wait_channel_cmp(struct wait_channel * wc1,struct wait_channel * wc2)66928fc495Schristos wait_channel_cmp(struct wait_channel *wc1, struct wait_channel *wc2)
67928fc495Schristos {
68928fc495Schristos return (strcmp(wc1->name, wc2->name));
69928fc495Schristos }
70928fc495Schristos
71e9a2d6faSchristos static enum cmd_retval cmd_wait_for_signal(struct cmdq_item *, const char *,
72928fc495Schristos struct wait_channel *);
73e9a2d6faSchristos static enum cmd_retval cmd_wait_for_wait(struct cmdq_item *, const char *,
74928fc495Schristos struct wait_channel *);
75e9a2d6faSchristos static enum cmd_retval cmd_wait_for_lock(struct cmdq_item *, const char *,
76928fc495Schristos struct wait_channel *);
77e9a2d6faSchristos static enum cmd_retval cmd_wait_for_unlock(struct cmdq_item *, const char *,
78928fc495Schristos struct wait_channel *);
79928fc495Schristos
80e9a2d6faSchristos static struct wait_channel *cmd_wait_for_add(const char *);
81e9a2d6faSchristos static void cmd_wait_for_remove(struct wait_channel *);
8299e242abSchristos
83e9a2d6faSchristos static struct wait_channel *
cmd_wait_for_add(const char * name)8499e242abSchristos cmd_wait_for_add(const char *name)
8599e242abSchristos {
8699e242abSchristos struct wait_channel *wc;
8799e242abSchristos
8899e242abSchristos wc = xmalloc(sizeof *wc);
8999e242abSchristos wc->name = xstrdup(name);
9099e242abSchristos
9199e242abSchristos wc->locked = 0;
9299e242abSchristos wc->woken = 0;
9399e242abSchristos
9499e242abSchristos TAILQ_INIT(&wc->waiters);
9599e242abSchristos TAILQ_INIT(&wc->lockers);
9699e242abSchristos
9799e242abSchristos RB_INSERT(wait_channels, &wait_channels, wc);
9899e242abSchristos
9999e242abSchristos log_debug("add wait channel %s", wc->name);
10099e242abSchristos
10199e242abSchristos return (wc);
10299e242abSchristos }
10399e242abSchristos
104e9a2d6faSchristos static void
cmd_wait_for_remove(struct wait_channel * wc)10599e242abSchristos cmd_wait_for_remove(struct wait_channel *wc)
10699e242abSchristos {
10799e242abSchristos if (wc->locked)
10899e242abSchristos return;
10999e242abSchristos if (!TAILQ_EMPTY(&wc->waiters) || !wc->woken)
11099e242abSchristos return;
11199e242abSchristos
11299e242abSchristos log_debug("remove wait channel %s", wc->name);
11399e242abSchristos
11499e242abSchristos RB_REMOVE(wait_channels, &wait_channels, wc);
11599e242abSchristos
11699e242abSchristos free(__UNCONST(wc->name));
11799e242abSchristos free(wc);
11899e242abSchristos }
11999e242abSchristos
120e9a2d6faSchristos static enum cmd_retval
cmd_wait_for_exec(struct cmd * self,struct cmdq_item * item)121e9a2d6faSchristos cmd_wait_for_exec(struct cmd *self, struct cmdq_item *item)
122928fc495Schristos {
123e271dbb8Schristos struct args *args = cmd_get_args(self);
124*46548964Swiz const char *name = args_string(args, 0);
125*46548964Swiz struct wait_channel *wc, find;
126928fc495Schristos
127*46548964Swiz find.name = name;
128*46548964Swiz wc = RB_FIND(wait_channels, &wait_channels, &find);
129928fc495Schristos
130928fc495Schristos if (args_has(args, 'S'))
131e9a2d6faSchristos return (cmd_wait_for_signal(item, name, wc));
132928fc495Schristos if (args_has(args, 'L'))
133e9a2d6faSchristos return (cmd_wait_for_lock(item, name, wc));
134928fc495Schristos if (args_has(args, 'U'))
135e9a2d6faSchristos return (cmd_wait_for_unlock(item, name, wc));
136e9a2d6faSchristos return (cmd_wait_for_wait(item, name, wc));
137928fc495Schristos }
138928fc495Schristos
139e9a2d6faSchristos static enum cmd_retval
cmd_wait_for_signal(__unused struct cmdq_item * item,const char * name,struct wait_channel * wc)140e9a2d6faSchristos cmd_wait_for_signal(__unused struct cmdq_item *item, const char *name,
141928fc495Schristos struct wait_channel *wc)
142928fc495Schristos {
143e9a2d6faSchristos struct wait_item *wi, *wi1;
144928fc495Schristos
14599e242abSchristos if (wc == NULL)
14699e242abSchristos wc = cmd_wait_for_add(name);
14799e242abSchristos
14899e242abSchristos if (TAILQ_EMPTY(&wc->waiters) && !wc->woken) {
14999e242abSchristos log_debug("signal wait channel %s, no waiters", wc->name);
15099e242abSchristos wc->woken = 1;
15199e242abSchristos return (CMD_RETURN_NORMAL);
152928fc495Schristos }
15399e242abSchristos log_debug("signal wait channel %s, with waiters", wc->name);
154928fc495Schristos
155e9a2d6faSchristos TAILQ_FOREACH_SAFE(wi, &wc->waiters, entry, wi1) {
15630744affSchristos cmdq_continue(wi->item);
157e9a2d6faSchristos
158e9a2d6faSchristos TAILQ_REMOVE(&wc->waiters, wi, entry);
159e9a2d6faSchristos free(wi);
160928fc495Schristos }
161928fc495Schristos
16299e242abSchristos cmd_wait_for_remove(wc);
163928fc495Schristos return (CMD_RETURN_NORMAL);
164928fc495Schristos }
165928fc495Schristos
166e9a2d6faSchristos static enum cmd_retval
cmd_wait_for_wait(struct cmdq_item * item,const char * name,struct wait_channel * wc)167e9a2d6faSchristos cmd_wait_for_wait(struct cmdq_item *item, const char *name,
168928fc495Schristos struct wait_channel *wc)
169928fc495Schristos {
170e271dbb8Schristos struct client *c = cmdq_get_client(item);
171e9a2d6faSchristos struct wait_item *wi;
17299e242abSchristos
1730a274e86Schristos if (c == NULL) {
174e9a2d6faSchristos cmdq_error(item, "not able to wait");
175928fc495Schristos return (CMD_RETURN_ERROR);
176928fc495Schristos }
177928fc495Schristos
17899e242abSchristos if (wc == NULL)
17999e242abSchristos wc = cmd_wait_for_add(name);
18099e242abSchristos
18199e242abSchristos if (wc->woken) {
182f26e8bc9Schristos log_debug("wait channel %s already woken (%p)", wc->name, c);
18399e242abSchristos cmd_wait_for_remove(wc);
18499e242abSchristos return (CMD_RETURN_NORMAL);
185928fc495Schristos }
186f26e8bc9Schristos log_debug("wait channel %s not woken (%p)", wc->name, c);
187928fc495Schristos
188e9a2d6faSchristos wi = xcalloc(1, sizeof *wi);
189e9a2d6faSchristos wi->item = item;
190e9a2d6faSchristos TAILQ_INSERT_TAIL(&wc->waiters, wi, entry);
191928fc495Schristos
192928fc495Schristos return (CMD_RETURN_WAIT);
193928fc495Schristos }
194928fc495Schristos
195e9a2d6faSchristos static enum cmd_retval
cmd_wait_for_lock(struct cmdq_item * item,const char * name,struct wait_channel * wc)196e9a2d6faSchristos cmd_wait_for_lock(struct cmdq_item *item, const char *name,
197928fc495Schristos struct wait_channel *wc)
198928fc495Schristos {
199e9a2d6faSchristos struct wait_item *wi;
200e9a2d6faSchristos
201e271dbb8Schristos if (cmdq_get_client(item) == NULL) {
202e9a2d6faSchristos cmdq_error(item, "not able to lock");
203928fc495Schristos return (CMD_RETURN_ERROR);
204928fc495Schristos }
205928fc495Schristos
20699e242abSchristos if (wc == NULL)
20799e242abSchristos wc = cmd_wait_for_add(name);
208928fc495Schristos
209928fc495Schristos if (wc->locked) {
210e9a2d6faSchristos wi = xcalloc(1, sizeof *wi);
211e9a2d6faSchristos wi->item = item;
212e9a2d6faSchristos TAILQ_INSERT_TAIL(&wc->lockers, wi, entry);
213928fc495Schristos return (CMD_RETURN_WAIT);
214928fc495Schristos }
215928fc495Schristos wc->locked = 1;
216928fc495Schristos
217928fc495Schristos return (CMD_RETURN_NORMAL);
218928fc495Schristos }
219928fc495Schristos
220e9a2d6faSchristos static enum cmd_retval
cmd_wait_for_unlock(struct cmdq_item * item,const char * name,struct wait_channel * wc)221e9a2d6faSchristos cmd_wait_for_unlock(struct cmdq_item *item, const char *name,
222928fc495Schristos struct wait_channel *wc)
223928fc495Schristos {
224e9a2d6faSchristos struct wait_item *wi;
225928fc495Schristos
226928fc495Schristos if (wc == NULL || !wc->locked) {
227e9a2d6faSchristos cmdq_error(item, "channel %s not locked", name);
228928fc495Schristos return (CMD_RETURN_ERROR);
229928fc495Schristos }
230928fc495Schristos
231e9a2d6faSchristos if ((wi = TAILQ_FIRST(&wc->lockers)) != NULL) {
23230744affSchristos cmdq_continue(wi->item);
233e9a2d6faSchristos TAILQ_REMOVE(&wc->lockers, wi, entry);
234e9a2d6faSchristos free(wi);
235928fc495Schristos } else {
236928fc495Schristos wc->locked = 0;
23799e242abSchristos cmd_wait_for_remove(wc);
238928fc495Schristos }
239928fc495Schristos
240928fc495Schristos return (CMD_RETURN_NORMAL);
241928fc495Schristos }
242928fc495Schristos
24399e242abSchristos void
cmd_wait_for_flush(void)24499e242abSchristos cmd_wait_for_flush(void)
24599e242abSchristos {
24699e242abSchristos struct wait_channel *wc, *wc1;
247e9a2d6faSchristos struct wait_item *wi, *wi1;
24899e242abSchristos
24999e242abSchristos RB_FOREACH_SAFE(wc, wait_channels, &wait_channels, wc1) {
250e9a2d6faSchristos TAILQ_FOREACH_SAFE(wi, &wc->waiters, entry, wi1) {
25130744affSchristos cmdq_continue(wi->item);
252e9a2d6faSchristos TAILQ_REMOVE(&wc->waiters, wi, entry);
253e9a2d6faSchristos free(wi);
25499e242abSchristos }
25599e242abSchristos wc->woken = 1;
256e9a2d6faSchristos TAILQ_FOREACH_SAFE(wi, &wc->lockers, entry, wi1) {
25730744affSchristos cmdq_continue(wi->item);
258e9a2d6faSchristos TAILQ_REMOVE(&wc->lockers, wi, entry);
259e9a2d6faSchristos free(wi);
26099e242abSchristos }
26199e242abSchristos wc->locked = 0;
26299e242abSchristos cmd_wait_for_remove(wc);
26399e242abSchristos }
26499e242abSchristos }
265