xref: /minix3/external/bsd/tmux/dist/cmd-wait-for.c (revision 0a6a1f1d05b60e214de2f05a7310ddd1f0e590e7)
1*0a6a1f1dSLionel Sambuc /* Id */
2*0a6a1f1dSLionel Sambuc 
3*0a6a1f1dSLionel Sambuc /*
4*0a6a1f1dSLionel Sambuc  * Copyright (c) 2013 Nicholas Marriott <nicm@users.sourceforge.net>
5*0a6a1f1dSLionel Sambuc  * Copyright (c) 2013 Thiago de Arruda <tpadilha84@gmail.com>
6*0a6a1f1dSLionel Sambuc  *
7*0a6a1f1dSLionel Sambuc  * Permission to use, copy, modify, and distribute this software for any
8*0a6a1f1dSLionel Sambuc  * purpose with or without fee is hereby granted, provided that the above
9*0a6a1f1dSLionel Sambuc  * copyright notice and this permission notice appear in all copies.
10*0a6a1f1dSLionel Sambuc  *
11*0a6a1f1dSLionel Sambuc  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
12*0a6a1f1dSLionel Sambuc  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
13*0a6a1f1dSLionel Sambuc  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
14*0a6a1f1dSLionel Sambuc  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
15*0a6a1f1dSLionel Sambuc  * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER
16*0a6a1f1dSLionel Sambuc  * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
17*0a6a1f1dSLionel Sambuc  * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
18*0a6a1f1dSLionel Sambuc  */
19*0a6a1f1dSLionel Sambuc 
20*0a6a1f1dSLionel Sambuc #include <sys/types.h>
21*0a6a1f1dSLionel Sambuc 
22*0a6a1f1dSLionel Sambuc #include <stdlib.h>
23*0a6a1f1dSLionel Sambuc #include <string.h>
24*0a6a1f1dSLionel Sambuc 
25*0a6a1f1dSLionel Sambuc #include "tmux.h"
26*0a6a1f1dSLionel Sambuc 
27*0a6a1f1dSLionel Sambuc /*
28*0a6a1f1dSLionel Sambuc  * Block or wake a client on a named wait channel.
29*0a6a1f1dSLionel Sambuc  */
30*0a6a1f1dSLionel Sambuc 
31*0a6a1f1dSLionel Sambuc enum cmd_retval cmd_wait_for_exec(struct cmd *, struct cmd_q *);
32*0a6a1f1dSLionel Sambuc 
33*0a6a1f1dSLionel Sambuc const struct cmd_entry cmd_wait_for_entry = {
34*0a6a1f1dSLionel Sambuc 	"wait-for", "wait",
35*0a6a1f1dSLionel Sambuc 	"LSU", 1, 1,
36*0a6a1f1dSLionel Sambuc 	"[-L|-S|-U] channel",
37*0a6a1f1dSLionel Sambuc 	0,
38*0a6a1f1dSLionel Sambuc 	NULL,
39*0a6a1f1dSLionel Sambuc 	cmd_wait_for_exec
40*0a6a1f1dSLionel Sambuc };
41*0a6a1f1dSLionel Sambuc 
42*0a6a1f1dSLionel Sambuc struct wait_channel {
43*0a6a1f1dSLionel Sambuc 	const char	       *name;
44*0a6a1f1dSLionel Sambuc 	int			locked;
45*0a6a1f1dSLionel Sambuc 
46*0a6a1f1dSLionel Sambuc 	TAILQ_HEAD(, cmd_q)	waiters;
47*0a6a1f1dSLionel Sambuc 	TAILQ_HEAD(, cmd_q)	lockers;
48*0a6a1f1dSLionel Sambuc 
49*0a6a1f1dSLionel Sambuc 	RB_ENTRY(wait_channel)	entry;
50*0a6a1f1dSLionel Sambuc };
51*0a6a1f1dSLionel Sambuc RB_HEAD(wait_channels, wait_channel);
52*0a6a1f1dSLionel Sambuc struct wait_channels wait_channels = RB_INITIALIZER(wait_channels);
53*0a6a1f1dSLionel Sambuc 
54*0a6a1f1dSLionel Sambuc int	wait_channel_cmp(struct wait_channel *, struct wait_channel *);
55*0a6a1f1dSLionel Sambuc RB_PROTOTYPE(wait_channels, wait_channel, entry, wait_channel_cmp);
56*0a6a1f1dSLionel Sambuc RB_GENERATE(wait_channels, wait_channel, entry, wait_channel_cmp);
57*0a6a1f1dSLionel Sambuc 
58*0a6a1f1dSLionel Sambuc int
wait_channel_cmp(struct wait_channel * wc1,struct wait_channel * wc2)59*0a6a1f1dSLionel Sambuc wait_channel_cmp(struct wait_channel *wc1, struct wait_channel *wc2)
60*0a6a1f1dSLionel Sambuc {
61*0a6a1f1dSLionel Sambuc 	return (strcmp(wc1->name, wc2->name));
62*0a6a1f1dSLionel Sambuc }
63*0a6a1f1dSLionel Sambuc 
64*0a6a1f1dSLionel Sambuc enum cmd_retval	cmd_wait_for_signal(struct cmd_q *, const char *,
65*0a6a1f1dSLionel Sambuc 		    struct wait_channel *);
66*0a6a1f1dSLionel Sambuc enum cmd_retval	cmd_wait_for_wait(struct cmd_q *, const char *,
67*0a6a1f1dSLionel Sambuc 		    struct wait_channel *);
68*0a6a1f1dSLionel Sambuc enum cmd_retval	cmd_wait_for_lock(struct cmd_q *, const char *,
69*0a6a1f1dSLionel Sambuc 		    struct wait_channel *);
70*0a6a1f1dSLionel Sambuc enum cmd_retval	cmd_wait_for_unlock(struct cmd_q *, const char *,
71*0a6a1f1dSLionel Sambuc 		    struct wait_channel *);
72*0a6a1f1dSLionel Sambuc 
73*0a6a1f1dSLionel Sambuc enum cmd_retval
cmd_wait_for_exec(struct cmd * self,struct cmd_q * cmdq)74*0a6a1f1dSLionel Sambuc cmd_wait_for_exec(struct cmd *self, struct cmd_q *cmdq)
75*0a6a1f1dSLionel Sambuc {
76*0a6a1f1dSLionel Sambuc 	struct args     	*args = self->args;
77*0a6a1f1dSLionel Sambuc 	const char		*name = args->argv[0];
78*0a6a1f1dSLionel Sambuc 	struct wait_channel	*wc, wc0;
79*0a6a1f1dSLionel Sambuc 
80*0a6a1f1dSLionel Sambuc 	wc0.name = name;
81*0a6a1f1dSLionel Sambuc 	wc = RB_FIND(wait_channels, &wait_channels, &wc0);
82*0a6a1f1dSLionel Sambuc 
83*0a6a1f1dSLionel Sambuc 	if (args_has(args, 'S'))
84*0a6a1f1dSLionel Sambuc 		return (cmd_wait_for_signal(cmdq, name, wc));
85*0a6a1f1dSLionel Sambuc 	if (args_has(args, 'L'))
86*0a6a1f1dSLionel Sambuc 		return (cmd_wait_for_lock(cmdq, name, wc));
87*0a6a1f1dSLionel Sambuc 	if (args_has(args, 'U'))
88*0a6a1f1dSLionel Sambuc 		return (cmd_wait_for_unlock(cmdq, name, wc));
89*0a6a1f1dSLionel Sambuc 	return (cmd_wait_for_wait(cmdq, name, wc));
90*0a6a1f1dSLionel Sambuc }
91*0a6a1f1dSLionel Sambuc 
92*0a6a1f1dSLionel Sambuc enum cmd_retval
cmd_wait_for_signal(struct cmd_q * cmdq,const char * name,struct wait_channel * wc)93*0a6a1f1dSLionel Sambuc cmd_wait_for_signal(struct cmd_q *cmdq, const char *name,
94*0a6a1f1dSLionel Sambuc     struct wait_channel *wc)
95*0a6a1f1dSLionel Sambuc {
96*0a6a1f1dSLionel Sambuc 	struct cmd_q	*wq, *wq1;
97*0a6a1f1dSLionel Sambuc 
98*0a6a1f1dSLionel Sambuc 	if (wc == NULL || TAILQ_EMPTY(&wc->waiters)) {
99*0a6a1f1dSLionel Sambuc 		cmdq_error(cmdq, "no waiting clients on %s", name);
100*0a6a1f1dSLionel Sambuc 		return (CMD_RETURN_ERROR);
101*0a6a1f1dSLionel Sambuc 	}
102*0a6a1f1dSLionel Sambuc 
103*0a6a1f1dSLionel Sambuc 	TAILQ_FOREACH_SAFE(wq, &wc->waiters, waitentry, wq1) {
104*0a6a1f1dSLionel Sambuc 		TAILQ_REMOVE(&wc->waiters, wq, waitentry);
105*0a6a1f1dSLionel Sambuc 		if (!cmdq_free(wq))
106*0a6a1f1dSLionel Sambuc 			cmdq_continue(wq);
107*0a6a1f1dSLionel Sambuc 	}
108*0a6a1f1dSLionel Sambuc 
109*0a6a1f1dSLionel Sambuc 	if (!wc->locked) {
110*0a6a1f1dSLionel Sambuc 		RB_REMOVE(wait_channels, &wait_channels, wc);
111*0a6a1f1dSLionel Sambuc 		free(__UNCONST(wc->name));
112*0a6a1f1dSLionel Sambuc 		free(wc);
113*0a6a1f1dSLionel Sambuc 	}
114*0a6a1f1dSLionel Sambuc 
115*0a6a1f1dSLionel Sambuc 	return (CMD_RETURN_NORMAL);
116*0a6a1f1dSLionel Sambuc }
117*0a6a1f1dSLionel Sambuc 
118*0a6a1f1dSLionel Sambuc enum cmd_retval
cmd_wait_for_wait(struct cmd_q * cmdq,const char * name,struct wait_channel * wc)119*0a6a1f1dSLionel Sambuc cmd_wait_for_wait(struct cmd_q *cmdq, const char *name,
120*0a6a1f1dSLionel Sambuc     struct wait_channel *wc)
121*0a6a1f1dSLionel Sambuc {
122*0a6a1f1dSLionel Sambuc 	if (cmdq->client == NULL || cmdq->client->session != NULL) {
123*0a6a1f1dSLionel Sambuc 		cmdq_error(cmdq, "not able to wait");
124*0a6a1f1dSLionel Sambuc 		return (CMD_RETURN_ERROR);
125*0a6a1f1dSLionel Sambuc 	}
126*0a6a1f1dSLionel Sambuc 
127*0a6a1f1dSLionel Sambuc 	if (wc == NULL) {
128*0a6a1f1dSLionel Sambuc 		wc = xmalloc(sizeof *wc);
129*0a6a1f1dSLionel Sambuc 		wc->name = xstrdup(name);
130*0a6a1f1dSLionel Sambuc 		wc->locked = 0;
131*0a6a1f1dSLionel Sambuc 		TAILQ_INIT(&wc->waiters);
132*0a6a1f1dSLionel Sambuc 		TAILQ_INIT(&wc->lockers);
133*0a6a1f1dSLionel Sambuc 		RB_INSERT(wait_channels, &wait_channels, wc);
134*0a6a1f1dSLionel Sambuc 	}
135*0a6a1f1dSLionel Sambuc 
136*0a6a1f1dSLionel Sambuc 	TAILQ_INSERT_TAIL(&wc->waiters, cmdq, waitentry);
137*0a6a1f1dSLionel Sambuc 	cmdq->references++;
138*0a6a1f1dSLionel Sambuc 
139*0a6a1f1dSLionel Sambuc 	return (CMD_RETURN_WAIT);
140*0a6a1f1dSLionel Sambuc }
141*0a6a1f1dSLionel Sambuc 
142*0a6a1f1dSLionel Sambuc enum cmd_retval
cmd_wait_for_lock(struct cmd_q * cmdq,const char * name,struct wait_channel * wc)143*0a6a1f1dSLionel Sambuc cmd_wait_for_lock(struct cmd_q *cmdq, const char *name,
144*0a6a1f1dSLionel Sambuc     struct wait_channel *wc)
145*0a6a1f1dSLionel Sambuc {
146*0a6a1f1dSLionel Sambuc 	if (cmdq->client == NULL || cmdq->client->session != NULL) {
147*0a6a1f1dSLionel Sambuc 		cmdq_error(cmdq, "not able to lock");
148*0a6a1f1dSLionel Sambuc 		return (CMD_RETURN_ERROR);
149*0a6a1f1dSLionel Sambuc 	}
150*0a6a1f1dSLionel Sambuc 
151*0a6a1f1dSLionel Sambuc 	if (wc == NULL) {
152*0a6a1f1dSLionel Sambuc 		wc = xmalloc(sizeof *wc);
153*0a6a1f1dSLionel Sambuc 		wc->name = xstrdup(name);
154*0a6a1f1dSLionel Sambuc 		wc->locked = 0;
155*0a6a1f1dSLionel Sambuc 		TAILQ_INIT(&wc->waiters);
156*0a6a1f1dSLionel Sambuc 		TAILQ_INIT(&wc->lockers);
157*0a6a1f1dSLionel Sambuc 		RB_INSERT(wait_channels, &wait_channels, wc);
158*0a6a1f1dSLionel Sambuc 	}
159*0a6a1f1dSLionel Sambuc 
160*0a6a1f1dSLionel Sambuc 	if (wc->locked) {
161*0a6a1f1dSLionel Sambuc 		TAILQ_INSERT_TAIL(&wc->lockers, cmdq, waitentry);
162*0a6a1f1dSLionel Sambuc 		cmdq->references++;
163*0a6a1f1dSLionel Sambuc 		return (CMD_RETURN_WAIT);
164*0a6a1f1dSLionel Sambuc 	}
165*0a6a1f1dSLionel Sambuc 	wc->locked = 1;
166*0a6a1f1dSLionel Sambuc 
167*0a6a1f1dSLionel Sambuc 	return (CMD_RETURN_NORMAL);
168*0a6a1f1dSLionel Sambuc }
169*0a6a1f1dSLionel Sambuc 
170*0a6a1f1dSLionel Sambuc enum cmd_retval
cmd_wait_for_unlock(struct cmd_q * cmdq,const char * name,struct wait_channel * wc)171*0a6a1f1dSLionel Sambuc cmd_wait_for_unlock(struct cmd_q *cmdq, const char *name,
172*0a6a1f1dSLionel Sambuc     struct wait_channel *wc)
173*0a6a1f1dSLionel Sambuc {
174*0a6a1f1dSLionel Sambuc 	struct cmd_q	*wq;
175*0a6a1f1dSLionel Sambuc 
176*0a6a1f1dSLionel Sambuc 	if (wc == NULL || !wc->locked) {
177*0a6a1f1dSLionel Sambuc 		cmdq_error(cmdq, "channel %s not locked", name);
178*0a6a1f1dSLionel Sambuc 		return (CMD_RETURN_ERROR);
179*0a6a1f1dSLionel Sambuc 	}
180*0a6a1f1dSLionel Sambuc 
181*0a6a1f1dSLionel Sambuc 	if ((wq = TAILQ_FIRST(&wc->lockers)) != NULL) {
182*0a6a1f1dSLionel Sambuc 		TAILQ_REMOVE(&wc->lockers, wq, waitentry);
183*0a6a1f1dSLionel Sambuc 		if (!cmdq_free(wq))
184*0a6a1f1dSLionel Sambuc 			cmdq_continue(wq);
185*0a6a1f1dSLionel Sambuc 	} else {
186*0a6a1f1dSLionel Sambuc 		wc->locked = 0;
187*0a6a1f1dSLionel Sambuc 		if (TAILQ_EMPTY(&wc->waiters)) {
188*0a6a1f1dSLionel Sambuc 			RB_REMOVE(wait_channels, &wait_channels, wc);
189*0a6a1f1dSLionel Sambuc 			free(__UNCONST(wc->name));
190*0a6a1f1dSLionel Sambuc 			free(wc);
191*0a6a1f1dSLionel Sambuc 		}
192*0a6a1f1dSLionel Sambuc 	}
193*0a6a1f1dSLionel Sambuc 
194*0a6a1f1dSLionel Sambuc 	return (CMD_RETURN_NORMAL);
195*0a6a1f1dSLionel Sambuc }
196*0a6a1f1dSLionel Sambuc 
197