blob: 4f9d6840d17057492ab6bf5e9ce64042bad374bc [file] [log] [blame]
Willy Tarreau1e63130a2007-04-09 12:03:06 +02001/*
2 * FD polling functions for FreeBSD kqueue()
3 *
4 * Copyright 2000-2007 Willy Tarreau <w@1wt.eu>
5 *
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU General Public License
8 * as published by the Free Software Foundation; either version
9 * 2 of the License, or (at your option) any later version.
10 *
11 * Note: not knowing much about kqueue, I had to rely on OpenBSD's detailed man
12 * page and to check how it was implemented in lighttpd to understand it better.
13 * But it is possible that I got things wrong.
14 *
15 */
16
Willy Tarreau1e63130a2007-04-09 12:03:06 +020017#include <unistd.h>
18#include <sys/time.h>
19#include <sys/types.h>
20
21#include <sys/event.h>
22#include <sys/time.h>
23
24#include <common/compat.h>
25#include <common/config.h>
26#include <common/time.h>
27
28#include <types/fd.h>
29#include <types/global.h>
30
31#include <proto/fd.h>
Willy Tarreau1e63130a2007-04-09 12:03:06 +020032#include <proto/task.h>
33
34/* private data */
35static fd_set *fd_evts[2];
36static int kqueue_fd;
37static struct kevent *kev = NULL;
38
39/* speeds up conversion of DIR_RD/DIR_WR to EVFILT* */
40static const int dir2filt[2] = { EVFILT_READ, EVFILT_WRITE };
41
42/* completes a change list for deletion */
43REGPRM3 static int kqev_del(struct kevent *kev, const int fd, const int dir)
44{
45 if (FD_ISSET(fd, fd_evts[dir])) {
46 FD_CLR(fd, fd_evts[dir]);
47 EV_SET(kev, fd, dir2filt[dir], EV_DELETE, 0, 0, NULL);
48 return 1;
49 }
50 return 0;
51}
52
53/*
54 * Returns non-zero if direction <dir> is already set for <fd>.
55 */
Willy Tarreau63455a92007-04-09 15:34:49 +020056REGPRM2 static int __fd_is_set(const int fd, int dir)
Willy Tarreau1e63130a2007-04-09 12:03:06 +020057{
58 return FD_ISSET(fd, fd_evts[dir]);
59}
60
61REGPRM2 static int __fd_set(const int fd, int dir)
62{
63 /* if the value was set, do nothing */
64 if (FD_ISSET(fd, fd_evts[dir]))
65 return 0;
66
67 FD_SET(fd, fd_evts[dir]);
68 EV_SET(kev, fd, dir2filt[dir], EV_ADD|EV_CLEAR, 0, 0, NULL);
69 kevent(kqueue_fd, kev, 1, NULL, 0, NULL);
70 return 1;
71}
72
73REGPRM2 static int __fd_clr(const int fd, int dir)
74{
Willy Tarreau1e63130a2007-04-09 12:03:06 +020075 if (!kqev_del(kev, fd, dir))
76 return 0;
77 kevent(kqueue_fd, kev, 1, NULL, 0, NULL);
78 return 1;
79}
80
81REGPRM1 static void __fd_rem(int fd)
82{
83 int changes = 0;
84
Willy Tarreau1e63130a2007-04-09 12:03:06 +020085 changes += kqev_del(&kev[changes], fd, DIR_RD);
86 changes += kqev_del(&kev[changes], fd, DIR_WR);
87
88 if (changes)
89 kevent(kqueue_fd, kev, changes, NULL, 0, NULL);
90}
91
92/*
93 * kqueue() poller
94 */
95REGPRM2 static void kqueue_poll(struct poller *p, int wait_time)
96{
97 int status;
98 int count, fd;
Willy Tarreaucd5ce2a2007-04-09 16:25:46 +020099 struct timespec timeout, *to_ptr;
Willy Tarreau1e63130a2007-04-09 12:03:06 +0200100
Willy Tarreaucd5ce2a2007-04-09 16:25:46 +0200101 to_ptr = NULL; // no timeout
102 if (wait_time >= 0) {
103 timeout.tv_sec = wait_time / 1000;
104 timeout.tv_nsec = (wait_time % 1000) * 1000000;
105 to_ptr = &timeout;
106 }
Willy Tarreau1e63130a2007-04-09 12:03:06 +0200107
108 status = kevent(kqueue_fd, // int kq
109 NULL, // const struct kevent *changelist
110 0, // int nchanges
111 kev, // struct kevent *eventlist
112 maxfd, // int nevents
Willy Tarreaucd5ce2a2007-04-09 16:25:46 +0200113 to_ptr); // const struct timespec *timeout
Willy Tarreau1e63130a2007-04-09 12:03:06 +0200114
115 for (count = 0; count < status; count++) {
116 fd = kev[count].ident;
117 if (kev[count].filter == EVFILT_READ) {
118 if (FD_ISSET(fd, fd_evts[DIR_RD])) {
119 if (fdtab[fd].state == FD_STCLOSE)
120 continue;
121 fdtab[fd].cb[DIR_RD].f(fd);
122 }
123 } else if (kev[count].filter == EVFILT_WRITE) {
124 if (FD_ISSET(fd, fd_evts[DIR_WR])) {
125 if (fdtab[fd].state == FD_STCLOSE)
126 continue;
127 fdtab[fd].cb[DIR_WR].f(fd);
128 }
129 }
130 }
131}
132
133/*
134 * Initialization of the kqueue() poller.
135 * Returns 0 in case of failure, non-zero in case of success. If it fails, it
136 * disables the poller by setting its pref to 0.
137 */
138REGPRM1 static int kqueue_init(struct poller *p)
139{
140 __label__ fail_wevt, fail_revt, fail_fd;
141 int fd_set_bytes;
142
143 p->private = NULL;
144 fd_set_bytes = sizeof(fd_set) * (global.maxsock + FD_SETSIZE - 1) / FD_SETSIZE;
145
146 kqueue_fd = kqueue();
147 if (kqueue_fd < 0)
148 goto fail_fd;
149
150 kev = (struct kevent*)calloc(1, sizeof(struct kevent) * global.maxsock);
151
152 if (kev == NULL)
153 goto fail_kev;
154
155 if ((fd_evts[DIR_RD] = (fd_set *)calloc(1, fd_set_bytes)) == NULL)
156 goto fail_revt;
157
158 if ((fd_evts[DIR_WR] = (fd_set *)calloc(1, fd_set_bytes)) == NULL)
159 goto fail_wevt;
160
161 return 1;
162
163 fail_wevt:
164 free(fd_evts[DIR_RD]);
165 fail_revt:
166 free(kev);
167 fail_kev:
168 close(kqueue_fd);
169 kqueue_fd = 0;
170 fail_fd:
171 p->pref = 0;
172 return 0;
173}
174
175/*
176 * Termination of the kqueue() poller.
177 * Memory is released and the poller is marked as unselectable.
178 */
179REGPRM1 static void kqueue_term(struct poller *p)
180{
181 if (fd_evts[DIR_WR])
182 free(fd_evts[DIR_WR]);
183 if (fd_evts[DIR_RD])
184 free(fd_evts[DIR_RD]);
185 if (kev)
186 free(kev);
187 close(kqueue_fd);
188 kqueue_fd = 0;
189
190 p->private = NULL;
191 p->pref = 0;
192}
193
194/*
195 * The only exported function. Returns 1.
196 */
197//int kqueue_native_register(struct poller *p)
198int kqueue_register(struct poller *p)
199{
200 p->name = "kqueue";
201 p->pref = 300;
202 p->private = NULL;
203
204 p->init = kqueue_init;
205 p->term = kqueue_term;
206 p->poll = kqueue_poll;
207
Willy Tarreau63455a92007-04-09 15:34:49 +0200208 p->is_set = __fd_is_set;
Willy Tarreau1e63130a2007-04-09 12:03:06 +0200209 p->cond_s = p->set = __fd_set;
210 p->cond_c = p->clr = __fd_clr;
211 p->clo = p->rem = __fd_rem;
212
213 return 1;
214}
215
216
217/*
218 * Local variables:
219 * c-indent-level: 8
220 * c-basic-offset: 8
221 * End:
222 */