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