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