Annotation of src/usr.bin/tmux/cmd-wait-for.c, Revision 1.4
1.4 ! nicm 1: /* $OpenBSD: cmd-wait-for.c,v 1.3 2013/10/10 12:00:24 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: NULL,
39: cmd_wait_for_exec
40: };
41:
42: struct wait_channel {
43: const char *name;
1.2 nicm 44: int locked;
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:
73: enum cmd_retval
74: cmd_wait_for_exec(struct cmd *self, struct cmd_q *cmdq)
75: {
76: struct args *args = self->args;
77: const char *name = args->argv[0];
78: struct wait_channel *wc, wc0;
79:
80: wc0.name = name;
81: wc = RB_FIND(wait_channels, &wait_channels, &wc0);
82:
83: if (args_has(args, 'S'))
84: return (cmd_wait_for_signal(cmdq, name, wc));
1.2 nicm 85: if (args_has(args, 'L'))
86: return (cmd_wait_for_lock(cmdq, name, wc));
87: if (args_has(args, 'U'))
88: return (cmd_wait_for_unlock(cmdq, name, wc));
1.1 nicm 89: return (cmd_wait_for_wait(cmdq, name, wc));
90: }
91:
92: enum cmd_retval
93: cmd_wait_for_signal(struct cmd_q *cmdq, const char *name,
94: struct wait_channel *wc)
95: {
96: struct cmd_q *wq, *wq1;
97:
98: if (wc == NULL || TAILQ_EMPTY(&wc->waiters)) {
99: cmdq_error(cmdq, "no waiting clients on %s", name);
100: return (CMD_RETURN_ERROR);
101: }
102:
103: TAILQ_FOREACH_SAFE(wq, &wc->waiters, waitentry, wq1) {
104: TAILQ_REMOVE(&wc->waiters, wq, waitentry);
105: if (!cmdq_free(wq))
106: cmdq_continue(wq);
107: }
1.2 nicm 108:
109: if (!wc->locked) {
110: RB_REMOVE(wait_channels, &wait_channels, wc);
111: free((void*) wc->name);
112: free(wc);
113: }
1.1 nicm 114:
115: return (CMD_RETURN_NORMAL);
116: }
117:
118: enum cmd_retval
119: cmd_wait_for_wait(struct cmd_q *cmdq, const char *name,
120: struct wait_channel *wc)
121: {
122: if (cmdq->client == NULL || cmdq->client->session != NULL) {
123: cmdq_error(cmdq, "not able to wait");
124: return (CMD_RETURN_ERROR);
125: }
126:
127: if (wc == NULL) {
128: wc = xmalloc(sizeof *wc);
129: wc->name = xstrdup(name);
1.2 nicm 130: wc->locked = 0;
1.1 nicm 131: TAILQ_INIT(&wc->waiters);
1.2 nicm 132: TAILQ_INIT(&wc->lockers);
1.1 nicm 133: RB_INSERT(wait_channels, &wait_channels, wc);
134: }
1.2 nicm 135:
1.1 nicm 136: TAILQ_INSERT_TAIL(&wc->waiters, cmdq, waitentry);
137: cmdq->references++;
138:
139: return (CMD_RETURN_WAIT);
140: }
1.2 nicm 141:
142: enum cmd_retval
143: cmd_wait_for_lock(struct cmd_q *cmdq, const char *name,
144: struct wait_channel *wc)
145: {
146: if (cmdq->client == NULL || cmdq->client->session != NULL) {
147: cmdq_error(cmdq, "not able to lock");
148: return (CMD_RETURN_ERROR);
149: }
150:
151: if (wc == NULL) {
152: wc = xmalloc(sizeof *wc);
153: wc->name = xstrdup(name);
154: wc->locked = 0;
155: TAILQ_INIT(&wc->waiters);
156: TAILQ_INIT(&wc->lockers);
157: RB_INSERT(wait_channels, &wait_channels, wc);
158: }
159:
160: if (wc->locked) {
161: TAILQ_INSERT_TAIL(&wc->lockers, cmdq, waitentry);
162: cmdq->references++;
163: return (CMD_RETURN_WAIT);
164: }
165: wc->locked = 1;
166:
167: return (CMD_RETURN_NORMAL);
168: }
169:
170: enum cmd_retval
171: cmd_wait_for_unlock(struct cmd_q *cmdq, const char *name,
172: struct wait_channel *wc)
173: {
174: struct cmd_q *wq;
175:
176: if (wc == NULL || !wc->locked) {
177: cmdq_error(cmdq, "channel %s not locked", name);
178: return (CMD_RETURN_ERROR);
179: }
180:
181: if ((wq = TAILQ_FIRST(&wc->lockers)) != NULL) {
182: TAILQ_REMOVE(&wc->lockers, wq, waitentry);
183: if (!cmdq_free(wq))
184: cmdq_continue(wq);
185: } else {
186: wc->locked = 0;
187: if (TAILQ_EMPTY(&wc->waiters)) {
188: RB_REMOVE(wait_channels, &wait_channels, wc);
189: free((void*) wc->name);
190: free(wc);
191: }
192: }
193:
194: return (CMD_RETURN_NORMAL);
195: }
196: