summaryrefslogtreecommitdiff
path: root/src/fdevent_fdnode.c
blob: f498c57b0e5af34c70f55173b0fc4a2d9790170f (plain)
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
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
#include "first.h"

#include "fdevent_impl.h"
#include "fdevent.h"
#include "buffer.h"
#include "log.h"

#include <stdlib.h>
#include <errno.h>

__attribute_malloc__
__attribute_returns_nonnull__
static fdnode *
fdnode_init (void)
{
    return ck_calloc(1, sizeof(fdnode));
}

static void
fdnode_free (fdnode *fdn)
{
    free(fdn);
}

fdnode *
fdevent_register (fdevents *ev, int fd, fdevent_handler handler, void *ctx)
{
  #ifdef _WIN32
    fdnode *fdn  = fdnode_init();
    ev->fdarray[(fdn->fda_ndx = ev->count++)] = fdn;
  #else
    fdnode *fdn  = ev->fdarray[fd] = fdnode_init();
  #endif
    fdn->handler = handler;
    fdn->fd      = fd;
    fdn->ctx     = ctx;
    fdn->events  = 0;
    fdn->fde_ndx = -1;
    return fdn;
}

#ifdef _WIN32
#define fdevent_fdarray_slot(ev,fdn) &(ev)->fdarray[(fdn)->fda_ndx]
#else
#define fdevent_fdarray_slot(ev,fdn) &(ev)->fdarray[(fdn)->fd]
#endif

void
fdevent_unregister (fdevents *ev, fdnode *fdn)
{
    fdnode **fdn_slot = fdevent_fdarray_slot(ev, fdn);
    if ((uintptr_t)*fdn_slot & 0x3) return; /*(should not happen)*/
  #ifdef _WIN32
    if (--ev->count != fdn->fda_ndx) {
        /* compact fdarray; move element in last slot */
        fdnode **fdn_last = &ev->fdarray[ev->count];
        *fdn_slot = *fdn_last;
        ((fdnode *)((uintptr_t)*fdn_slot & ~0x3))->fda_ndx = fdn->fda_ndx;
        fdn_slot = fdn_last;
    }
  #endif
    *fdn_slot = NULL;
    fdnode_free(fdn);
}

void
fdevent_sched_close (fdevents *ev, fdnode *fdn)
{
    fdnode **fdn_slot = fdevent_fdarray_slot(ev, fdn);
    if ((uintptr_t)*fdn_slot & 0x3) return;
    *fdn_slot = (fdnode *)((uintptr_t)fdn | 0x3);
    fdn->handler = (fdevent_handler)NULL;
    fdn->ctx = ev->pendclose;
    ev->pendclose = fdn;
}

__attribute_cold__
__attribute_noinline__
static int
fdevent_fdnode_event_unsetter_retry (fdevents *ev, fdnode *fdn)
{
    do {
        switch (errno) {
         #ifdef EWOULDBLOCK
         #if EAGAIN != EWOULDBLOCK
          case EWOULDBLOCK:
         #endif
         #endif
          case EAGAIN:
          case EINTR:
            /* temporary error; retry */
            break;
          /*case ENOMEM:*/
          default:
            /* unrecoverable error; might leak fd */
            log_perror(ev->errh, __FILE__, __LINE__,
              "fdevent event_del failed on fd %d", fdn->fd);
            return 0;
        }
    } while (0 != ev->event_del(ev, fdn));
    return 1;
}

static void
fdevent_fdnode_event_unsetter (fdevents *ev, fdnode *fdn)
{
    if (-1 == fdn->fde_ndx) return;
    if (0 != ev->event_del(ev, fdn))
        fdevent_fdnode_event_unsetter_retry(ev, fdn);
    fdn->fde_ndx = -1;
    fdn->events = 0;
}

__attribute_cold__
__attribute_noinline__
static int
fdevent_fdnode_event_setter_retry (fdevents *ev, fdnode *fdn, int events)
{
    do {
        switch (errno) {
         #ifdef EWOULDBLOCK
         #if EAGAIN != EWOULDBLOCK
          case EWOULDBLOCK:
         #endif
         #endif
          case EAGAIN:
          case EINTR:
            /* temporary error; retry */
            break;
          /*case ENOMEM:*/
          default:
            /* unrecoverable error */
            log_perror(ev->errh, __FILE__, __LINE__,
              "fdevent event_set failed on fd %d", fdn->fd);
            return 0;
        }
    } while (0 != ev->event_set(ev, fdn, events));
    return 1;
}

static void
fdevent_fdnode_event_setter (fdevents *ev, fdnode *fdn, int events)
{
    /*(Note: skips registering with kernel if initial events is 0,
     * so caller should pass non-zero events for initial registration.
     * If never registered due to never being called with non-zero events,
     * then FDEVENT_HUP or FDEVENT_ERR will never be returned.) */
    if (fdn->events == events) return;/*(no change; nothing to do)*/

    if (0 == ev->event_set(ev, fdn, events)
        || fdevent_fdnode_event_setter_retry(ev, fdn, events))
        fdn->events = events;
}

void
fdevent_fdnode_event_del (fdevents *ev, fdnode *fdn)
{
    if (NULL != fdn) fdevent_fdnode_event_unsetter(ev, fdn);
}

void
fdevent_fdnode_event_set (fdevents *ev, fdnode *fdn, int events)
{
    if (NULL != fdn) fdevent_fdnode_event_setter(ev, fdn, events);
}

void
fdevent_fdnode_event_add (fdevents *ev, fdnode *fdn, int event)
{
    if (NULL != fdn) fdevent_fdnode_event_setter(ev, fdn, (fdn->events|event));
}

void
fdevent_fdnode_event_clr (fdevents *ev, fdnode *fdn, int event)
{
    if (NULL != fdn) fdevent_fdnode_event_setter(ev, fdn, (fdn->events&~event));
}