xref: /openbsd-src/usr.bin/tmux/cmd-wait-for.c (revision a51dead1c4d3ed85038c588391e643b117324e5a)
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