Annotation of src/usr.bin/tmux/cmd-wait-for.c, Revision 1.11
1.11 ! nicm 1: /* $OpenBSD: cmd-wait-for.c,v 1.10 2015/10/20 21:12:08 nicm Exp $ */
1.1 nicm 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",
1.2 nicm 35: "LSU", 1, 1,
1.4 nicm 36: "[-L|-S|-U] channel",
1.1 nicm 37: 0,
38: cmd_wait_for_exec
39: };
40:
41: struct wait_channel {
42: const char *name;
1.2 nicm 43: int locked;
1.8 nicm 44: int woken;
1.2 nicm 45:
1.1 nicm 46: TAILQ_HEAD(, cmd_q) waiters;
1.2 nicm 47: TAILQ_HEAD(, cmd_q) lockers;
1.1 nicm 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 *);
1.2 nicm 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 *);
1.1 nicm 72:
1.8 nicm 73: struct wait_channel *cmd_wait_for_add(const char *);
74: void cmd_wait_for_remove(struct wait_channel *wc);
75:
76: struct wait_channel *
77: cmd_wait_for_add(const char *name)
78: {
79: struct wait_channel *wc;
80:
81: wc = xmalloc(sizeof *wc);
82: wc->name = xstrdup(name);
83:
84: wc->locked = 0;
85: wc->woken = 0;
86:
87: TAILQ_INIT(&wc->waiters);
88: TAILQ_INIT(&wc->lockers);
89:
90: RB_INSERT(wait_channels, &wait_channels, wc);
91:
92: log_debug("add wait channel %s", wc->name);
93:
94: return (wc);
95: }
96:
97: void
98: cmd_wait_for_remove(struct wait_channel *wc)
99: {
100: if (wc->locked)
101: return;
102: if (!TAILQ_EMPTY(&wc->waiters) || !wc->woken)
103: return;
104:
105: log_debug("remove wait channel %s", wc->name);
106:
107: RB_REMOVE(wait_channels, &wait_channels, wc);
108:
109: free((void *)wc->name);
110: free(wc);
111: }
112:
1.1 nicm 113: enum cmd_retval
1.11 ! nicm 114: cmd_wait_for_exec(struct cmd *self, struct cmd_q *cmdq)
1.1 nicm 115: {
116: struct args *args = self->args;
117: const char *name = args->argv[0];
118: struct wait_channel *wc, wc0;
119:
120: wc0.name = name;
121: wc = RB_FIND(wait_channels, &wait_channels, &wc0);
122:
123: if (args_has(args, 'S'))
124: return (cmd_wait_for_signal(cmdq, name, wc));
1.2 nicm 125: if (args_has(args, 'L'))
126: return (cmd_wait_for_lock(cmdq, name, wc));
127: if (args_has(args, 'U'))
128: return (cmd_wait_for_unlock(cmdq, name, wc));
1.1 nicm 129: return (cmd_wait_for_wait(cmdq, name, wc));
130: }
131:
132: enum cmd_retval
1.11 ! nicm 133: cmd_wait_for_signal(__unused struct cmd_q *cmdq, const char *name,
1.1 nicm 134: struct wait_channel *wc)
135: {
136: struct cmd_q *wq, *wq1;
137:
1.8 nicm 138: if (wc == NULL)
139: wc = cmd_wait_for_add(name);
140:
141: if (TAILQ_EMPTY(&wc->waiters) && !wc->woken) {
142: log_debug("signal wait channel %s, no waiters", wc->name);
143: wc->woken = 1;
144: return (CMD_RETURN_NORMAL);
1.1 nicm 145: }
1.8 nicm 146: log_debug("signal wait channel %s, with waiters", wc->name);
1.1 nicm 147:
148: TAILQ_FOREACH_SAFE(wq, &wc->waiters, waitentry, wq1) {
149: TAILQ_REMOVE(&wc->waiters, wq, waitentry);
150: if (!cmdq_free(wq))
151: cmdq_continue(wq);
152: }
1.2 nicm 153:
1.8 nicm 154: cmd_wait_for_remove(wc);
1.1 nicm 155: return (CMD_RETURN_NORMAL);
156: }
157:
158: enum cmd_retval
159: cmd_wait_for_wait(struct cmd_q *cmdq, const char *name,
160: struct wait_channel *wc)
161: {
1.8 nicm 162: struct client *c = cmdq->client;
163:
164: if (c == NULL || c->session != NULL) {
1.1 nicm 165: cmdq_error(cmdq, "not able to wait");
166: return (CMD_RETURN_ERROR);
167: }
168:
1.8 nicm 169: if (wc == NULL)
170: wc = cmd_wait_for_add(name);
171:
172: if (wc->woken) {
1.10 nicm 173: log_debug("wait channel %s already woken (%p)", wc->name, c);
1.8 nicm 174: cmd_wait_for_remove(wc);
175: return (CMD_RETURN_NORMAL);
1.1 nicm 176: }
1.10 nicm 177: log_debug("wait channel %s not woken (%p)", wc->name, c);
1.2 nicm 178:
1.1 nicm 179: TAILQ_INSERT_TAIL(&wc->waiters, cmdq, waitentry);
180: cmdq->references++;
181:
182: return (CMD_RETURN_WAIT);
183: }
1.2 nicm 184:
185: enum cmd_retval
186: cmd_wait_for_lock(struct cmd_q *cmdq, const char *name,
187: struct wait_channel *wc)
188: {
189: if (cmdq->client == NULL || cmdq->client->session != NULL) {
190: cmdq_error(cmdq, "not able to lock");
191: return (CMD_RETURN_ERROR);
192: }
193:
1.8 nicm 194: if (wc == NULL)
195: wc = cmd_wait_for_add(name);
1.2 nicm 196:
197: if (wc->locked) {
198: TAILQ_INSERT_TAIL(&wc->lockers, cmdq, waitentry);
199: cmdq->references++;
200: return (CMD_RETURN_WAIT);
201: }
202: wc->locked = 1;
203:
204: return (CMD_RETURN_NORMAL);
205: }
206:
207: enum cmd_retval
208: cmd_wait_for_unlock(struct cmd_q *cmdq, const char *name,
209: struct wait_channel *wc)
210: {
211: struct cmd_q *wq;
212:
213: if (wc == NULL || !wc->locked) {
214: cmdq_error(cmdq, "channel %s not locked", name);
215: return (CMD_RETURN_ERROR);
216: }
217:
218: if ((wq = TAILQ_FIRST(&wc->lockers)) != NULL) {
219: TAILQ_REMOVE(&wc->lockers, wq, waitentry);
220: if (!cmdq_free(wq))
221: cmdq_continue(wq);
222: } else {
223: wc->locked = 0;
1.8 nicm 224: cmd_wait_for_remove(wc);
1.2 nicm 225: }
226:
227: return (CMD_RETURN_NORMAL);
228: }
229:
1.6 nicm 230: void
231: cmd_wait_for_flush(void)
232: {
233: struct wait_channel *wc, *wc1;
234: struct cmd_q *wq, *wq1;
235:
236: RB_FOREACH_SAFE(wc, wait_channels, &wait_channels, wc1) {
237: TAILQ_FOREACH_SAFE(wq, &wc->waiters, waitentry, wq1) {
238: TAILQ_REMOVE(&wc->waiters, wq, waitentry);
239: if (!cmdq_free(wq))
240: cmdq_continue(wq);
241: }
1.9 nicm 242: wc->woken = 1;
243: TAILQ_FOREACH_SAFE(wq, &wc->lockers, waitentry, wq1) {
1.6 nicm 244: TAILQ_REMOVE(&wc->lockers, wq, waitentry);
245: if (!cmdq_free(wq))
246: cmdq_continue(wq);
247: }
1.8 nicm 248: wc->locked = 0;
249: cmd_wait_for_remove(wc);
1.6 nicm 250: }
251: }