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 #include "dprint.h"
23 #include "Poller_poll.h"
24
25 #include <errno.h>
26 #include <assert.h>
27 #include <stdlib.h>
28 #include <fcntl.h>
29
init()30 int Poller_poll::init()
31 {
32 DPRINT(("init()\n"));
33
34 // Allocate things indexed by file descriptor.
35 m_fd2client_used = 0;
36 m_fd2client_alloc = 16;
37 m_fd2pfdnum = (int *)malloc(sizeof(int) * m_fd2client_alloc);
38 if (!m_fd2pfdnum)
39 return ENOMEM;
40
41 // Allocate things indexed by client number.
42 m_pfds_used = 0;
43 m_pfds_alloc = 16;
44 m_clients = (Client **)malloc(sizeof(Client *) * m_pfds_alloc);
45 if (!m_clients)
46 return ENOMEM;
47 m_pfds = (struct pollfd *)malloc(sizeof(struct pollfd) * m_pfds_alloc);
48 if (!m_pfds)
49 return ENOMEM;
50
51 Poller::init();
52 return 0;
53 }
54
shutdown()55 void Poller_poll::shutdown()
56 {
57 if (m_fd2pfdnum) {
58 free(m_fd2pfdnum);
59 m_fd2pfdnum = NULL;
60 free(m_clients);
61 m_clients = NULL;
62 free(m_pfds);
63 m_pfds = NULL;
64 }
65 Poller::shutdown();
66 }
67
add(int fd,Client * client,short eventmask)68 int Poller_poll::add(int fd, Client *client, short eventmask)
69 {
70 int i, n;
71 // Resize arrays indexed by fd if fd is beyond what we've seen.
72 if (fd >= m_fd2client_alloc) {
73 n = m_fd2client_alloc * 2;
74 if (n < fd + 1)
75 n = fd + 1;
76
77 int *pn = (int *)realloc(m_fd2pfdnum, n * sizeof(int));
78 if (!pn)
79 return ENOMEM;
80 // Clear new elements
81 for (i=m_fd2client_alloc; i<n; i++)
82 pn[i] = -1;
83 m_fd2pfdnum = pn;
84
85 m_fd2client_alloc = n;
86 }
87
88 // Resize things indexed by client number if we've run out of spots.
89 if (m_pfds_used == m_pfds_alloc) {
90 n = m_pfds_alloc * 2;
91
92 Client **clients= (Client **) realloc(m_clients, n * sizeof(Client *));
93 if (!clients)
94 return ENOMEM;
95 m_clients = clients;
96
97 struct pollfd *pfds = (struct pollfd *) realloc(m_pfds, n * sizeof(struct pollfd));
98 if (!pfds)
99 return ENOMEM;
100 m_pfds = pfds;
101
102 m_pfds_alloc = n;
103 }
104
105 // Update things indexed by file descriptor.
106 m_clients[m_pfds_used] = client;
107 m_pfds[m_pfds_used].fd = fd;
108 m_pfds[m_pfds_used].events = eventmask;
109
110 // Update things indexed by file descriptor.
111 m_fd2pfdnum[fd] = m_pfds_used;
112
113 // Update limits.
114 if (fd >= m_fd2client_used)
115 m_fd2client_used = fd+1;
116 m_pfds_used++;
117
118 DPRINT(("add(%d, %p, %x) pfdnum %d m_pfds_used %d\n",
119 fd, client, eventmask, m_pfds_used -1, m_pfds_used));
120 return 0;
121 }
122
del(int fd)123 int Poller_poll::del(int fd)
124 {
125 // Sanity checks
126 if (fd < 0 || fd >= m_fd2client_used) {
127 LOG_ERROR(("del(%d): fd out of range\n", fd));
128 return EINVAL;
129 }
130 assert(m_pfds_used > 0);
131
132 // Note where the Client was in the pollfd / m_clients array
133 int pfdnum = m_fd2pfdnum[fd];
134 DPRINT(("del(%d): pfdnum %d m_pfds_used %d on entry\n", fd, pfdnum, m_pfds_used));
135 if (pfdnum == -1)
136 return ENOENT;
137 assert(pfdnum >= 0);
138 assert(pfdnum < m_pfds_used);
139
140 // Remove from arrays indexed by pfdnum. Close up hole so poll() doesn't barf.
141 if (pfdnum != m_pfds_used - 1) {
142 m_clients[pfdnum] = m_clients[m_pfds_used - 1];
143 m_pfds[pfdnum] = m_pfds[m_pfds_used - 1];
144 m_fd2pfdnum[m_pfds[pfdnum].fd] = pfdnum;
145 }
146 m_clients[m_pfds_used - 1] = NULL;
147 m_pfds[m_pfds_used - 1].fd = -1;
148
149 // Remove from arrays indexed by fd.
150 m_fd2pfdnum[fd] = -1;
151
152 // Update limits
153 while (m_fd2client_used && (m_fd2pfdnum[m_fd2client_used - 1] == -1))
154 m_fd2client_used--;
155 m_pfds_used--;
156
157 return 0;
158 }
159
setMask(int fd,short eventmask)160 int Poller_poll::setMask(int fd, short eventmask)
161 {
162 int i = m_fd2pfdnum[fd];
163 if (i == -1) {
164 DPRINT(("setMask(fd %d, %x): fd no longer in Poller\n", fd, eventmask));
165 return ENOENT;
166 }
167 assert(i >= 0);
168 assert(i < m_pfds_used);
169 m_pfds[i].events = eventmask;
170
171 DPRINT(("setMask(%d, %x): new mask %x\n", fd, eventmask, m_pfds[i].events));
172
173 return 0;
174 }
175
orMask(int fd,short eventmask)176 int Poller_poll::orMask(int fd, short eventmask)
177 {
178 int i = m_fd2pfdnum[fd];
179 assert(i >= 0);
180 assert(i < m_pfds_used);
181 m_pfds[i].events |= eventmask;
182
183 DPRINT(("orMask(%d, %x): new mask %x\n", fd, eventmask, m_pfds[i].events));
184
185 return 0;
186 }
187
andMask(int fd,short eventmask)188 int Poller_poll::andMask(int fd, short eventmask)
189 {
190 int i = m_fd2pfdnum[fd];
191 assert(i >= 0);
192 assert(i < m_pfds_used);
193 m_pfds[i].events &= eventmask;
194
195 DPRINT(("andMask(%d, %x): new mask %x\n", fd, eventmask, m_pfds[i].events));
196
197 return 0;
198 }
199
200 /**
201 Sleep at most timeout_millisec waiting for an I/O readiness event
202 on the file descriptors we're watching. Fills internal array
203 of readiness events. Call getNextEvent() repeatedly to read its
204 contents.
205 @return 0 on success, EWOULDBLOCK if no events ready
206 */
waitForEvents(int timeout_millisec)207 int Poller_poll::waitForEvents(int timeout_millisec)
208 {
209 int err;
210
211 // Wait for I/O events the clients are interested in.
212 m_rfds = poll(m_pfds, m_pfds_used, timeout_millisec);
213 if (m_rfds == -1) {
214 err = errno;
215 m_cur_pfdnum = -1;
216 DPRINT(("waitForEvents: poll() returned -1, errno %d\n", err));
217 return err;
218 }
219 m_cur_pfdnum = m_pfds_used;
220
221 LOG_TRACE(("waitForEvents: got %d events\n", m_rfds));
222 return m_rfds ? 0 : EWOULDBLOCK;
223 }
224
225 /**
226 Get the next event that was found by waitForEvents.
227 @return 0 on success, EWOULDBLOCK if no more events
228 */
getNextEvent(PollEvent * e)229 int Poller_poll::getNextEvent(PollEvent *e)
230 {
231 if (m_rfds < 1)
232 return EWOULDBLOCK;
233
234 // Iterate downwards because otherwise if notifypollEvent()
235 // calls add() or del(), we might skip an fd or process one
236 // that hasn't been through poll() yet.
237 if (m_cur_pfdnum > m_pfds_used)
238 m_cur_pfdnum = m_pfds_used;
239 while (--m_cur_pfdnum > -1) {
240 if (m_pfds[m_cur_pfdnum].revents)
241 break;
242 }
243 if (m_cur_pfdnum == -1)
244 return EWOULDBLOCK;
245
246 int fd = m_pfds[m_cur_pfdnum].fd;
247
248 // Sanity checks
249 assert((0 <= fd) && (fd < m_fd2client_used));
250 int j = m_fd2pfdnum[fd];
251 assert((0 <= j) && (j < m_pfds_used));
252 assert(j == m_cur_pfdnum);
253
254 e->fd = fd;
255 e->revents = m_pfds[m_cur_pfdnum].revents;
256 e->client = m_clients[j];
257
258 m_rfds--;
259 LOG_TRACE(("getNextEvent: fd %d revents %x j %d m_rfds %d\n",
260 e->fd, e->revents, j, m_rfds));
261
262 return 0;
263 }
waitAndDispatchEvents(int timeout_millisec)264 int Poller_poll::waitAndDispatchEvents(int timeout_millisec)
265 {
266 int err;
267 PollEvent event;
268
269 err = waitForEvents(timeout_millisec);
270 if (err)
271 return err;
272
273 // Pump any network traffic into the appropriate Clients
274 while (m_rfds > 0) {
275 err = getNextEvent(&event);
276 if (err) {
277 if (err != EWOULDBLOCK)
278 DPRINT(("waitAndDispatchEvents: getNextEvent() returned %d\n", err));
279 break;
280 }
281 err = event.client->notifyPollEvent(&event);
282 if (err) {
283 DPRINT(("waitAndDispatchEvents: %p->notifyPollEvent(fd %d) returned %d, deleting\n",
284 event.client, event.fd, err));
285 del(event.fd);
286 }
287 }
288
289 return 0;
290 }
291