4 * Copyright (c) 2013 Nicholas Marriott <nicholas.marriott@gmail.com>
5 * Copyright (c) 2013 Thiago de Arruda <tpadilha84@gmail.com>
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.
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.
20 #include <sys/types.h>
28 * Block or wake a client on a named wait channel.
31 static enum cmd_retval
cmd_wait_for_exec(struct cmd
*, struct cmdq_item
*);
33 const struct cmd_entry cmd_wait_for_entry
= {
37 .args
= { "LSU", 1, 1, NULL
},
38 .usage
= "[-L|-S|-U] channel",
41 .exec
= cmd_wait_for_exec
45 struct cmdq_item
*item
;
46 TAILQ_ENTRY(wait_item
) entry
;
54 TAILQ_HEAD(, wait_item
) waiters
;
55 TAILQ_HEAD(, wait_item
) lockers
;
57 RB_ENTRY(wait_channel
) entry
;
59 RB_HEAD(wait_channels
, wait_channel
);
60 static struct wait_channels wait_channels
= RB_INITIALIZER(wait_channels
);
62 static int wait_channel_cmp(struct wait_channel
*, struct wait_channel
*);
63 RB_GENERATE_STATIC(wait_channels
, wait_channel
, entry
, wait_channel_cmp
);
66 wait_channel_cmp(struct wait_channel
*wc1
, struct wait_channel
*wc2
)
68 return (strcmp(wc1
->name
, wc2
->name
));
71 static enum cmd_retval
cmd_wait_for_signal(struct cmdq_item
*, const char *,
72 struct wait_channel
*);
73 static enum cmd_retval
cmd_wait_for_wait(struct cmdq_item
*, const char *,
74 struct wait_channel
*);
75 static enum cmd_retval
cmd_wait_for_lock(struct cmdq_item
*, const char *,
76 struct wait_channel
*);
77 static enum cmd_retval
cmd_wait_for_unlock(struct cmdq_item
*, const char *,
78 struct wait_channel
*);
80 static struct wait_channel
*cmd_wait_for_add(const char *);
81 static void cmd_wait_for_remove(struct wait_channel
*);
83 static struct wait_channel
*
84 cmd_wait_for_add(const char *name
)
86 struct wait_channel
*wc
;
88 wc
= xmalloc(sizeof *wc
);
89 wc
->name
= xstrdup(name
);
94 TAILQ_INIT(&wc
->waiters
);
95 TAILQ_INIT(&wc
->lockers
);
97 RB_INSERT(wait_channels
, &wait_channels
, wc
);
99 log_debug("add wait channel %s", wc
->name
);
105 cmd_wait_for_remove(struct wait_channel
*wc
)
109 if (!TAILQ_EMPTY(&wc
->waiters
) || !wc
->woken
)
112 log_debug("remove wait channel %s", wc
->name
);
114 RB_REMOVE(wait_channels
, &wait_channels
, wc
);
116 free((void *)wc
->name
);
120 static enum cmd_retval
121 cmd_wait_for_exec(struct cmd
*self
, struct cmdq_item
*item
)
123 struct args
*args
= cmd_get_args(self
);
124 const char *name
= args_string(args
, 0);
125 struct wait_channel
*wc
, find
;
128 wc
= RB_FIND(wait_channels
, &wait_channels
, &find
);
130 if (args_has(args
, 'S'))
131 return (cmd_wait_for_signal(item
, name
, wc
));
132 if (args_has(args
, 'L'))
133 return (cmd_wait_for_lock(item
, name
, wc
));
134 if (args_has(args
, 'U'))
135 return (cmd_wait_for_unlock(item
, name
, wc
));
136 return (cmd_wait_for_wait(item
, name
, wc
));
139 static enum cmd_retval
140 cmd_wait_for_signal(__unused
struct cmdq_item
*item
, const char *name
,
141 struct wait_channel
*wc
)
143 struct wait_item
*wi
, *wi1
;
146 wc
= cmd_wait_for_add(name
);
148 if (TAILQ_EMPTY(&wc
->waiters
) && !wc
->woken
) {
149 log_debug("signal wait channel %s, no waiters", wc
->name
);
151 return (CMD_RETURN_NORMAL
);
153 log_debug("signal wait channel %s, with waiters", wc
->name
);
155 TAILQ_FOREACH_SAFE(wi
, &wc
->waiters
, entry
, wi1
) {
156 cmdq_continue(wi
->item
);
158 TAILQ_REMOVE(&wc
->waiters
, wi
, entry
);
162 cmd_wait_for_remove(wc
);
163 return (CMD_RETURN_NORMAL
);
166 static enum cmd_retval
167 cmd_wait_for_wait(struct cmdq_item
*item
, const char *name
,
168 struct wait_channel
*wc
)
170 struct client
*c
= cmdq_get_client(item
);
171 struct wait_item
*wi
;
174 cmdq_error(item
, "not able to wait");
175 return (CMD_RETURN_ERROR
);
179 wc
= cmd_wait_for_add(name
);
182 log_debug("wait channel %s already woken (%p)", wc
->name
, c
);
183 cmd_wait_for_remove(wc
);
184 return (CMD_RETURN_NORMAL
);
186 log_debug("wait channel %s not woken (%p)", wc
->name
, c
);
188 wi
= xcalloc(1, sizeof *wi
);
190 TAILQ_INSERT_TAIL(&wc
->waiters
, wi
, entry
);
192 return (CMD_RETURN_WAIT
);
195 static enum cmd_retval
196 cmd_wait_for_lock(struct cmdq_item
*item
, const char *name
,
197 struct wait_channel
*wc
)
199 struct wait_item
*wi
;
201 if (cmdq_get_client(item
) == NULL
) {
202 cmdq_error(item
, "not able to lock");
203 return (CMD_RETURN_ERROR
);
207 wc
= cmd_wait_for_add(name
);
210 wi
= xcalloc(1, sizeof *wi
);
212 TAILQ_INSERT_TAIL(&wc
->lockers
, wi
, entry
);
213 return (CMD_RETURN_WAIT
);
217 return (CMD_RETURN_NORMAL
);
220 static enum cmd_retval
221 cmd_wait_for_unlock(struct cmdq_item
*item
, const char *name
,
222 struct wait_channel
*wc
)
224 struct wait_item
*wi
;
226 if (wc
== NULL
|| !wc
->locked
) {
227 cmdq_error(item
, "channel %s not locked", name
);
228 return (CMD_RETURN_ERROR
);
231 if ((wi
= TAILQ_FIRST(&wc
->lockers
)) != NULL
) {
232 cmdq_continue(wi
->item
);
233 TAILQ_REMOVE(&wc
->lockers
, wi
, entry
);
237 cmd_wait_for_remove(wc
);
240 return (CMD_RETURN_NORMAL
);
244 cmd_wait_for_flush(void)
246 struct wait_channel
*wc
, *wc1
;
247 struct wait_item
*wi
, *wi1
;
249 RB_FOREACH_SAFE(wc
, wait_channels
, &wait_channels
, wc1
) {
250 TAILQ_FOREACH_SAFE(wi
, &wc
->waiters
, entry
, wi1
) {
251 cmdq_continue(wi
->item
);
252 TAILQ_REMOVE(&wc
->waiters
, wi
, entry
);
256 TAILQ_FOREACH_SAFE(wi
, &wc
->lockers
, entry
, wi1
) {
257 cmdq_continue(wi
->item
);
258 TAILQ_REMOVE(&wc
->lockers
, wi
, entry
);
262 cmd_wait_for_remove(wc
);