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