[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.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: