1*a51dead1Snicm /* $OpenBSD: cmd-wait-for.c,v 1.22 2021/08/21 10:22:39 nicm Exp $ */
28d127fbbSnicm
38d127fbbSnicm /*
498ca8272Snicm * Copyright (c) 2013 Nicholas Marriott <nicholas.marriott@gmail.com>
58d127fbbSnicm * Copyright (c) 2013 Thiago de Arruda <tpadilha84@gmail.com>
68d127fbbSnicm *
78d127fbbSnicm * Permission to use, copy, modify, and distribute this software for any
88d127fbbSnicm * purpose with or without fee is hereby granted, provided that the above
98d127fbbSnicm * copyright notice and this permission notice appear in all copies.
108d127fbbSnicm *
118d127fbbSnicm * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
128d127fbbSnicm * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
138d127fbbSnicm * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
148d127fbbSnicm * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
158d127fbbSnicm * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER
168d127fbbSnicm * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
178d127fbbSnicm * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
188d127fbbSnicm */
198d127fbbSnicm
208d127fbbSnicm #include <sys/types.h>
218d127fbbSnicm
228d127fbbSnicm #include <stdlib.h>
238d127fbbSnicm #include <string.h>
248d127fbbSnicm
258d127fbbSnicm #include "tmux.h"
268d127fbbSnicm
278d127fbbSnicm /*
288d127fbbSnicm * Block or wake a client on a named wait channel.
298d127fbbSnicm */
308d127fbbSnicm
3168e0a7f2Snicm static enum cmd_retval cmd_wait_for_exec(struct cmd *, struct cmdq_item *);
328d127fbbSnicm
338d127fbbSnicm const struct cmd_entry cmd_wait_for_entry = {
34c057646bSnicm .name = "wait-for",
35c057646bSnicm .alias = "wait",
36c057646bSnicm
37*a51dead1Snicm .args = { "LSU", 1, 1, NULL },
38c057646bSnicm .usage = "[-L|-S|-U] channel",
39c057646bSnicm
40c057646bSnicm .flags = 0,
41c057646bSnicm .exec = cmd_wait_for_exec
428d127fbbSnicm };
438d127fbbSnicm
44765b9a58Snicm struct wait_item {
4568e0a7f2Snicm struct cmdq_item *item;
46765b9a58Snicm TAILQ_ENTRY(wait_item) entry;
47765b9a58Snicm };
48765b9a58Snicm
498d127fbbSnicm struct wait_channel {
508d127fbbSnicm const char *name;
516afca3b0Snicm int locked;
52e2e43c59Snicm int woken;
536afca3b0Snicm
54765b9a58Snicm TAILQ_HEAD(, wait_item) waiters;
55765b9a58Snicm TAILQ_HEAD(, wait_item) lockers;
568d127fbbSnicm
578d127fbbSnicm RB_ENTRY(wait_channel) entry;
588d127fbbSnicm };
598d127fbbSnicm RB_HEAD(wait_channels, wait_channel);
60dc1f0f5fSnicm static struct wait_channels wait_channels = RB_INITIALIZER(wait_channels);
618d127fbbSnicm
62dc1f0f5fSnicm static int wait_channel_cmp(struct wait_channel *, struct wait_channel *);
63dc1f0f5fSnicm RB_GENERATE_STATIC(wait_channels, wait_channel, entry, wait_channel_cmp);
648d127fbbSnicm
65dc1f0f5fSnicm static int
wait_channel_cmp(struct wait_channel * wc1,struct wait_channel * wc2)668d127fbbSnicm wait_channel_cmp(struct wait_channel *wc1, struct wait_channel *wc2)
678d127fbbSnicm {
688d127fbbSnicm return (strcmp(wc1->name, wc2->name));
698d127fbbSnicm }
708d127fbbSnicm
7168e0a7f2Snicm static enum cmd_retval cmd_wait_for_signal(struct cmdq_item *, const char *,
728d127fbbSnicm struct wait_channel *);
7368e0a7f2Snicm static enum cmd_retval cmd_wait_for_wait(struct cmdq_item *, const char *,
748d127fbbSnicm struct wait_channel *);
7568e0a7f2Snicm static enum cmd_retval cmd_wait_for_lock(struct cmdq_item *, const char *,
766afca3b0Snicm struct wait_channel *);
7768e0a7f2Snicm static enum cmd_retval cmd_wait_for_unlock(struct cmdq_item *, const char *,
786afca3b0Snicm struct wait_channel *);
798d127fbbSnicm
80dc1f0f5fSnicm static struct wait_channel *cmd_wait_for_add(const char *);
8168e0a7f2Snicm static void cmd_wait_for_remove(struct wait_channel *);
82e2e43c59Snicm
83dc1f0f5fSnicm static struct wait_channel *
cmd_wait_for_add(const char * name)84e2e43c59Snicm cmd_wait_for_add(const char *name)
85e2e43c59Snicm {
86e2e43c59Snicm struct wait_channel *wc;
87e2e43c59Snicm
88e2e43c59Snicm wc = xmalloc(sizeof *wc);
89e2e43c59Snicm wc->name = xstrdup(name);
90e2e43c59Snicm
91e2e43c59Snicm wc->locked = 0;
92e2e43c59Snicm wc->woken = 0;
93e2e43c59Snicm
94e2e43c59Snicm TAILQ_INIT(&wc->waiters);
95e2e43c59Snicm TAILQ_INIT(&wc->lockers);
96e2e43c59Snicm
97e2e43c59Snicm RB_INSERT(wait_channels, &wait_channels, wc);
98e2e43c59Snicm
99e2e43c59Snicm log_debug("add wait channel %s", wc->name);
100e2e43c59Snicm
101e2e43c59Snicm return (wc);
102e2e43c59Snicm }
103e2e43c59Snicm
104dc1f0f5fSnicm static void
cmd_wait_for_remove(struct wait_channel * wc)105e2e43c59Snicm cmd_wait_for_remove(struct wait_channel *wc)
106e2e43c59Snicm {
107e2e43c59Snicm if (wc->locked)
108e2e43c59Snicm return;
109e2e43c59Snicm if (!TAILQ_EMPTY(&wc->waiters) || !wc->woken)
110e2e43c59Snicm return;
111e2e43c59Snicm
112e2e43c59Snicm log_debug("remove wait channel %s", wc->name);
113e2e43c59Snicm
114e2e43c59Snicm RB_REMOVE(wait_channels, &wait_channels, wc);
115e2e43c59Snicm
116e2e43c59Snicm free((void *)wc->name);
117e2e43c59Snicm free(wc);
118e2e43c59Snicm }
119e2e43c59Snicm
120dc1f0f5fSnicm static enum cmd_retval
cmd_wait_for_exec(struct cmd * self,struct cmdq_item * item)12168e0a7f2Snicm cmd_wait_for_exec(struct cmd *self, struct cmdq_item *item)
1228d127fbbSnicm {
12390d7ba38Snicm struct args *args = cmd_get_args(self);
1241693b10bSnicm const char *name = args_string(args, 0);
1251693b10bSnicm struct wait_channel *wc, find;
1268d127fbbSnicm
1271693b10bSnicm find.name = name;
1281693b10bSnicm wc = RB_FIND(wait_channels, &wait_channels, &find);
1298d127fbbSnicm
1308d127fbbSnicm if (args_has(args, 'S'))
13168e0a7f2Snicm return (cmd_wait_for_signal(item, name, wc));
1326afca3b0Snicm if (args_has(args, 'L'))
13368e0a7f2Snicm return (cmd_wait_for_lock(item, name, wc));
1346afca3b0Snicm if (args_has(args, 'U'))
13568e0a7f2Snicm return (cmd_wait_for_unlock(item, name, wc));
13668e0a7f2Snicm return (cmd_wait_for_wait(item, name, wc));
1378d127fbbSnicm }
1388d127fbbSnicm
139dc1f0f5fSnicm static enum cmd_retval
cmd_wait_for_signal(__unused struct cmdq_item * item,const char * name,struct wait_channel * wc)14068e0a7f2Snicm cmd_wait_for_signal(__unused struct cmdq_item *item, const char *name,
1418d127fbbSnicm struct wait_channel *wc)
1428d127fbbSnicm {
143765b9a58Snicm struct wait_item *wi, *wi1;
1448d127fbbSnicm
145e2e43c59Snicm if (wc == NULL)
146e2e43c59Snicm wc = cmd_wait_for_add(name);
147e2e43c59Snicm
148e2e43c59Snicm if (TAILQ_EMPTY(&wc->waiters) && !wc->woken) {
149e2e43c59Snicm log_debug("signal wait channel %s, no waiters", wc->name);
150e2e43c59Snicm wc->woken = 1;
151e2e43c59Snicm return (CMD_RETURN_NORMAL);
1528d127fbbSnicm }
153e2e43c59Snicm log_debug("signal wait channel %s, with waiters", wc->name);
1548d127fbbSnicm
155765b9a58Snicm TAILQ_FOREACH_SAFE(wi, &wc->waiters, entry, wi1) {
156780ca620Snicm cmdq_continue(wi->item);
157765b9a58Snicm
158765b9a58Snicm TAILQ_REMOVE(&wc->waiters, wi, entry);
159765b9a58Snicm free(wi);
1608d127fbbSnicm }
1616afca3b0Snicm
162e2e43c59Snicm cmd_wait_for_remove(wc);
1638d127fbbSnicm return (CMD_RETURN_NORMAL);
1648d127fbbSnicm }
1658d127fbbSnicm
166dc1f0f5fSnicm static enum cmd_retval
cmd_wait_for_wait(struct cmdq_item * item,const char * name,struct wait_channel * wc)16768e0a7f2Snicm cmd_wait_for_wait(struct cmdq_item *item, const char *name,
16868e0a7f2Snicm struct wait_channel *wc)
1698d127fbbSnicm {
170040343aeSnicm struct client *c = cmdq_get_client(item);
171765b9a58Snicm struct wait_item *wi;
172e2e43c59Snicm
173a9adeebdSnicm if (c == NULL) {
17468e0a7f2Snicm cmdq_error(item, "not able to wait");
1758d127fbbSnicm return (CMD_RETURN_ERROR);
1768d127fbbSnicm }
1778d127fbbSnicm
178e2e43c59Snicm if (wc == NULL)
179e2e43c59Snicm wc = cmd_wait_for_add(name);
180e2e43c59Snicm
181e2e43c59Snicm if (wc->woken) {
1827ad4d2ccSnicm log_debug("wait channel %s already woken (%p)", wc->name, c);
183e2e43c59Snicm cmd_wait_for_remove(wc);
184e2e43c59Snicm return (CMD_RETURN_NORMAL);
1858d127fbbSnicm }
1867ad4d2ccSnicm log_debug("wait channel %s not woken (%p)", wc->name, c);
1876afca3b0Snicm
188765b9a58Snicm wi = xcalloc(1, sizeof *wi);
18968e0a7f2Snicm wi->item = item;
190765b9a58Snicm TAILQ_INSERT_TAIL(&wc->waiters, wi, entry);
1918d127fbbSnicm
1928d127fbbSnicm return (CMD_RETURN_WAIT);
1938d127fbbSnicm }
1946afca3b0Snicm
195dc1f0f5fSnicm static enum cmd_retval
cmd_wait_for_lock(struct cmdq_item * item,const char * name,struct wait_channel * wc)19668e0a7f2Snicm cmd_wait_for_lock(struct cmdq_item *item, const char *name,
19768e0a7f2Snicm struct wait_channel *wc)
1986afca3b0Snicm {
199765b9a58Snicm struct wait_item *wi;
200765b9a58Snicm
201040343aeSnicm if (cmdq_get_client(item) == NULL) {
20268e0a7f2Snicm cmdq_error(item, "not able to lock");
2036afca3b0Snicm return (CMD_RETURN_ERROR);
2046afca3b0Snicm }
2056afca3b0Snicm
206e2e43c59Snicm if (wc == NULL)
207e2e43c59Snicm wc = cmd_wait_for_add(name);
2086afca3b0Snicm
2096afca3b0Snicm if (wc->locked) {
210765b9a58Snicm wi = xcalloc(1, sizeof *wi);
21168e0a7f2Snicm wi->item = item;
212765b9a58Snicm TAILQ_INSERT_TAIL(&wc->lockers, wi, entry);
2136afca3b0Snicm return (CMD_RETURN_WAIT);
2146afca3b0Snicm }
2156afca3b0Snicm wc->locked = 1;
2166afca3b0Snicm
2176afca3b0Snicm return (CMD_RETURN_NORMAL);
2186afca3b0Snicm }
2196afca3b0Snicm
220dc1f0f5fSnicm static enum cmd_retval
cmd_wait_for_unlock(struct cmdq_item * item,const char * name,struct wait_channel * wc)22168e0a7f2Snicm cmd_wait_for_unlock(struct cmdq_item *item, const char *name,
2226afca3b0Snicm struct wait_channel *wc)
2236afca3b0Snicm {
224765b9a58Snicm struct wait_item *wi;
2256afca3b0Snicm
2266afca3b0Snicm if (wc == NULL || !wc->locked) {
22768e0a7f2Snicm cmdq_error(item, "channel %s not locked", name);
2286afca3b0Snicm return (CMD_RETURN_ERROR);
2296afca3b0Snicm }
2306afca3b0Snicm
231765b9a58Snicm if ((wi = TAILQ_FIRST(&wc->lockers)) != NULL) {
232780ca620Snicm cmdq_continue(wi->item);
233765b9a58Snicm TAILQ_REMOVE(&wc->lockers, wi, entry);
234765b9a58Snicm free(wi);
2356afca3b0Snicm } else {
2366afca3b0Snicm wc->locked = 0;
237e2e43c59Snicm cmd_wait_for_remove(wc);
2386afca3b0Snicm }
2396afca3b0Snicm
2406afca3b0Snicm return (CMD_RETURN_NORMAL);
2416afca3b0Snicm }
2426afca3b0Snicm
2437d5939e2Snicm void
cmd_wait_for_flush(void)2447d5939e2Snicm cmd_wait_for_flush(void)
2457d5939e2Snicm {
2467d5939e2Snicm struct wait_channel *wc, *wc1;
247765b9a58Snicm struct wait_item *wi, *wi1;
2487d5939e2Snicm
2497d5939e2Snicm RB_FOREACH_SAFE(wc, wait_channels, &wait_channels, wc1) {
250765b9a58Snicm TAILQ_FOREACH_SAFE(wi, &wc->waiters, entry, wi1) {
251780ca620Snicm cmdq_continue(wi->item);
252765b9a58Snicm TAILQ_REMOVE(&wc->waiters, wi, entry);
253765b9a58Snicm free(wi);
2547d5939e2Snicm }
255523072a5Snicm wc->woken = 1;
256765b9a58Snicm TAILQ_FOREACH_SAFE(wi, &wc->lockers, entry, wi1) {
257780ca620Snicm cmdq_continue(wi->item);
258765b9a58Snicm TAILQ_REMOVE(&wc->lockers, wi, entry);
259765b9a58Snicm free(wi);
2607d5939e2Snicm }
261e2e43c59Snicm wc->locked = 0;
262e2e43c59Snicm cmd_wait_for_remove(wc);
2637d5939e2Snicm }
2647d5939e2Snicm }
265