xref: /minix/external/bsd/tmux/dist/cmd-wait-for.c (revision 0a6a1f1d)
1 /* Id */
2 
3 /*
4  * Copyright (c) 2013 Nicholas Marriott <nicm@users.sourceforge.net>
5  * Copyright (c) 2013 Thiago de Arruda <tpadilha84@gmail.com>
6  *
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.
10  *
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.
18  */
19 
20 #include <sys/types.h>
21 
22 #include <stdlib.h>
23 #include <string.h>
24 
25 #include "tmux.h"
26 
27 /*
28  * Block or wake a client on a named wait channel.
29  */
30 
31 enum cmd_retval cmd_wait_for_exec(struct cmd *, struct cmd_q *);
32 
33 const struct cmd_entry cmd_wait_for_entry = {
34 	"wait-for", "wait",
35 	"LSU", 1, 1,
36 	"[-L|-S|-U] channel",
37 	0,
38 	NULL,
39 	cmd_wait_for_exec
40 };
41 
42 struct wait_channel {
43 	const char	       *name;
44 	int			locked;
45 
46 	TAILQ_HEAD(, cmd_q)	waiters;
47 	TAILQ_HEAD(, cmd_q)	lockers;
48 
49 	RB_ENTRY(wait_channel)	entry;
50 };
51 RB_HEAD(wait_channels, wait_channel);
52 struct wait_channels wait_channels = RB_INITIALIZER(wait_channels);
53 
54 int	wait_channel_cmp(struct wait_channel *, struct wait_channel *);
55 RB_PROTOTYPE(wait_channels, wait_channel, entry, wait_channel_cmp);
56 RB_GENERATE(wait_channels, wait_channel, entry, wait_channel_cmp);
57 
58 int
wait_channel_cmp(struct wait_channel * wc1,struct wait_channel * wc2)59 wait_channel_cmp(struct wait_channel *wc1, struct wait_channel *wc2)
60 {
61 	return (strcmp(wc1->name, wc2->name));
62 }
63 
64 enum cmd_retval	cmd_wait_for_signal(struct cmd_q *, const char *,
65 		    struct wait_channel *);
66 enum cmd_retval	cmd_wait_for_wait(struct cmd_q *, const char *,
67 		    struct wait_channel *);
68 enum cmd_retval	cmd_wait_for_lock(struct cmd_q *, const char *,
69 		    struct wait_channel *);
70 enum cmd_retval	cmd_wait_for_unlock(struct cmd_q *, const char *,
71 		    struct wait_channel *);
72 
73 enum cmd_retval
cmd_wait_for_exec(struct cmd * self,struct cmd_q * cmdq)74 cmd_wait_for_exec(struct cmd *self, struct cmd_q *cmdq)
75 {
76 	struct args     	*args = self->args;
77 	const char		*name = args->argv[0];
78 	struct wait_channel	*wc, wc0;
79 
80 	wc0.name = name;
81 	wc = RB_FIND(wait_channels, &wait_channels, &wc0);
82 
83 	if (args_has(args, 'S'))
84 		return (cmd_wait_for_signal(cmdq, name, wc));
85 	if (args_has(args, 'L'))
86 		return (cmd_wait_for_lock(cmdq, name, wc));
87 	if (args_has(args, 'U'))
88 		return (cmd_wait_for_unlock(cmdq, name, wc));
89 	return (cmd_wait_for_wait(cmdq, name, wc));
90 }
91 
92 enum cmd_retval
cmd_wait_for_signal(struct cmd_q * cmdq,const char * name,struct wait_channel * wc)93 cmd_wait_for_signal(struct cmd_q *cmdq, const char *name,
94     struct wait_channel *wc)
95 {
96 	struct cmd_q	*wq, *wq1;
97 
98 	if (wc == NULL || TAILQ_EMPTY(&wc->waiters)) {
99 		cmdq_error(cmdq, "no waiting clients on %s", name);
100 		return (CMD_RETURN_ERROR);
101 	}
102 
103 	TAILQ_FOREACH_SAFE(wq, &wc->waiters, waitentry, wq1) {
104 		TAILQ_REMOVE(&wc->waiters, wq, waitentry);
105 		if (!cmdq_free(wq))
106 			cmdq_continue(wq);
107 	}
108 
109 	if (!wc->locked) {
110 		RB_REMOVE(wait_channels, &wait_channels, wc);
111 		free(__UNCONST(wc->name));
112 		free(wc);
113 	}
114 
115 	return (CMD_RETURN_NORMAL);
116 }
117 
118 enum cmd_retval
cmd_wait_for_wait(struct cmd_q * cmdq,const char * name,struct wait_channel * wc)119 cmd_wait_for_wait(struct cmd_q *cmdq, const char *name,
120     struct wait_channel *wc)
121 {
122 	if (cmdq->client == NULL || cmdq->client->session != NULL) {
123 		cmdq_error(cmdq, "not able to wait");
124 		return (CMD_RETURN_ERROR);
125 	}
126 
127 	if (wc == NULL) {
128 		wc = xmalloc(sizeof *wc);
129 		wc->name = xstrdup(name);
130 		wc->locked = 0;
131 		TAILQ_INIT(&wc->waiters);
132 		TAILQ_INIT(&wc->lockers);
133 		RB_INSERT(wait_channels, &wait_channels, wc);
134 	}
135 
136 	TAILQ_INSERT_TAIL(&wc->waiters, cmdq, waitentry);
137 	cmdq->references++;
138 
139 	return (CMD_RETURN_WAIT);
140 }
141 
142 enum cmd_retval
cmd_wait_for_lock(struct cmd_q * cmdq,const char * name,struct wait_channel * wc)143 cmd_wait_for_lock(struct cmd_q *cmdq, const char *name,
144     struct wait_channel *wc)
145 {
146 	if (cmdq->client == NULL || cmdq->client->session != NULL) {
147 		cmdq_error(cmdq, "not able to lock");
148 		return (CMD_RETURN_ERROR);
149 	}
150 
151 	if (wc == NULL) {
152 		wc = xmalloc(sizeof *wc);
153 		wc->name = xstrdup(name);
154 		wc->locked = 0;
155 		TAILQ_INIT(&wc->waiters);
156 		TAILQ_INIT(&wc->lockers);
157 		RB_INSERT(wait_channels, &wait_channels, wc);
158 	}
159 
160 	if (wc->locked) {
161 		TAILQ_INSERT_TAIL(&wc->lockers, cmdq, waitentry);
162 		cmdq->references++;
163 		return (CMD_RETURN_WAIT);
164 	}
165 	wc->locked = 1;
166 
167 	return (CMD_RETURN_NORMAL);
168 }
169 
170 enum cmd_retval
cmd_wait_for_unlock(struct cmd_q * cmdq,const char * name,struct wait_channel * wc)171 cmd_wait_for_unlock(struct cmd_q *cmdq, const char *name,
172     struct wait_channel *wc)
173 {
174 	struct cmd_q	*wq;
175 
176 	if (wc == NULL || !wc->locked) {
177 		cmdq_error(cmdq, "channel %s not locked", name);
178 		return (CMD_RETURN_ERROR);
179 	}
180 
181 	if ((wq = TAILQ_FIRST(&wc->lockers)) != NULL) {
182 		TAILQ_REMOVE(&wc->lockers, wq, waitentry);
183 		if (!cmdq_free(wq))
184 			cmdq_continue(wq);
185 	} else {
186 		wc->locked = 0;
187 		if (TAILQ_EMPTY(&wc->waiters)) {
188 			RB_REMOVE(wait_channels, &wait_channels, wc);
189 			free(__UNCONST(wc->name));
190 			free(wc);
191 		}
192 	}
193 
194 	return (CMD_RETURN_NORMAL);
195 }
196 
197