1 /*--------------------------------------------------------------------------
2 Copyright 1999,2000, Dan Kegel http://www.kegel.com/
3 See the file COPYING
4 (Also freely licensed to Disappearing, Inc. under a separate license
5 which allows them to do absolutely anything they want with it, without
6 regard to the GPL.)
7
8 This module is free software; you can redistribute it and/or modify
9 it under the terms of the GNU General Public License as published by
10 the Free Software Foundation; either version 2 of the License, or
11 (at your option) any later version.
12
13 This module is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 GNU General Public License for more details.
17
18 You should have received a copy of the GNU General Public License
19 along with this program; if not, write to the Free Software
20 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
21 --------------------------------------------------------------------------*/
22 #if HAVE_DEVPOLL
23
24 #include "dprint.h"
25 #include <string.h>
26 #include <sys/ioctl.h>
27 #include <sys/stat.h>
28 #include <sys/types.h>
29 #include <sys/poll.h>
30 #include <sys/devpoll.h>
31 #include "Poller_devpoll.h"
32
33 #include <errno.h>
34 #include <assert.h>
35 #include <stdlib.h>
36 #include <fcntl.h>
37
38 #ifndef POLLREMOVE
39 #define POLLREMOVE 0x1000
40 #endif
41
init()42 int Poller_devpoll::init()
43 {
44 DPRINT(("init()\n"));
45
46 m_rfds = 0;
47
48 // Allocate things indexed by file descriptor.
49 m_clivent_alloc = 16;
50 m_clivents = (struct clivent *)malloc(sizeof(struct clivent) * m_clivent_alloc);
51 if (!m_clivents)
52 return ENOMEM;
53 memset(m_clivents, 0, m_clivent_alloc * sizeof(struct clivent));
54
55 // Allocate array of pollfds
56 m_pfds_used = 0;
57 m_pfds_alloc = 16;
58 m_pfds = (struct pollfd *)malloc(sizeof(struct pollfd) * m_pfds_alloc);
59 if (!m_pfds)
60 return ENOMEM;
61
62 // Open /dev/poll driver
63 if ((m_dpfd = open("/dev/poll", O_RDWR)) < 0)
64 return ENOENT;
65
66 Poller::init();
67 return 0;
68 }
69
shutdown()70 void Poller_devpoll::shutdown()
71 {
72 if (m_clivents) {
73 close(m_dpfd);
74 free(m_clivents);
75 m_clivents = NULL;
76 free(m_pfds);
77 m_pfds = NULL;
78 }
79 Poller::shutdown();
80 }
81
add(int fd,Client * client,short eventmask)82 int Poller_devpoll::add(int fd, Client *client, short eventmask)
83 {
84 if (fd < 0) {
85 LOG_ERROR(("add(fd %d): fd out of range\n", fd));
86 return EINVAL;
87 }
88 if ((fd < m_clivent_alloc) && m_clivents[fd].client) {
89 LOG_ERROR(("add(fd %d, %p,): already monitoring that fd!\n", fd, m_clivents[fd].client));
90 return EINVAL;
91 }
92
93 int i, n;
94
95 // Resize arrays indexed by fd if fd is beyond what we've seen.
96 if (fd >= m_clivent_alloc) {
97 n = m_clivent_alloc * 2;
98 if (n < fd + 1)
99 n = fd + 1;
100
101 struct clivent *pcv = (struct clivent *) realloc(m_clivents, n * sizeof(struct clivent));
102 if (!pcv)
103 return ENOMEM;
104 // Clear new elements
105 for (i=m_clivent_alloc; i<n; i++)
106 pcv[i].client = NULL;
107 m_clivents = pcv;
108
109 m_clivent_alloc = n;
110 }
111
112
113 // Resize things indexed by client number if we've run out of spots.
114 if (m_pfds_used == m_pfds_alloc) {
115 n = m_pfds_alloc * 2;
116
117 struct pollfd *pfds = (struct pollfd *) realloc(m_pfds, n * sizeof(struct pollfd));
118 if (!pfds)
119 return ENOMEM;
120 m_pfds = pfds;
121
122 m_pfds_alloc = n;
123 }
124
125 // Update things indexed by file descriptor.
126 m_clivents[fd].client = client;
127 m_clivents[fd].events = eventmask;
128
129 // Prepare a pollfd to write to /dev/poll
130 struct pollfd tmp_pfd;
131
132 tmp_pfd.fd = fd;
133 tmp_pfd.events = eventmask;
134
135 // Write pollfd to /dev/poll
136 if (write(m_dpfd, &tmp_pfd, sizeof(struct pollfd)) != sizeof(struct pollfd)) {
137 LOG_ERROR(("add(fd %d): could not write fd to dev/poll", fd));
138 return EINVAL;
139 }
140
141 // Update limits.
142 m_pfds_used++;
143
144 DPRINT(("add(%d, %p, %x) m_pfds_used %d\n",
145 fd, client, eventmask, m_pfds_used));
146 return 0;
147 }
148
del(int fd)149 int Poller_devpoll::del(int fd)
150 {
151 // Sanity checks
152 if (fd < 0 || fd >= m_clivent_alloc) {
153 LOG_ERROR(("del(%d): fd out of range\n", fd));
154 return EINVAL;
155 }
156 if (!m_clivents[fd].client) {
157 LOG_ERROR(("del(fd %d): not monitoring that fd!\n", fd));
158 return EINVAL;
159 }
160 assert(m_pfds_used > 0);
161
162 DPRINT(("del(%d): m_pfds_used %d on entry\n", fd, m_pfds_used));
163
164 // Remove from set of pollfds monitored by /dev/poll
165 struct pollfd tmp_pfd;
166
167 tmp_pfd.fd = fd;
168 tmp_pfd.events = POLLREMOVE;
169
170 // Write pollfd to /dev/poll
171 if (write(m_dpfd, &tmp_pfd, sizeof(struct pollfd)) != sizeof(struct pollfd)) {
172 LOG_ERROR(("add(fd %d): could not write fd to dev/poll", fd));
173 return EINVAL;
174 }
175
176 // Remove from arrays indexed by fd.
177 m_clivents[fd].client = NULL;
178
179 // Update limits
180 m_pfds_used--;
181
182 return 0;
183 }
184
setMask(int fd,short eventmask)185 int Poller_devpoll::setMask(int fd, short eventmask)
186 {
187 // Sanity checks
188 if ((fd < 0) || (fd >= m_clivent_alloc)) {
189 LOG_ERROR(("setMask(fd %d): fd out of range\n", fd));
190 return EINVAL;
191 }
192 if (!m_clivents[fd].client) {
193 LOG_ERROR(("setMask(fd %d): not monitoring that fd!\n", fd));
194 return EINVAL;
195 }
196
197 m_clivents[fd].events = eventmask;
198
199 // Prepare a pollfd to write to /dev/poll
200 #ifdef SOLARIS
201
202 struct pollfd tmp_pfd[2];
203
204 tmp_pfd[0].fd = fd;
205 tmp_pfd[0].events = POLLREMOVE;
206 tmp_pfd[1].fd = fd;
207 tmp_pfd[1].events = m_clivents[fd].events;
208
209 #else
210 struct pollfd tmp_pfd[1];
211
212 tmp_pfd[0].fd = fd;
213 tmp_pfd[0].events = m_clivents[fd].events;
214
215 #endif
216 // Write pollfd to /dev/poll
217 if (write(m_dpfd, tmp_pfd, sizeof(tmp_pfd)) != sizeof(tmp_pfd)) {
218 LOG_ERROR(("setMask(fd %d): could not write fd to dev/poll", fd));
219 return EINVAL;
220 }
221
222 DPRINT(("setMask(%d, %x): new mask %x\n", fd, eventmask, tmp_pfd.events));
223
224 return 0;
225 }
226
orMask(int fd,short eventmask)227 int Poller_devpoll::orMask(int fd, short eventmask)
228 {
229 // Sanity checks
230 if ((fd < 0) || (fd >= m_clivent_alloc)) {
231 LOG_ERROR(("orMask(fd %d): fd out of range\n", fd));
232 return EINVAL;
233 }
234 if (!m_clivents[fd].client) {
235 LOG_ERROR(("orMask(fd %d): not monitoring that fd!\n", fd));
236 return EINVAL;
237 }
238
239 m_clivents[fd].events |= eventmask;
240
241 // Prepare a pollfd to write to /dev/poll
242 struct pollfd tmp_pfd;
243
244 tmp_pfd.fd = fd;
245 tmp_pfd.events = m_clivents[fd].events;
246
247 // Write pollfd to /dev/poll
248 if (write(m_dpfd, &tmp_pfd, sizeof(struct pollfd)) != sizeof(struct pollfd)) {
249 LOG_ERROR(("orMask(fd %d): could not write fd to dev/poll", fd));
250 return EINVAL;
251 }
252
253 DPRINT(("orMask(%d, %x): new mask %x\n", fd, eventmask, tmp_pfd.events));
254
255 return 0;
256 }
257
andMask(int fd,short eventmask)258 int Poller_devpoll::andMask(int fd, short eventmask)
259 {
260 // Sanity checks
261 if ((fd < 0) || (fd >= m_clivent_alloc)) {
262 LOG_ERROR(("andMask(fd %d): fd out of range\n", fd));
263 return EINVAL;
264 }
265 if (!m_clivents[fd].client) {
266 LOG_ERROR(("andMask(fd %d): not monitoring that fd!\n", fd));
267 return EINVAL;
268 }
269
270 m_clivents[fd].events &= eventmask;
271
272 // Prepare a pollfd to write to /dev/poll
273 #ifdef SOLARIS
274
275 struct pollfd tmp_pfd[2];
276
277 tmp_pfd[0].fd = fd;
278 tmp_pfd[0].events = POLLREMOVE;
279 tmp_pfd[1].fd = fd;
280 tmp_pfd[1].events = m_clivents[fd].events;
281
282 #else
283 struct pollfd tmp_pfd[1];
284
285 tmp_pfd[0].fd = fd;
286 tmp_pfd[0].events = m_clivents[fd].events;
287
288 #endif
289 // Write pollfd to /dev/poll
290 if (write(m_dpfd, tmp_pfd, sizeof(tmp_pfd)) != sizeof(tmp_pfd)) {
291 LOG_ERROR(("andMask(fd %d): could not write fd to dev/poll", fd));
292 return EINVAL;
293 }
294
295 DPRINT(("andMask(%d, %x): new mask %x\n", fd, eventmask, tmp_pfd.events));
296
297 return 0;
298 }
299
300 /**
301 Sleep at most timeout_millisec waiting for an I/O readiness event
302 on the file descriptors we're watching. Fills internal array
303 of readiness events. Call getNextEvent() repeatedly to read its
304 contents.
305 @return 0 on success, EWOULDBLOCK if no events ready
306 */
waitForEvents(int timeout_millisec)307 int Poller_devpoll::waitForEvents(int timeout_millisec)
308 {
309 int err;
310 struct dvpoll dopoll;
311
312 dopoll.dp_timeout = timeout_millisec;
313 dopoll.dp_nfds = m_pfds_used;
314 dopoll.dp_fds = m_pfds;
315
316 // Wait for I/O events the clients are interested in.
317 m_rfds = ioctl(m_dpfd, DP_POLL, &dopoll);
318 if (m_rfds == -1) {
319 err = errno;
320 DPRINT(("waitForEvents: poll() returned -1, errno %d\n", err));
321 return err;
322 }
323
324 LOG_TRACE(("waitForEvents: got %d events\n", m_rfds));
325
326 return m_rfds ? 0 : EWOULDBLOCK;
327 }
328
329 /**
330 Get the next event that was found by waitForEvents.
331 @return 0 on success, EWOULDBLOCK if no more events
332 */
getNextEvent(PollEvent * e)333 int Poller_devpoll::getNextEvent(PollEvent *e)
334 {
335 while (m_rfds >= 1) {
336 m_rfds--;
337 assert(m_pfds[m_rfds].revents > 0);
338
339 int fd = m_pfds[m_rfds].fd;
340 assert((0 <= fd) && (fd < m_clivent_alloc));
341
342 // Check to make sure that del() hasnt been called on this fd
343 if (!m_clivents[fd].client)
344 continue;
345
346 e->fd = fd;
347 e->revents = m_pfds[m_rfds].revents;
348 e->client = m_clivents[fd].client;
349
350 LOG_TRACE(("getNextEvent: fd %d revents %x m_rfds %d\n", e->fd, e->revents, m_rfds));
351
352 return 0;
353 }
354 return EWOULDBLOCK;
355 }
356
357
waitAndDispatchEvents(int timeout_millisec)358 int Poller_devpoll::waitAndDispatchEvents(int timeout_millisec)
359 {
360 int err;
361 PollEvent event;
362
363 err = waitForEvents(timeout_millisec);
364 if (err)
365 return err;
366
367 // Pump any network traffic into the appropriate Clients
368 while (m_rfds > 0) {
369 err = getNextEvent(&event);
370 if (err) {
371 if (err != EWOULDBLOCK)
372 DPRINT(("waitAndDispatchEvents: getNextEvent() returned %d\n", err));
373 break;
374 }
375 err = event.client->notifyPollEvent(&event);
376 if (err) {
377 DPRINT(("waitAndDispatchEvents: %p->notifyPollEvent(fd %d) returned %d, deleting\n",
378 event.client, event.fd, err));
379 del(event.fd);
380 }
381 }
382
383 return 0;
384 }
385
386 #endif
387