File: handle-wait.c

package info (click to toggle)
putty 0.83-3
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 13,216 kB
  • sloc: ansic: 148,476; python: 8,466; perl: 1,830; makefile: 128; sh: 117
file content (143 lines) | stat: -rw-r--r-- 4,266 bytes parent folder | download | duplicates (2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
/*
 * handle-wait.c: Manage a collection of HANDLEs to wait for (in a
 * WaitFor{Single,Multiple}Objects sense), each with a callback to be
 * called when it's activated. Tracks the list, and provides an API to
 * event loops that let them get a list of things to wait for and a
 * way to call back to here when one of them does something.
 */

/*
 * TODO: currently this system can't cope with more than
 * MAXIMUM_WAIT_OBJECTS (= 64) handles at a time. It enforces that by
 * assertion, so we'll at least find out if that assumption is ever
 * violated.
 *
 * It should be OK for the moment. As of 2021-05-24, the only uses of
 * this system are by the ConPTY backend (just once, to watch for its
 * subprocess terminating); by Pageant (for the event that the
 * WM_COPYDATA subthread uses to signal the main thread); and by
 * named-pipe-server.c (once per named-pipe server, of which there is
 * one in Pageant and one in connection-sharing upstreams). So the
 * total number of handles has a pretty small upper bound.
 *
 * But sooner or later, I'm sure we'll find a reason why we really
 * need to watch a squillion handles at once. When that happens, I
 * can't see any alternative to setting up some kind of tree of
 * subthreads in this module, each one condensing 64 of our handles
 * into one, by doing its own WaitForMultipleObjects and setting an
 * event object to indicate that one of them did something. It'll be
 * horribly ugly.
 */

#include "putty.h"

struct HandleWait {
    HANDLE handle;
    handle_wait_callback_fn_t callback;
    void *callback_ctx;

    int index;                    /* sort key for tree234 */
};

struct HandleWaitListInner {
    HandleWait *hws[MAXIMUM_WAIT_OBJECTS];
    HANDLE handles[MAXIMUM_WAIT_OBJECTS];

    struct HandleWaitList hwl;
};

static int handlewait_cmp(void *av, void *bv)
{
    HandleWait *a = (HandleWait *)av, *b = (HandleWait *)bv;
    if (a->index < b->index)
        return -1;
    if (a->index > b->index)
        return +1;
    return 0;
}

static tree234 *handlewaits_tree_real;

static inline tree234 *ensure_handlewaits_tree_exists(void)
{
    if (!handlewaits_tree_real)
        handlewaits_tree_real = newtree234(handlewait_cmp);
    return handlewaits_tree_real;
}

static int allocate_index(void)
{
    tree234 *t = ensure_handlewaits_tree_exists();
    search234_state st[1];

    search234_start(st, t);
    while (st->element) {
        HandleWait *hw = (HandleWait *)st->element;
        if (st->index < hw->index) {
            /* There are unused index slots to the left of this element */
            search234_step(st, -1);
        } else {
            assert(st->index == hw->index);
            search234_step(st, +1);
        }
    }

    return st->index;
}

HandleWait *add_handle_wait(HANDLE h, handle_wait_callback_fn_t callback,
                            void *callback_ctx)
{
    HandleWait *hw = snew(HandleWait);
    hw->handle = h;
    hw->callback = callback;
    hw->callback_ctx = callback_ctx;

    tree234 *t = ensure_handlewaits_tree_exists();
    hw->index = allocate_index();
    HandleWait *added = add234(t, hw);
    assert(added == hw);

    return hw;
}

void delete_handle_wait(HandleWait *hw)
{
    tree234 *t = ensure_handlewaits_tree_exists();
    HandleWait *deleted = del234(t, hw);
    assert(deleted == hw);
    sfree(hw);
}

HandleWaitList *get_handle_wait_list(void)
{
    tree234 *t = ensure_handlewaits_tree_exists();
    struct HandleWaitListInner *hwli = snew(struct HandleWaitListInner);
    size_t n = 0;
    HandleWait *hw;
    for (int i = 0; (hw = index234(t, i)) != NULL; i++) {
        assert(n < MAXIMUM_WAIT_OBJECTS);
        hwli->hws[n] = hw;
        hwli->hwl.handles[n] = hw->handle;
        n++;
    }
    hwli->hwl.nhandles = n;
    return &hwli->hwl;
}

void handle_wait_activate(HandleWaitList *hwl, int index)
{
    struct HandleWaitListInner *hwli =
        container_of(hwl, struct HandleWaitListInner, hwl);
    assert(0 <= index);
    assert(index < hwli->hwl.nhandles);
    HandleWait *hw = hwli->hws[index];
    hw->callback(hw->callback_ctx);
}

void handle_wait_list_free(HandleWaitList *hwl)
{
    struct HandleWaitListInner *hwli =
        container_of(hwl, struct HandleWaitListInner, hwl);
    sfree(hwli);
}