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