Annotation of embedaddon/tmux/cmd-wait-for.c, revision 1.1.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>