* Use id(1) instead of grep(1) to detect the presence of the smmsp
[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  * $DragonFly: src/lib/libc_r/uthread/uthread_select.c,v 1.2 2003/06/17 04:26:48 dillon 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 #include <pthread.h>
45 #include "pthread_private.h"
46
47 int 
48 _select(int numfds, fd_set * readfds, fd_set * writefds, fd_set * exceptfds,
49     struct timeval * timeout)
50 {
51         struct pthread  *curthread = _get_curthread();
52         struct timespec ts;
53         int             i, ret = 0, f_wait = 1;
54         int             pfd_index, got_events = 0, fd_count = 0;
55         struct pthread_poll_data data;
56
57         if (numfds > _thread_dtablesize) {
58                 numfds = _thread_dtablesize;
59         }
60         /* Check if a timeout was specified: */
61         if (timeout) {
62                 if (timeout->tv_sec < 0 ||
63                         timeout->tv_usec < 0 || timeout->tv_usec >= 1000000) {
64                         errno = EINVAL;
65                         return (-1);
66                 }
67
68                 /* Convert the timeval to a timespec: */
69                 TIMEVAL_TO_TIMESPEC(timeout, &ts);
70
71                 /* Set the wake up time: */
72                 _thread_kern_set_timeout(&ts);
73                 if (ts.tv_sec == 0 && ts.tv_nsec == 0)
74                         f_wait = 0;
75         } else {
76                 /* Wait for ever: */
77                 _thread_kern_set_timeout(NULL);
78         }
79
80         /* Count the number of file descriptors to be polled: */
81         if (readfds || writefds || exceptfds) {
82                 for (i = 0; i < numfds; i++) {
83                         if ((readfds && FD_ISSET(i, readfds)) ||
84                             (exceptfds && FD_ISSET(i, exceptfds)) ||
85                             (writefds && FD_ISSET(i, writefds))) {
86                                 fd_count++;
87                         }
88                 }
89         }
90
91         /*
92          * Allocate memory for poll data if it hasn't already been
93          * allocated or if previously allocated memory is insufficient.
94          */
95         if ((curthread->poll_data.fds == NULL) ||
96             (curthread->poll_data.nfds < fd_count)) {
97                 data.fds = (struct pollfd *) realloc(curthread->poll_data.fds,
98                     sizeof(struct pollfd) * MAX(128, fd_count));
99                 if (data.fds == NULL) {
100                         errno = ENOMEM;
101                         ret = -1;
102                 }
103                 else {
104                         /*
105                          * Note that the threads poll data always
106                          * indicates what is allocated, not what is
107                          * currently being polled.
108                          */
109                         curthread->poll_data.fds = data.fds;
110                         curthread->poll_data.nfds = MAX(128, fd_count);
111                 }
112         }
113         if (ret == 0) {
114                 /* Setup the wait data. */
115                 data.fds = curthread->poll_data.fds;
116                 data.nfds = fd_count;
117
118                 /*
119                  * Setup the array of pollfds.  Optimize this by
120                  * running the loop in reverse and stopping when
121                  * the number of selected file descriptors is reached.
122                  */
123                 for (i = numfds - 1, pfd_index = fd_count - 1;
124                     (i >= 0) && (pfd_index >= 0); i--) {
125                         data.fds[pfd_index].events = 0;
126                         if (readfds && FD_ISSET(i, readfds)) {
127                                 data.fds[pfd_index].events = POLLRDNORM;
128                         }
129                         if (exceptfds && FD_ISSET(i, exceptfds)) {
130                                 data.fds[pfd_index].events |= POLLRDBAND;
131                         }
132                         if (writefds && FD_ISSET(i, writefds)) {
133                                 data.fds[pfd_index].events |= POLLWRNORM;
134                         }
135                         if (data.fds[pfd_index].events != 0) {
136                                 /*
137                                  * Set the file descriptor to be polled and
138                                  * clear revents in case of a timeout which
139                                  * leaves fds unchanged:
140                                  */
141                                 data.fds[pfd_index].fd = i;
142                                 data.fds[pfd_index].revents = 0;
143                                 pfd_index--;
144                         }
145                 }
146                 if (((ret = __sys_poll(data.fds, data.nfds, 0)) == 0) &&
147                    (f_wait != 0)) {
148                         curthread->data.poll_data = &data;
149                         curthread->interrupted = 0;
150                         _thread_kern_sched_state(PS_SELECT_WAIT, __FILE__, __LINE__);
151                         if (curthread->interrupted) {
152                                 errno = EINTR;
153                                 data.nfds = 0;
154                                 ret = -1;
155                         } else
156                                 ret = data.nfds;
157                 }
158         }
159
160         if (ret >= 0) {
161                 numfds = 0;
162                 for (i = 0; i < fd_count; i++) {
163                         /*
164                          * Check the results of the poll and clear
165                          * this file descriptor from the fdset if
166                          * the requested event wasn't ready.
167                          */
168
169                         /*
170                          * First check for invalid descriptor.
171                          * If found, set errno and return -1.
172                          */
173                         if (data.fds[i].revents & POLLNVAL) {
174                                 errno = EBADF;
175                                 return -1;
176                         }
177
178                         got_events = 0;
179                         if (readfds != NULL) {
180                                 if (FD_ISSET(data.fds[i].fd, readfds)) {
181                                         if ((data.fds[i].revents & (POLLIN
182                                             | POLLRDNORM | POLLERR
183                                             | POLLHUP | POLLNVAL)) != 0)
184                                                 got_events++;
185                                         else
186                                                 FD_CLR(data.fds[i].fd, readfds);
187                                 }
188                         }
189                         if (writefds != NULL) {
190                                 if (FD_ISSET(data.fds[i].fd, writefds)) {
191                                         if ((data.fds[i].revents & (POLLOUT
192                                             | POLLWRNORM | POLLWRBAND | POLLERR
193                                             | POLLHUP | POLLNVAL)) != 0)
194                                                 got_events++;
195                                         else
196                                                 FD_CLR(data.fds[i].fd,
197                                                     writefds);
198                                 }
199                         }
200                         if (exceptfds != NULL) {
201                                 if (FD_ISSET(data.fds[i].fd, exceptfds)) {
202                                         if (data.fds[i].revents & (POLLRDBAND |
203                                             POLLPRI))
204                                                 got_events++;
205                                         else
206                                                 FD_CLR(data.fds[i].fd,
207                                                     exceptfds);
208                                 }
209                         }
210                         if (got_events != 0)
211                                 numfds+=got_events;
212                 }
213                 ret = numfds;
214         }
215
216         return (ret);
217 }
218
219 int 
220 select(int numfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds,
221         struct timeval *timeout)
222 {
223         int ret;
224
225         _thread_enter_cancellation_point();
226         ret = _select(numfds, readfds, writefds, exceptfds, timeout);
227         _thread_leave_cancellation_point();
228
229         return ret;
230 }