comparison src/lib/ioloop-kqueue.c @ 3749:194295062e5e HEAD

Added kqueue support. Patch by Vaclav Haisman.
author Timo Sirainen <tss@iki.fi>
date Wed, 14 Dec 2005 20:51:52 +0200
parents
children d996a407aa4b
comparison
equal deleted inserted replaced
3748:1649ca519b7d 3749:194295062e5e
1 /*
2 * FreeBSD kqueue() based ioloop handler.
3 *
4 * Copyright (c) 2005 Vaclav Haisman <v.haisman@sh.cvut.cz>
5 *
6 * This library is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU Lesser General Public License as published
8 * by the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 */
11
12 /* @UNSAFE: whole file */
13
14 #include "lib.h"
15 #include "ioloop-internal.h"
16
17 #ifdef IOLOOP_KQUEUE
18
19 #include <sys/types.h>
20 #include <sys/event.h>
21 #include <sys/time.h>
22
23 #ifndef INITIAL_BUF_SIZE
24 # define INITIAL_BUF_SIZE 128
25 #endif
26
27 struct ioloop_handler_context {
28 int kq;
29 size_t evbuf_size;
30 struct kevent *evbuf;
31
32 size_t fds_size;
33 struct fdrecord *fds;
34 };
35
36 struct fdrecord {
37 struct io *errio;
38 enum io_condition mode;
39 };
40
41 void io_loop_handler_init(struct ioloop *ioloop)
42 {
43 struct ioloop_handler_context *ctx;
44
45 ioloop->handler_context = ctx =
46 p_new(ioloop->pool, struct ioloop_handler_context, 1);
47
48 ctx->evbuf_size = INITIAL_BUF_SIZE;
49 ctx->evbuf = p_new(ioloop->pool, struct kevent, ctx->evbuf_size);
50 ctx->kq = kqueue();
51 if (ctx->kq < 0)
52 i_fatal("kqueue(): %m");
53
54 ctx->fds_size = INITIAL_BUF_SIZE;
55 ctx->fds = p_new(ioloop->pool, struct fdrecord, ctx->fds_size);
56 }
57
58 void io_loop_handler_deinit(struct ioloop *ioloop)
59 {
60 p_free(ioloop->pool, ioloop->handler_context->evbuf);
61 p_free(ioloop->pool, ioloop->handler_context->fds);
62 p_free(ioloop->pool, ioloop->handler_context);
63 }
64
65 void io_loop_handle_add(struct ioloop *ioloop, struct io *io)
66 {
67 struct ioloop_handler_context *ctx = ioloop->handler_context;
68 const int fd = io->fd;
69 struct kevent ev = {fd, 0, EV_ADD | EV_CLEAR | EV_EOF, 0, 0, NULL};
70 enum io_condition condition = io->condition;
71
72 /* grow ctx->fds array if necessary */
73 if ((size_t)fd >= ctx->fds_size) {
74 size_t old_size = ctx->fds_size;
75
76 ctx->fds_size = nearest_power((unsigned int)fd+1);
77 i_assert(ctx->fds_size < (size_t)-1 / sizeof(int));
78
79 ctx->fds = p_realloc(ioloop->pool, ctx->fds,
80 sizeof(struct fdrecord) * old_size,
81 sizeof(struct fdrecord) * ctx->fds_size);
82 memset(ctx->fds + old_size, 0,
83 sizeof(struct fdrecord) * (ctx->fds_size - old_size));
84 }
85
86 if (condition & (IO_READ | IO_WRITE))
87 ev.udata = io;
88 if (condition & IO_ERROR)
89 ctx->fds[fd].errio = io;
90
91 if (condition & (IO_READ | IO_ERROR)) {
92 ctx->fds[fd].mode |= condition;
93 ev.filter = EVFILT_READ;
94 kevent(ctx->kq, &ev, 1, NULL, 0, NULL);
95 }
96 if (condition & (IO_WRITE | IO_ERROR)) {
97 ctx->fds[fd].mode |= condition;
98 ev.filter = EVFILT_WRITE;
99 kevent(ctx->kq, &ev, 1, NULL, 0, NULL);
100 }
101 }
102
103 void io_loop_handle_remove(struct ioloop *ioloop, struct io *io)
104 {
105 struct ioloop_handler_context *ctx = ioloop->handler_context;
106 struct kevent ev = { fd, 0, EV_DELETE, 0, 0, NULL };
107 struct fdrecord *const fds = ctx->fds;
108 const int fd = io->fd;
109 const enum io_condition condition = io->condition;
110
111 i_assert((size_t)fd < ctx->fds_size);
112 i_assert(fds[fd].mode != 0);
113
114 if (condition & IO_ERROR)
115 fds[fd].errio = NULL;
116 if (condition & (IO_READ | IO_ERROR)) {
117 ev.filter = EVFILT_READ;
118 fds[fd].mode &= ~condition;
119 if ((fds[fd].mode & (IO_READ | IO_ERROR)) == 0)
120 kevent(ctx->kq, &ev, 1, NULL, 0, NULL);
121 }
122 if (condition & (IO_WRITE | IO_ERROR)) {
123 ev.filter = EVFILT_WRITE;
124 fds[fd].mode &= ~condition;
125 if ((fds[fd].mode & (IO_WRITE | IO_ERROR)) == 0)
126 kevent(ctx->kq, &ev, 1, NULL, 0, NULL);
127 }
128 }
129
130 void io_loop_handler_run(struct ioloop *ioloop)
131 {
132 struct ioloop_handler_context *ctx = ioloop->handler_context;
133 struct timeval tv;
134 struct timespec ts;
135 unsigned int t_id;
136 int msecs, ret, i;
137
138 /* get the time left for next timeout task */
139 msecs = io_loop_get_wait_time(ioloop->timeouts, &tv, NULL);
140 ts.tv_sec = tv.tv_sec;
141 ts.tv_nsec = tv.tv_usec * 1000;
142
143 /* wait for events */
144 ret = kevent (ctx->kq, NULL, 0, ctx->evbuf, ctx->evbuf_size, &ts);
145 if (ret < 0 && errno != EINTR)
146 i_fatal("kevent(): %m");
147
148 /* execute timeout handlers */
149 io_loop_handle_timeouts(ioloop);
150
151 if (ret <= 0 || !ioloop->running) {
152 /* no I/O events */
153 return;
154 }
155
156 i_assert((size_t)ret <= ctx->evbuf_size);
157
158 /* loop through all received events */
159 for (i = 0; i < ret; ++i) {
160 struct io *io = ctx->evbuf[i].udata;
161
162 i_assert(ctx->evbuf[i].ident < ctx->fds_size);
163 if (ctx->fds[ctx->evbuf[i].ident].mode & IO_ERROR) {
164 struct io *errio = ctx->fds[ctx->evbuf[i].ident].errio;
165
166 t_id = t_push();
167 errio->callback(errio->context);
168 if (t_pop() != t_id) {
169 i_panic("Leaked a t_pop() call"
170 " in I/O handler %p",
171 (void *)errio->callback);
172 }
173 }
174
175 if (ctx->fds[ctx->evbuf[i].ident].mode & (IO_WRITE | IO_READ)) {
176 t_id = t_push();
177 io->callback(io->context);
178 if (t_pop() != t_id) {
179 i_panic("Leaked a t_pop() call"
180 " in I/O handler %p",
181 (void *)io->callback);
182 }
183 }
184 }
185 }
186
187 #endif