1 /* $OpenBSD: uthread_select.c,v 1.11 2004/01/03 07:35:10 brad Exp $ */
2 /*
3 * Copyright (c) 1995-1998 John Birrell <jb@cimlogic.com.au>
4 * All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 * 1. Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 * 3. All advertising materials mentioning features or use of this software
15 * must display the following acknowledgement:
16 * This product includes software developed by John Birrell.
17 * 4. Neither the name of the author nor the names of any co-contributors
18 * may be used to endorse or promote products derived from this software
19 * without specific prior written permission.
20 *
21 * THIS SOFTWARE IS PROVIDED BY JOHN BIRRELL AND CONTRIBUTORS ``AS IS'' AND
22 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
25 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31 * SUCH DAMAGE.
32 *
33 * $FreeBSD: uthread_select.c,v 1.13 1999/08/30 00:02:08 deischen Exp $
34 */
35 #include <unistd.h>
36 #include <errno.h>
37 #include <poll.h>
38 #include <stdlib.h>
39 #include <string.h>
40 #include <sys/param.h>
41 #include <sys/types.h>
42 #include <sys/time.h>
43 #include <sys/fcntl.h>
44 #ifdef _THREAD_SAFE
45 #include <pthread.h>
46 #include "pthread_private.h"
47
48 /*
49 * Minimum number of poll_data entries to allocate
50 */
51 #define POLLDATA_MIN 128
52
53 int
select(int numfds,fd_set * readfds,fd_set * writefds,fd_set * exceptfds,struct timeval * timeout)54 select(int numfds, fd_set * readfds, fd_set * writefds,
55 fd_set * exceptfds, struct timeval * timeout)
56 {
57 struct pthread *curthread = _get_curthread();
58 struct timespec ts;
59 int bit, i, j, ret = 0, f_wait = 1;
60 int events, got_events = 0, fd_count = 0;
61 struct pthread_poll_data data;
62 fd_mask mask, rmask, wmask, emask;
63
64 /* This is a cancellation point: */
65 _thread_enter_cancellation_point();
66
67 if (numfds > _thread_dtablesize) {
68 numfds = _thread_dtablesize;
69 }
70 /* Check if a timeout was specified: */
71 if (timeout) {
72 if (timeout->tv_sec < 0 ||
73 timeout->tv_usec < 0 || timeout->tv_usec >= 1000000) {
74 errno = EINVAL;
75 ret = -1;
76 goto done;
77 }
78
79 /* Convert the timeval to a timespec: */
80 TIMEVAL_TO_TIMESPEC(timeout, &ts);
81
82 /* Set the wake up time: */
83 _thread_kern_set_timeout(&ts);
84 if (ts.tv_sec == 0 && ts.tv_nsec == 0)
85 f_wait = 0;
86 } else {
87 /* Wait for ever: */
88 _thread_kern_set_timeout(NULL);
89 }
90
91 /* Count the number of file descriptors to be polled: */
92 if (numfds && (readfds || writefds || exceptfds)) {
93 for (i = (numfds - 1) / NFDBITS; i >= 0; i--) {
94 rmask = readfds ? readfds->fds_bits[i] : 0;
95 wmask = writefds ? writefds->fds_bits[i] : 0;
96 emask = exceptfds ? exceptfds->fds_bits[i] : 0;
97 mask = rmask | wmask | emask;
98 for (; (bit = ffs(mask)); mask ^= (1 << (bit - 1)))
99 fd_count++;
100 }
101 }
102
103 /*
104 * Allocate memory for poll data if it hasn't already been
105 * allocated or if previously allocated memory is insufficient.
106 */
107 if ((curthread->poll_data.fds == NULL) ||
108 (curthread->poll_data.nfds < fd_count)) {
109 data.fds = (struct pollfd *) realloc(curthread->poll_data.fds,
110 sizeof(struct pollfd) * MAX(POLLDATA_MIN, fd_count));
111 if (data.fds == NULL) {
112 errno = ENOMEM;
113 ret = -1;
114 }
115 else {
116 /*
117 * Note that the threads poll data always
118 * indicates what is allocated, not what is
119 * currently being polled.
120 */
121 curthread->poll_data.fds = data.fds;
122 curthread->poll_data.nfds = MAX(POLLDATA_MIN, fd_count);
123 }
124 }
125 if (ret == 0) {
126 /* Setup the wait data. */
127 data.fds = curthread->poll_data.fds;
128 data.nfds = fd_count;
129
130 /*
131 * Setup the array of pollfds. Optimize this by
132 * running the loop in reverse and stopping when
133 * the number of selected file descriptors is reached.
134 */
135 for (i = (numfds - 1) / NFDBITS, j = fd_count;
136 j != 0 && i >= 0; i--) {
137 rmask = readfds ? readfds->fds_bits[i] : 0;
138 wmask = writefds ? writefds->fds_bits[i] : 0;
139 emask = exceptfds ? exceptfds->fds_bits[i] : 0;
140 mask = rmask | wmask | emask;
141 while ((bit = ffs(mask))) {
142 int n = 1 << (bit - 1);
143 mask ^= n;
144 events = 0;
145 if (rmask & n)
146 events |= POLLRDNORM;
147 if (wmask & n)
148 events |= POLLWRNORM;
149 if (emask & n)
150 events |= POLLRDBAND;
151 if (events != 0) {
152 /*
153 * Set the file descriptor to be polled
154 * and clear revents in case of a
155 * timeout which leaves fds unchanged:
156 */
157 data.fds[--j].fd =
158 (i * NFDBITS) + bit - 1;
159 data.fds[j].events = events;
160 data.fds[j].revents = 0;
161 }
162 }
163 }
164 if (((ret = _thread_sys_poll(data.fds, data.nfds, 0)) == 0) &&
165 (f_wait != 0)) {
166 curthread->data.poll_data = &data;
167 curthread->interrupted = 0;
168 _thread_kern_sched_state(PS_SELECT_WAIT, __FILE__, __LINE__);
169 if (curthread->interrupted) {
170 errno = EINTR;
171 data.nfds = 0;
172 ret = -1;
173 } else
174 ret = data.nfds;
175 }
176 }
177
178 if (ret >= 0) {
179 numfds = 0;
180 for (i = 0; i < fd_count; i++) {
181 /*
182 * Check the results of the poll and clear
183 * this file descriptor from the fdset if
184 * the requested event wasn't ready.
185 */
186
187 /*
188 * First check for invalid descriptor.
189 * If found, set errno and return -1.
190 */
191 if (data.fds[i].revents & POLLNVAL) {
192 errno = EBADF;
193 ret = -1;
194 goto done;
195 }
196
197 got_events = 0;
198 if (readfds != NULL) {
199 if (FD_ISSET(data.fds[i].fd, readfds)) {
200 if ((data.fds[i].revents & (POLLIN
201 | POLLRDNORM | POLLERR
202 | POLLHUP)) != 0)
203 got_events++;
204 else
205 FD_CLR(data.fds[i].fd, readfds);
206 }
207 }
208 if (writefds != NULL) {
209 if (FD_ISSET(data.fds[i].fd, writefds)) {
210 if ((data.fds[i].revents & (POLLOUT
211 | POLLWRNORM | POLLWRBAND | POLLERR
212 | POLLHUP)) != 0)
213 got_events++;
214 else
215 FD_CLR(data.fds[i].fd,
216 writefds);
217 }
218 }
219 if (exceptfds != NULL) {
220 if (FD_ISSET(data.fds[i].fd, exceptfds)) {
221 if (data.fds[i].revents & (POLLRDBAND |
222 POLLPRI))
223 got_events++;
224 else
225 FD_CLR(data.fds[i].fd,
226 exceptfds);
227 }
228 }
229 if (got_events != 0)
230 numfds+=got_events;
231 }
232 ret = numfds;
233 }
234
235 done:
236 /* No longer in a cancellation point: */
237 _thread_leave_cancellation_point();
238
239 return (ret);
240 }
241 #endif
242