summaryrefslogtreecommitdiff
path: root/src/fdevent_freebsd_kqueue.c
blob: 8960a19b9c913bda123d58730eeccddade45ffab (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
#include "first.h"

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

#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>

#ifdef FDEVENT_USE_FREEBSD_KQUEUE

# include <sys/event.h>
# include <sys/time.h>

__attribute_cold__
static void fdevent_freebsd_kqueue_free(fdevents *ev) {
	close(ev->kq_fd);
	free(ev->kq_results);
}

static int fdevent_freebsd_kqueue_event_del(fdevents *ev, fdnode *fdn) {
	struct kevent kev[2];
	struct timespec ts = {0, 0};
	int fd = fdn->fd;
	int n = 0;
	int oevents = fdn->events;

	if (oevents & FDEVENT_IN)  {
		EV_SET(&kev[n], fd, EVFILT_READ, EV_DELETE, 0, 0, fdn);
		n++;
	}
	if (oevents & FDEVENT_OUT)  {
		EV_SET(&kev[n], fd, EVFILT_WRITE, EV_DELETE, 0, 0, fdn);
		n++;
	}

	return (0 != n) ? kevent(ev->kq_fd, kev, n, NULL, 0, &ts) : 0;
	/*(kevent() changelist still processed on EINTR,
	 * but EINTR should not be received since 0 == nevents)*/
}

static int fdevent_freebsd_kqueue_event_set(fdevents *ev, fdnode *fdn, int events) {
	struct kevent kev[2];
	struct timespec ts = {0, 0};
	int fd = fdn->fde_ndx = fdn->fd;
	int n = 0;
	int oevents = fdn->events;
	int addevents = events & ~oevents;
	int delevents = ~events & oevents;

	if (addevents & FDEVENT_IN)  {
		EV_SET(&kev[n], fd, EVFILT_READ, EV_ADD, 0, 0, fdn);
		n++;
	} else if (delevents & FDEVENT_IN) {
		EV_SET(&kev[n], fd, EVFILT_READ, EV_DELETE, 0, 0, fdn);
		n++;
	}
	if (addevents & FDEVENT_OUT)  {
		EV_SET(&kev[n], fd, EVFILT_WRITE, EV_ADD, 0, 0, fdn);
		n++;
	} else if (delevents & FDEVENT_OUT) {
		EV_SET(&kev[n], fd, EVFILT_WRITE, EV_DELETE, 0, 0, fdn);
		n++;
	}

	return (0 != n) ? kevent(ev->kq_fd, kev, n, NULL, 0, &ts) : 0;
	/*(kevent() changelist still processed on EINTR,
	 * but EINTR should not be received since 0 == nevents)*/
}

static int fdevent_freebsd_kqueue_poll(fdevents * const ev, int timeout_ms) {
    server * const srv = ev->srv;
    struct timespec ts;
    int n;

    ts.tv_sec  = timeout_ms / 1000;
    ts.tv_nsec = (timeout_ms % 1000) * 1000000;

    n = kevent(ev->kq_fd, NULL, 0, ev->kq_results, ev->maxfds, &ts);

    for (int i = 0; i < n; ++i) {
        fdnode * const fdn = (fdnode *)ev->kq_results[i].udata;
        int filt = ev->kq_results[i].filter;
        int e = ev->kq_results[i].flags;
        if ((fdevent_handler)NULL != fdn->handler) {
            int revents = (filt == EVFILT_READ) ? FDEVENT_IN : FDEVENT_OUT;
            if (e & EV_EOF)
                revents |= (filt == EVFILT_READ ? FDEVENT_RDHUP : FDEVENT_HUP);
            if (e & EV_ERROR)
                revents |= FDEVENT_ERR;
            (*fdn->handler)(srv, fdn->ctx, revents);
        }
    }
    return n;
}

__attribute_cold__
static int fdevent_freebsd_kqueue_reset(fdevents *ev) {
	return (-1 != (ev->kq_fd = kqueue())) ? 0 : -1;
}

__attribute_cold__
int fdevent_freebsd_kqueue_init(fdevents *ev) {
	ev->type       = FDEVENT_HANDLER_FREEBSD_KQUEUE;
	ev->event_set  = fdevent_freebsd_kqueue_event_set;
	ev->event_del  = fdevent_freebsd_kqueue_event_del;
	ev->poll       = fdevent_freebsd_kqueue_poll;
	ev->reset      = fdevent_freebsd_kqueue_reset;
	ev->free       = fdevent_freebsd_kqueue_free;
	ev->kq_fd      = -1;
	ev->kq_results = calloc(ev->maxfds, sizeof(*ev->kq_results));
	force_assert(NULL != ev->kq_results);
	return 0;
}

#endif