Annotation of src/usr.bin/tmux/cmd-wait-for.c, Revision 1.15
1.15 ! nicm 1: /* $OpenBSD: cmd-wait-for.c,v 1.14 2016/10/10 21:51:39 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.14 nicm 31: static enum cmd_retval cmd_wait_for_exec(struct cmd *, struct cmd_q *);
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 {
! 45: struct cmd_q *cmdq;
! 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.14 nicm 71: static enum cmd_retval cmd_wait_for_signal(struct cmd_q *, const char *,
72: struct wait_channel *);
73: static enum cmd_retval cmd_wait_for_wait(struct cmd_q *, const char *,
74: struct wait_channel *);
75: static enum cmd_retval cmd_wait_for_lock(struct cmd_q *, const char *,
76: struct wait_channel *);
77: static enum cmd_retval cmd_wait_for_unlock(struct cmd_q *, const char *,
78: struct wait_channel *);
1.1 nicm 79:
1.14 nicm 80: static struct wait_channel *cmd_wait_for_add(const char *);
81: static void cmd_wait_for_remove(struct wait_channel *wc);
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.11 nicm 121: cmd_wait_for_exec(struct cmd *self, struct cmd_q *cmdq)
1.1 nicm 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(cmdq, name, wc));
1.2 nicm 132: if (args_has(args, 'L'))
133: return (cmd_wait_for_lock(cmdq, name, wc));
134: if (args_has(args, 'U'))
135: return (cmd_wait_for_unlock(cmdq, name, wc));
1.1 nicm 136: return (cmd_wait_for_wait(cmdq, name, wc));
137: }
138:
1.14 nicm 139: static enum cmd_retval
1.11 nicm 140: cmd_wait_for_signal(__unused struct cmd_q *cmdq, 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) {
! 156: wi->cmdq->flags &= ~CMD_Q_WAITING;
! 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.15 ! nicm 167: cmd_wait_for_wait(struct cmd_q *cmdq, const char *name, struct wait_channel *wc)
1.1 nicm 168: {
1.15 ! nicm 169: struct client *c = cmdq->client;
! 170: struct wait_item *wi;
1.8 nicm 171:
172: if (c == NULL || c->session != NULL) {
1.1 nicm 173: cmdq_error(cmdq, "not able to wait");
174: return (CMD_RETURN_ERROR);
175: }
176:
1.8 nicm 177: if (wc == NULL)
178: wc = cmd_wait_for_add(name);
179:
180: if (wc->woken) {
1.10 nicm 181: log_debug("wait channel %s already woken (%p)", wc->name, c);
1.8 nicm 182: cmd_wait_for_remove(wc);
183: return (CMD_RETURN_NORMAL);
1.1 nicm 184: }
1.10 nicm 185: log_debug("wait channel %s not woken (%p)", wc->name, c);
1.2 nicm 186:
1.15 ! nicm 187: wi = xcalloc(1, sizeof *wi);
! 188: wi->cmdq = cmdq;
! 189: TAILQ_INSERT_TAIL(&wc->waiters, wi, entry);
1.1 nicm 190:
191: return (CMD_RETURN_WAIT);
192: }
1.2 nicm 193:
1.14 nicm 194: static enum cmd_retval
1.15 ! nicm 195: cmd_wait_for_lock(struct cmd_q *cmdq, const char *name, struct wait_channel *wc)
1.2 nicm 196: {
1.15 ! nicm 197: struct wait_item *wi;
! 198:
1.2 nicm 199: if (cmdq->client == NULL || cmdq->client->session != NULL) {
200: cmdq_error(cmdq, "not able to lock");
201: return (CMD_RETURN_ERROR);
202: }
203:
1.8 nicm 204: if (wc == NULL)
205: wc = cmd_wait_for_add(name);
1.2 nicm 206:
207: if (wc->locked) {
1.15 ! nicm 208: wi = xcalloc(1, sizeof *wi);
! 209: wi->cmdq = cmdq;
! 210: TAILQ_INSERT_TAIL(&wc->lockers, wi, entry);
1.2 nicm 211: return (CMD_RETURN_WAIT);
212: }
213: wc->locked = 1;
214:
215: return (CMD_RETURN_NORMAL);
216: }
217:
1.14 nicm 218: static enum cmd_retval
1.2 nicm 219: cmd_wait_for_unlock(struct cmd_q *cmdq, const char *name,
220: struct wait_channel *wc)
221: {
1.15 ! nicm 222: struct wait_item *wi;
1.2 nicm 223:
224: if (wc == NULL || !wc->locked) {
225: cmdq_error(cmdq, "channel %s not locked", name);
226: return (CMD_RETURN_ERROR);
227: }
228:
1.15 ! nicm 229: if ((wi = TAILQ_FIRST(&wc->lockers)) != NULL) {
! 230: wi->cmdq->flags &= ~CMD_Q_WAITING;
! 231: TAILQ_REMOVE(&wc->lockers, wi, entry);
! 232: free(wi);
1.2 nicm 233: } else {
234: wc->locked = 0;
1.8 nicm 235: cmd_wait_for_remove(wc);
1.2 nicm 236: }
237:
238: return (CMD_RETURN_NORMAL);
239: }
240:
1.6 nicm 241: void
242: cmd_wait_for_flush(void)
243: {
244: struct wait_channel *wc, *wc1;
1.15 ! nicm 245: struct wait_item *wi, *wi1;
1.6 nicm 246:
247: RB_FOREACH_SAFE(wc, wait_channels, &wait_channels, wc1) {
1.15 ! nicm 248: TAILQ_FOREACH_SAFE(wi, &wc->waiters, entry, wi1) {
! 249: wi->cmdq->flags &= ~CMD_Q_WAITING;
! 250: TAILQ_REMOVE(&wc->waiters, wi, entry);
! 251: free(wi);
1.6 nicm 252: }
1.9 nicm 253: wc->woken = 1;
1.15 ! nicm 254: TAILQ_FOREACH_SAFE(wi, &wc->lockers, entry, wi1) {
! 255: wi->cmdq->flags &= ~CMD_Q_WAITING;
! 256: TAILQ_REMOVE(&wc->lockers, wi, entry);
! 257: free(wi);
1.6 nicm 258: }
1.8 nicm 259: wc->locked = 0;
260: cmd_wait_for_remove(wc);
1.6 nicm 261: }
262: }