Mercurial > dovecot > original-hg > dovecot-1.2
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 |