1 /* $OpenBSD: cmd-wait-for.c,v 1.7 2014/10/20 22:29:25 nicm Exp $ */ 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 45 TAILQ_HEAD(, cmd_q) waiters; 46 TAILQ_HEAD(, cmd_q) lockers; 47 48 RB_ENTRY(wait_channel) entry; 49 }; 50 RB_HEAD(wait_channels, wait_channel); 51 struct wait_channels wait_channels = RB_INITIALIZER(wait_channels); 52 53 int wait_channel_cmp(struct wait_channel *, struct wait_channel *); 54 RB_PROTOTYPE(wait_channels, wait_channel, entry, wait_channel_cmp); 55 RB_GENERATE(wait_channels, wait_channel, entry, wait_channel_cmp); 56 57 int 58 wait_channel_cmp(struct wait_channel *wc1, struct wait_channel *wc2) 59 { 60 return (strcmp(wc1->name, wc2->name)); 61 } 62 63 enum cmd_retval cmd_wait_for_signal(struct cmd_q *, const char *, 64 struct wait_channel *); 65 enum cmd_retval cmd_wait_for_wait(struct cmd_q *, const char *, 66 struct wait_channel *); 67 enum cmd_retval cmd_wait_for_lock(struct cmd_q *, const char *, 68 struct wait_channel *); 69 enum cmd_retval cmd_wait_for_unlock(struct cmd_q *, const char *, 70 struct wait_channel *); 71 72 enum cmd_retval 73 cmd_wait_for_exec(struct cmd *self, struct cmd_q *cmdq) 74 { 75 struct args *args = self->args; 76 const char *name = args->argv[0]; 77 struct wait_channel *wc, wc0; 78 79 wc0.name = name; 80 wc = RB_FIND(wait_channels, &wait_channels, &wc0); 81 82 if (args_has(args, 'S')) 83 return (cmd_wait_for_signal(cmdq, name, wc)); 84 if (args_has(args, 'L')) 85 return (cmd_wait_for_lock(cmdq, name, wc)); 86 if (args_has(args, 'U')) 87 return (cmd_wait_for_unlock(cmdq, name, wc)); 88 return (cmd_wait_for_wait(cmdq, name, wc)); 89 } 90 91 enum cmd_retval 92 cmd_wait_for_signal(struct cmd_q *cmdq, const char *name, 93 struct wait_channel *wc) 94 { 95 struct cmd_q *wq, *wq1; 96 97 if (wc == NULL || TAILQ_EMPTY(&wc->waiters)) { 98 cmdq_error(cmdq, "no waiting clients on %s", name); 99 return (CMD_RETURN_ERROR); 100 } 101 102 TAILQ_FOREACH_SAFE(wq, &wc->waiters, waitentry, wq1) { 103 TAILQ_REMOVE(&wc->waiters, wq, waitentry); 104 if (!cmdq_free(wq)) 105 cmdq_continue(wq); 106 } 107 108 if (!wc->locked) { 109 RB_REMOVE(wait_channels, &wait_channels, wc); 110 free((void *)wc->name); 111 free(wc); 112 } 113 114 return (CMD_RETURN_NORMAL); 115 } 116 117 enum cmd_retval 118 cmd_wait_for_wait(struct cmd_q *cmdq, const char *name, 119 struct wait_channel *wc) 120 { 121 if (cmdq->client == NULL || cmdq->client->session != NULL) { 122 cmdq_error(cmdq, "not able to wait"); 123 return (CMD_RETURN_ERROR); 124 } 125 126 if (wc == NULL) { 127 wc = xmalloc(sizeof *wc); 128 wc->name = xstrdup(name); 129 wc->locked = 0; 130 TAILQ_INIT(&wc->waiters); 131 TAILQ_INIT(&wc->lockers); 132 RB_INSERT(wait_channels, &wait_channels, wc); 133 } 134 135 TAILQ_INSERT_TAIL(&wc->waiters, cmdq, waitentry); 136 cmdq->references++; 137 138 return (CMD_RETURN_WAIT); 139 } 140 141 enum cmd_retval 142 cmd_wait_for_lock(struct cmd_q *cmdq, const char *name, 143 struct wait_channel *wc) 144 { 145 if (cmdq->client == NULL || cmdq->client->session != NULL) { 146 cmdq_error(cmdq, "not able to lock"); 147 return (CMD_RETURN_ERROR); 148 } 149 150 if (wc == NULL) { 151 wc = xmalloc(sizeof *wc); 152 wc->name = xstrdup(name); 153 wc->locked = 0; 154 TAILQ_INIT(&wc->waiters); 155 TAILQ_INIT(&wc->lockers); 156 RB_INSERT(wait_channels, &wait_channels, wc); 157 } 158 159 if (wc->locked) { 160 TAILQ_INSERT_TAIL(&wc->lockers, cmdq, waitentry); 161 cmdq->references++; 162 return (CMD_RETURN_WAIT); 163 } 164 wc->locked = 1; 165 166 return (CMD_RETURN_NORMAL); 167 } 168 169 enum cmd_retval 170 cmd_wait_for_unlock(struct cmd_q *cmdq, const char *name, 171 struct wait_channel *wc) 172 { 173 struct cmd_q *wq; 174 175 if (wc == NULL || !wc->locked) { 176 cmdq_error(cmdq, "channel %s not locked", name); 177 return (CMD_RETURN_ERROR); 178 } 179 180 if ((wq = TAILQ_FIRST(&wc->lockers)) != NULL) { 181 TAILQ_REMOVE(&wc->lockers, wq, waitentry); 182 if (!cmdq_free(wq)) 183 cmdq_continue(wq); 184 } else { 185 wc->locked = 0; 186 if (TAILQ_EMPTY(&wc->waiters)) { 187 RB_REMOVE(wait_channels, &wait_channels, wc); 188 free((void *)wc->name); 189 free(wc); 190 } 191 } 192 193 return (CMD_RETURN_NORMAL); 194 } 195 196 void 197 cmd_wait_for_flush(void) 198 { 199 struct wait_channel *wc, *wc1; 200 struct cmd_q *wq, *wq1; 201 202 RB_FOREACH_SAFE(wc, wait_channels, &wait_channels, wc1) { 203 TAILQ_FOREACH_SAFE(wq, &wc->waiters, waitentry, wq1) { 204 TAILQ_REMOVE(&wc->waiters, wq, waitentry); 205 if (!cmdq_free(wq)) 206 cmdq_continue(wq); 207 } 208 while ((wq = TAILQ_FIRST(&wc->lockers)) != NULL) { 209 TAILQ_REMOVE(&wc->lockers, wq, waitentry); 210 if (!cmdq_free(wq)) 211 cmdq_continue(wq); 212 } 213 RB_REMOVE(wait_channels, &wait_channels, wc); 214 free((void *)wc->name); 215 free(wc); 216 } 217 } 218