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 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 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 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 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 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 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