[BACK]Return to cmd-wait-for.c CVS log [TXT][DIR] Up to [local] / src / usr.bin / tmux

Annotation of src/usr.bin/tmux/cmd-wait-for.c, Revision 1.10

1.10    ! nicm        1: /* $OpenBSD: cmd-wait-for.c,v 1.9 2015/09/13 10:45:55 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.8       nicm      114: cmd_wait_for_exec(struct cmd *self, unused 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.8       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: }