1 /* $OpenBSD: cmd-wait-for.c,v 1.13 2016/01/19 15:59:12 nicm Exp $ */ 2 3 /* 4 * Copyright (c) 2013 Nicholas Marriott <nicholas.marriott@gmail.com> 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 .name = "wait-for", 35 .alias = "wait", 36 37 .args = { "LSU", 1, 1 }, 38 .usage = "[-L|-S|-U] channel", 39 40 .flags = 0, 41 .exec = cmd_wait_for_exec 42 }; 43 44 struct wait_channel { 45 const char *name; 46 int locked; 47 int woken; 48 49 TAILQ_HEAD(, cmd_q) waiters; 50 TAILQ_HEAD(, cmd_q) lockers; 51 52 RB_ENTRY(wait_channel) entry; 53 }; 54 RB_HEAD(wait_channels, wait_channel); 55 struct wait_channels wait_channels = RB_INITIALIZER(wait_channels); 56 57 int wait_channel_cmp(struct wait_channel *, struct wait_channel *); 58 RB_PROTOTYPE(wait_channels, wait_channel, entry, wait_channel_cmp); 59 RB_GENERATE(wait_channels, wait_channel, entry, wait_channel_cmp); 60 61 int 62 wait_channel_cmp(struct wait_channel *wc1, struct wait_channel *wc2) 63 { 64 return (strcmp(wc1->name, wc2->name)); 65 } 66 67 enum cmd_retval cmd_wait_for_signal(struct cmd_q *, const char *, 68 struct wait_channel *); 69 enum cmd_retval cmd_wait_for_wait(struct cmd_q *, const char *, 70 struct wait_channel *); 71 enum cmd_retval cmd_wait_for_lock(struct cmd_q *, const char *, 72 struct wait_channel *); 73 enum cmd_retval cmd_wait_for_unlock(struct cmd_q *, const char *, 74 struct wait_channel *); 75 76 struct wait_channel *cmd_wait_for_add(const char *); 77 void cmd_wait_for_remove(struct wait_channel *wc); 78 79 struct wait_channel * 80 cmd_wait_for_add(const char *name) 81 { 82 struct wait_channel *wc; 83 84 wc = xmalloc(sizeof *wc); 85 wc->name = xstrdup(name); 86 87 wc->locked = 0; 88 wc->woken = 0; 89 90 TAILQ_INIT(&wc->waiters); 91 TAILQ_INIT(&wc->lockers); 92 93 RB_INSERT(wait_channels, &wait_channels, wc); 94 95 log_debug("add wait channel %s", wc->name); 96 97 return (wc); 98 } 99 100 void 101 cmd_wait_for_remove(struct wait_channel *wc) 102 { 103 if (wc->locked) 104 return; 105 if (!TAILQ_EMPTY(&wc->waiters) || !wc->woken) 106 return; 107 108 log_debug("remove wait channel %s", wc->name); 109 110 RB_REMOVE(wait_channels, &wait_channels, wc); 111 112 free((void *)wc->name); 113 free(wc); 114 } 115 116 enum cmd_retval 117 cmd_wait_for_exec(struct cmd *self, struct cmd_q *cmdq) 118 { 119 struct args *args = self->args; 120 const char *name = args->argv[0]; 121 struct wait_channel *wc, wc0; 122 123 wc0.name = name; 124 wc = RB_FIND(wait_channels, &wait_channels, &wc0); 125 126 if (args_has(args, 'S')) 127 return (cmd_wait_for_signal(cmdq, name, wc)); 128 if (args_has(args, 'L')) 129 return (cmd_wait_for_lock(cmdq, name, wc)); 130 if (args_has(args, 'U')) 131 return (cmd_wait_for_unlock(cmdq, name, wc)); 132 return (cmd_wait_for_wait(cmdq, name, wc)); 133 } 134 135 enum cmd_retval 136 cmd_wait_for_signal(__unused struct cmd_q *cmdq, const char *name, 137 struct wait_channel *wc) 138 { 139 struct cmd_q *wq, *wq1; 140 141 if (wc == NULL) 142 wc = cmd_wait_for_add(name); 143 144 if (TAILQ_EMPTY(&wc->waiters) && !wc->woken) { 145 log_debug("signal wait channel %s, no waiters", wc->name); 146 wc->woken = 1; 147 return (CMD_RETURN_NORMAL); 148 } 149 log_debug("signal wait channel %s, with waiters", wc->name); 150 151 TAILQ_FOREACH_SAFE(wq, &wc->waiters, waitentry, wq1) { 152 TAILQ_REMOVE(&wc->waiters, wq, waitentry); 153 if (!cmdq_free(wq)) 154 cmdq_continue(wq); 155 } 156 157 cmd_wait_for_remove(wc); 158 return (CMD_RETURN_NORMAL); 159 } 160 161 enum cmd_retval 162 cmd_wait_for_wait(struct cmd_q *cmdq, const char *name, 163 struct wait_channel *wc) 164 { 165 struct client *c = cmdq->client; 166 167 if (c == NULL || c->session != NULL) { 168 cmdq_error(cmdq, "not able to wait"); 169 return (CMD_RETURN_ERROR); 170 } 171 172 if (wc == NULL) 173 wc = cmd_wait_for_add(name); 174 175 if (wc->woken) { 176 log_debug("wait channel %s already woken (%p)", wc->name, c); 177 cmd_wait_for_remove(wc); 178 return (CMD_RETURN_NORMAL); 179 } 180 log_debug("wait channel %s not woken (%p)", wc->name, c); 181 182 TAILQ_INSERT_TAIL(&wc->waiters, cmdq, waitentry); 183 cmdq->references++; 184 185 return (CMD_RETURN_WAIT); 186 } 187 188 enum cmd_retval 189 cmd_wait_for_lock(struct cmd_q *cmdq, const char *name, 190 struct wait_channel *wc) 191 { 192 if (cmdq->client == NULL || cmdq->client->session != NULL) { 193 cmdq_error(cmdq, "not able to lock"); 194 return (CMD_RETURN_ERROR); 195 } 196 197 if (wc == NULL) 198 wc = cmd_wait_for_add(name); 199 200 if (wc->locked) { 201 TAILQ_INSERT_TAIL(&wc->lockers, cmdq, waitentry); 202 cmdq->references++; 203 return (CMD_RETURN_WAIT); 204 } 205 wc->locked = 1; 206 207 return (CMD_RETURN_NORMAL); 208 } 209 210 enum cmd_retval 211 cmd_wait_for_unlock(struct cmd_q *cmdq, const char *name, 212 struct wait_channel *wc) 213 { 214 struct cmd_q *wq; 215 216 if (wc == NULL || !wc->locked) { 217 cmdq_error(cmdq, "channel %s not locked", name); 218 return (CMD_RETURN_ERROR); 219 } 220 221 if ((wq = TAILQ_FIRST(&wc->lockers)) != NULL) { 222 TAILQ_REMOVE(&wc->lockers, wq, waitentry); 223 if (!cmdq_free(wq)) 224 cmdq_continue(wq); 225 } else { 226 wc->locked = 0; 227 cmd_wait_for_remove(wc); 228 } 229 230 return (CMD_RETURN_NORMAL); 231 } 232 233 void 234 cmd_wait_for_flush(void) 235 { 236 struct wait_channel *wc, *wc1; 237 struct cmd_q *wq, *wq1; 238 239 RB_FOREACH_SAFE(wc, wait_channels, &wait_channels, wc1) { 240 TAILQ_FOREACH_SAFE(wq, &wc->waiters, waitentry, wq1) { 241 TAILQ_REMOVE(&wc->waiters, wq, waitentry); 242 if (!cmdq_free(wq)) 243 cmdq_continue(wq); 244 } 245 wc->woken = 1; 246 TAILQ_FOREACH_SAFE(wq, &wc->lockers, waitentry, wq1) { 247 TAILQ_REMOVE(&wc->lockers, wq, waitentry); 248 if (!cmdq_free(wq)) 249 cmdq_continue(wq); 250 } 251 wc->locked = 0; 252 cmd_wait_for_remove(wc); 253 } 254 } 255