Annotation of src/usr.bin/tmux/cmd-wait-for.c, Revision 1.19
1.19 ! nicm 1: /* $OpenBSD: cmd-wait-for.c,v 1.18 2019/06/18 11:08:42 nicm Exp $ */
1.1 nicm 2:
3: /*
1.13 nicm 4: * Copyright (c) 2013 Nicholas Marriott <nicholas.marriott@gmail.com>
1.1 nicm 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:
1.16 nicm 31: static enum cmd_retval cmd_wait_for_exec(struct cmd *, struct cmdq_item *);
1.1 nicm 32:
33: const struct cmd_entry cmd_wait_for_entry = {
1.12 nicm 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
1.1 nicm 42: };
43:
1.15 nicm 44: struct wait_item {
1.16 nicm 45: struct cmdq_item *item;
1.15 nicm 46: TAILQ_ENTRY(wait_item) entry;
47: };
48:
1.1 nicm 49: struct wait_channel {
50: const char *name;
1.2 nicm 51: int locked;
1.8 nicm 52: int woken;
1.2 nicm 53:
1.15 nicm 54: TAILQ_HEAD(, wait_item) waiters;
55: TAILQ_HEAD(, wait_item) lockers;
1.1 nicm 56:
57: RB_ENTRY(wait_channel) entry;
58: };
59: RB_HEAD(wait_channels, wait_channel);
1.14 nicm 60: static struct wait_channels wait_channels = RB_INITIALIZER(wait_channels);
1.1 nicm 61:
1.14 nicm 62: static int wait_channel_cmp(struct wait_channel *, struct wait_channel *);
63: RB_GENERATE_STATIC(wait_channels, wait_channel, entry, wait_channel_cmp);
1.1 nicm 64:
1.14 nicm 65: static int
1.1 nicm 66: wait_channel_cmp(struct wait_channel *wc1, struct wait_channel *wc2)
67: {
68: return (strcmp(wc1->name, wc2->name));
69: }
70:
1.16 nicm 71: static enum cmd_retval cmd_wait_for_signal(struct cmdq_item *, const char *,
1.14 nicm 72: struct wait_channel *);
1.16 nicm 73: static enum cmd_retval cmd_wait_for_wait(struct cmdq_item *, const char *,
1.14 nicm 74: struct wait_channel *);
1.16 nicm 75: static enum cmd_retval cmd_wait_for_lock(struct cmdq_item *, const char *,
1.14 nicm 76: struct wait_channel *);
1.16 nicm 77: static enum cmd_retval cmd_wait_for_unlock(struct cmdq_item *, const char *,
1.14 nicm 78: struct wait_channel *);
1.1 nicm 79:
1.14 nicm 80: static struct wait_channel *cmd_wait_for_add(const char *);
1.16 nicm 81: static void cmd_wait_for_remove(struct wait_channel *);
1.8 nicm 82:
1.14 nicm 83: static struct wait_channel *
1.8 nicm 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:
1.14 nicm 104: static void
1.8 nicm 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:
1.14 nicm 120: static enum cmd_retval
1.16 nicm 121: cmd_wait_for_exec(struct cmd *self, struct cmdq_item *item)
1.1 nicm 122: {
1.19 ! nicm 123: struct args *args = cmd_get_args(self);
1.1 nicm 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'))
1.16 nicm 131: return (cmd_wait_for_signal(item, name, wc));
1.2 nicm 132: if (args_has(args, 'L'))
1.16 nicm 133: return (cmd_wait_for_lock(item, name, wc));
1.2 nicm 134: if (args_has(args, 'U'))
1.16 nicm 135: return (cmd_wait_for_unlock(item, name, wc));
136: return (cmd_wait_for_wait(item, name, wc));
1.1 nicm 137: }
138:
1.14 nicm 139: static enum cmd_retval
1.16 nicm 140: cmd_wait_for_signal(__unused struct cmdq_item *item, const char *name,
1.1 nicm 141: struct wait_channel *wc)
142: {
1.15 nicm 143: struct wait_item *wi, *wi1;
1.1 nicm 144:
1.8 nicm 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);
1.1 nicm 152: }
1.8 nicm 153: log_debug("signal wait channel %s, with waiters", wc->name);
1.1 nicm 154:
1.15 nicm 155: TAILQ_FOREACH_SAFE(wi, &wc->waiters, entry, wi1) {
1.18 nicm 156: cmdq_continue(wi->item);
1.15 nicm 157:
158: TAILQ_REMOVE(&wc->waiters, wi, entry);
159: free(wi);
1.1 nicm 160: }
1.2 nicm 161:
1.8 nicm 162: cmd_wait_for_remove(wc);
1.1 nicm 163: return (CMD_RETURN_NORMAL);
164: }
165:
1.14 nicm 166: static enum cmd_retval
1.16 nicm 167: cmd_wait_for_wait(struct cmdq_item *item, const char *name,
168: struct wait_channel *wc)
1.1 nicm 169: {
1.16 nicm 170: struct client *c = item->client;
1.15 nicm 171: struct wait_item *wi;
1.8 nicm 172:
1.17 nicm 173: if (c == NULL) {
1.16 nicm 174: cmdq_error(item, "not able to wait");
1.1 nicm 175: return (CMD_RETURN_ERROR);
176: }
177:
1.8 nicm 178: if (wc == NULL)
179: wc = cmd_wait_for_add(name);
180:
181: if (wc->woken) {
1.10 nicm 182: log_debug("wait channel %s already woken (%p)", wc->name, c);
1.8 nicm 183: cmd_wait_for_remove(wc);
184: return (CMD_RETURN_NORMAL);
1.1 nicm 185: }
1.10 nicm 186: log_debug("wait channel %s not woken (%p)", wc->name, c);
1.2 nicm 187:
1.15 nicm 188: wi = xcalloc(1, sizeof *wi);
1.16 nicm 189: wi->item = item;
1.15 nicm 190: TAILQ_INSERT_TAIL(&wc->waiters, wi, entry);
1.1 nicm 191:
192: return (CMD_RETURN_WAIT);
193: }
1.2 nicm 194:
1.14 nicm 195: static enum cmd_retval
1.16 nicm 196: cmd_wait_for_lock(struct cmdq_item *item, const char *name,
197: struct wait_channel *wc)
1.2 nicm 198: {
1.15 nicm 199: struct wait_item *wi;
200:
1.17 nicm 201: if (item->client == NULL) {
1.16 nicm 202: cmdq_error(item, "not able to lock");
1.2 nicm 203: return (CMD_RETURN_ERROR);
204: }
205:
1.8 nicm 206: if (wc == NULL)
207: wc = cmd_wait_for_add(name);
1.2 nicm 208:
209: if (wc->locked) {
1.15 nicm 210: wi = xcalloc(1, sizeof *wi);
1.16 nicm 211: wi->item = item;
1.15 nicm 212: TAILQ_INSERT_TAIL(&wc->lockers, wi, entry);
1.2 nicm 213: return (CMD_RETURN_WAIT);
214: }
215: wc->locked = 1;
216:
217: return (CMD_RETURN_NORMAL);
218: }
219:
1.14 nicm 220: static enum cmd_retval
1.16 nicm 221: cmd_wait_for_unlock(struct cmdq_item *item, const char *name,
1.2 nicm 222: struct wait_channel *wc)
223: {
1.15 nicm 224: struct wait_item *wi;
1.2 nicm 225:
226: if (wc == NULL || !wc->locked) {
1.16 nicm 227: cmdq_error(item, "channel %s not locked", name);
1.2 nicm 228: return (CMD_RETURN_ERROR);
229: }
230:
1.15 nicm 231: if ((wi = TAILQ_FIRST(&wc->lockers)) != NULL) {
1.18 nicm 232: cmdq_continue(wi->item);
1.15 nicm 233: TAILQ_REMOVE(&wc->lockers, wi, entry);
234: free(wi);
1.2 nicm 235: } else {
236: wc->locked = 0;
1.8 nicm 237: cmd_wait_for_remove(wc);
1.2 nicm 238: }
239:
240: return (CMD_RETURN_NORMAL);
241: }
242:
1.6 nicm 243: void
244: cmd_wait_for_flush(void)
245: {
246: struct wait_channel *wc, *wc1;
1.15 nicm 247: struct wait_item *wi, *wi1;
1.6 nicm 248:
249: RB_FOREACH_SAFE(wc, wait_channels, &wait_channels, wc1) {
1.15 nicm 250: TAILQ_FOREACH_SAFE(wi, &wc->waiters, entry, wi1) {
1.18 nicm 251: cmdq_continue(wi->item);
1.15 nicm 252: TAILQ_REMOVE(&wc->waiters, wi, entry);
253: free(wi);
1.6 nicm 254: }
1.9 nicm 255: wc->woken = 1;
1.15 nicm 256: TAILQ_FOREACH_SAFE(wi, &wc->lockers, entry, wi1) {
1.18 nicm 257: cmdq_continue(wi->item);
1.15 nicm 258: TAILQ_REMOVE(&wc->lockers, wi, entry);
259: free(wi);
1.6 nicm 260: }
1.8 nicm 261: wc->locked = 0;
262: cmd_wait_for_remove(wc);
1.6 nicm 263: }
264: }