1 /* $OpenBSD$ */ 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 cmd_wait_for_exec 39 }; 40 41 struct wait_channel { 42 const char *name; 43 int locked; 44 int woken; 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 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 struct wait_channel *cmd_wait_for_add(const char *); 74 void cmd_wait_for_remove(struct wait_channel *wc); 75 76 struct wait_channel * 77 cmd_wait_for_add(const char *name) 78 { 79 struct wait_channel *wc; 80 81 wc = xmalloc(sizeof *wc); 82 wc->name = xstrdup(name); 83 84 wc->locked = 0; 85 wc->woken = 0; 86 87 TAILQ_INIT(&wc->waiters); 88 TAILQ_INIT(&wc->lockers); 89 90 RB_INSERT(wait_channels, &wait_channels, wc); 91 92 log_debug("add wait channel %s", wc->name); 93 94 return (wc); 95 } 96 97 void 98 cmd_wait_for_remove(struct wait_channel *wc) 99 { 100 if (wc->locked) 101 return; 102 if (!TAILQ_EMPTY(&wc->waiters) || !wc->woken) 103 return; 104 105 log_debug("remove wait channel %s", wc->name); 106 107 RB_REMOVE(wait_channels, &wait_channels, wc); 108 109 free(__UNCONST(wc->name)); 110 free(wc); 111 } 112 113 enum cmd_retval 114 cmd_wait_for_exec(struct cmd *self, unused struct cmd_q *cmdq) 115 { 116 struct args *args = self->args; 117 const char *name = args->argv[0]; 118 struct wait_channel *wc, wc0; 119 120 wc0.name = name; 121 wc = RB_FIND(wait_channels, &wait_channels, &wc0); 122 123 if (args_has(args, 'S')) 124 return (cmd_wait_for_signal(cmdq, name, wc)); 125 if (args_has(args, 'L')) 126 return (cmd_wait_for_lock(cmdq, name, wc)); 127 if (args_has(args, 'U')) 128 return (cmd_wait_for_unlock(cmdq, name, wc)); 129 return (cmd_wait_for_wait(cmdq, name, wc)); 130 } 131 132 enum cmd_retval 133 cmd_wait_for_signal(unused struct cmd_q *cmdq, const char *name, 134 struct wait_channel *wc) 135 { 136 struct cmd_q *wq, *wq1; 137 138 if (wc == NULL) 139 wc = cmd_wait_for_add(name); 140 141 if (TAILQ_EMPTY(&wc->waiters) && !wc->woken) { 142 log_debug("signal wait channel %s, no waiters", wc->name); 143 wc->woken = 1; 144 return (CMD_RETURN_NORMAL); 145 } 146 log_debug("signal wait channel %s, with waiters", wc->name); 147 148 TAILQ_FOREACH_SAFE(wq, &wc->waiters, waitentry, wq1) { 149 TAILQ_REMOVE(&wc->waiters, wq, waitentry); 150 if (!cmdq_free(wq)) 151 cmdq_continue(wq); 152 } 153 154 cmd_wait_for_remove(wc); 155 return (CMD_RETURN_NORMAL); 156 } 157 158 enum cmd_retval 159 cmd_wait_for_wait(struct cmd_q *cmdq, const char *name, 160 struct wait_channel *wc) 161 { 162 struct client *c = cmdq->client; 163 164 if (c == NULL || c->session != NULL) { 165 cmdq_error(cmdq, "not able to wait"); 166 return (CMD_RETURN_ERROR); 167 } 168 169 if (wc == NULL) 170 wc = cmd_wait_for_add(name); 171 172 if (wc->woken) { 173 log_debug("wait channel %s already woken (client %d)", wc->name, 174 c->tty.fd); 175 cmd_wait_for_remove(wc); 176 return (CMD_RETURN_NORMAL); 177 } 178 log_debug("wait channel %s not woken (client %d)", wc->name, c->tty.fd); 179 180 TAILQ_INSERT_TAIL(&wc->waiters, cmdq, waitentry); 181 cmdq->references++; 182 183 return (CMD_RETURN_WAIT); 184 } 185 186 enum cmd_retval 187 cmd_wait_for_lock(struct cmd_q *cmdq, const char *name, 188 struct wait_channel *wc) 189 { 190 if (cmdq->client == NULL || cmdq->client->session != NULL) { 191 cmdq_error(cmdq, "not able to lock"); 192 return (CMD_RETURN_ERROR); 193 } 194 195 if (wc == NULL) 196 wc = cmd_wait_for_add(name); 197 198 if (wc->locked) { 199 TAILQ_INSERT_TAIL(&wc->lockers, cmdq, waitentry); 200 cmdq->references++; 201 return (CMD_RETURN_WAIT); 202 } 203 wc->locked = 1; 204 205 return (CMD_RETURN_NORMAL); 206 } 207 208 enum cmd_retval 209 cmd_wait_for_unlock(struct cmd_q *cmdq, const char *name, 210 struct wait_channel *wc) 211 { 212 struct cmd_q *wq; 213 214 if (wc == NULL || !wc->locked) { 215 cmdq_error(cmdq, "channel %s not locked", name); 216 return (CMD_RETURN_ERROR); 217 } 218 219 if ((wq = TAILQ_FIRST(&wc->lockers)) != NULL) { 220 TAILQ_REMOVE(&wc->lockers, wq, waitentry); 221 if (!cmdq_free(wq)) 222 cmdq_continue(wq); 223 } else { 224 wc->locked = 0; 225 cmd_wait_for_remove(wc); 226 } 227 228 return (CMD_RETURN_NORMAL); 229 } 230 231 void 232 cmd_wait_for_flush(void) 233 { 234 struct wait_channel *wc, *wc1; 235 struct cmd_q *wq, *wq1; 236 237 RB_FOREACH_SAFE(wc, wait_channels, &wait_channels, wc1) { 238 TAILQ_FOREACH_SAFE(wq, &wc->waiters, waitentry, wq1) { 239 TAILQ_REMOVE(&wc->waiters, wq, waitentry); 240 if (!cmdq_free(wq)) 241 cmdq_continue(wq); 242 } 243 wc->woken = 1; 244 TAILQ_FOREACH_SAFE(wq, &wc->lockers, waitentry, wq1) { 245 TAILQ_REMOVE(&wc->lockers, wq, waitentry); 246 if (!cmdq_free(wq)) 247 cmdq_continue(wq); 248 } 249 wc->locked = 0; 250 cmd_wait_for_remove(wc); 251 } 252 } 253