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