xref: /third_party/NuttX/fs/vfs/fs_select.c (revision beacf11b)
1/****************************************************************************
2 * fs/vfs/fs_select.c
3 *
4 * Copyright (c) 2023 Huawei Device Co., Ltd. All rights reserved.
5 * Based on NuttX originally from nuttx source (nuttx/fs/ and nuttx/drivers/)
6 *
7 * Licensed under the Apache License, Version 2.0 (the "License");
8 * you may not use this file except in compliance with the License.
9 * You may obtain a copy of the License at
10 *
11 *     http://www.apache.org/licenses/LICENSE-2.0
12 *
13 * Unless required by applicable law or agreed to in writing, software
14 * distributed under the License is distributed on an "AS IS" BASIS,
15 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 * See the License for the specific language governing permissions and
17 * limitations under the License.
18 *
19 ****************************************************************************/
20
21/****************************************************************************
22 * Included Files
23 ****************************************************************************/
24
25#include "vfs_config.h"
26
27#include "sys/select.h"
28
29#include "string.h"
30#include "unistd.h"
31#include "poll.h"
32#include "assert.h"
33#include "errno.h"
34
35#include "stdlib.h"
36
37#include "los_signal.h"
38#include "los_syscall.h"
39
40#ifndef CONFIG_DISABLE_POLL
41
42
43/****************************************************************************
44 * Pre-processor Definitions
45 ****************************************************************************/
46
47#define POLL_IN_SET (POLLIN | POLLRDNORM | POLLRDBAND | POLLHUP | POLLERR)
48#define POLL_OUT_SET (POLLOUT | POLLWRBAND | POLLWRNORM | POLLERR)
49#define POLL_EX_SET (POLLPRI)
50
51/* pollfd count in stack, optimization in order to avoid small memory allocation */
52
53#define POLL_STACK_CNT 5
54
55/****************************************************************************
56 * Private Functions
57 ****************************************************************************/
58
59/****************************************************************************
60 * Public Functions
61 ****************************************************************************/
62extern unsigned int sleep(unsigned int seconds);
63
64/****************************************************************************
65 * Name: select
66 *
67 * Description:
68 *   select() allows a program to monitor multiple file descriptors, waiting
69 *   until one or more of the file descriptors become "ready" for some class
70 *   of I/O operation (e.g., input possible).  A file descriptor is
71 *   considered  ready if it is possible to perform the corresponding I/O
72 *   operation (e.g., read(2)) without blocking.
73 *
74 *   NOTE: poll() is the fundamental API for performing such monitoring
75 *   operation under NuttX.  select() is provided for compatibility and
76 *   is simply a layer of added logic on top of poll().  As such, select()
77 *   is more wasteful of resources and poll() is the recommended API to be
78 *   used.
79 *
80 * Input Parameters:
81 *   nfds - the maximum fd number (+1) of any descriptor in any of the
82 *     three sets.
83 *   readfds - the set of descriptions to monitor for read-ready events
84 *   writefds - the set of descriptions to monitor for write-ready events
85 *   exceptfds - the set of descriptions to monitor for error events
86 *   timeout - Return at this time if none of these events of interest
87 *     occur.
88 *
89 *  Returned Value:
90 *   0: Timer expired
91 *  >0: The number of bits set in the three sets of descriptors
92 *  -1: An error occurred (errno will be set appropriately)
93 *
94 ****************************************************************************/
95
96int do_select(int nfds, fd_set *readfds, fd_set *writefds,
97           fd_set *exceptfds, struct timeval *timeout, PollFun poll)
98{
99  struct pollfd *pollset = NULL;
100  struct pollfd pfd[POLL_STACK_CNT];
101  int pfd_alloc_flag = 0;
102  int fd;
103  int npfds;
104  int msec;
105  int ndx;
106  int ret;
107
108  if (nfds < 0 || nfds >= FD_SETSIZE)
109    {
110      set_errno(EINVAL);
111      return VFS_ERROR;
112    }
113
114  /* How many pollfd structures do we need to allocate? */
115
116  /* Initialize the descriptor list for poll() */
117
118  for (fd = 0, npfds = 0; fd < nfds; fd++)
119    {
120      /* Check if any monitor operation is requested on this fd */
121
122      if ((readfds   && FD_ISSET(fd, readfds))  ||
123          (writefds  && FD_ISSET(fd, writefds)) ||
124          (exceptfds && FD_ISSET(fd, exceptfds)))
125        {
126          /* Yes.. increment the count of pollfds structures needed */
127
128          npfds++;
129        }
130    }
131
132  /* Allocate the descriptor list for poll() */
133
134  if (npfds != 0)
135    {
136      /* use stack variable in order to avoid small memory allocation. */
137
138      if (npfds <= POLL_STACK_CNT)
139        {
140          pollset = pfd;
141          (void)memset_s(pollset, npfds * sizeof(struct pollfd), 0, npfds * sizeof(struct pollfd));
142        }
143      else
144        {
145          pollset = (struct pollfd *)zalloc(npfds * sizeof(struct pollfd));
146          if (pollset == NULL)
147            {
148              set_errno(ENOMEM);
149              return VFS_ERROR;
150            }
151          pfd_alloc_flag = 1;
152        }
153    }
154  else
155    {
156      /* If the readfds, writefds, and exceptfds arguments are all null pointers and
157       * the timeout argument is not a null pointer, the select() function shall block for
158       * the time specified. If the readfds, writefds, and exceptfds arguments are all
159       * null pointers and the timeout argument is a null pointer, this is NOT permitted
160       * as LiteOS doesn't support Signal machanism, so select() can't come back anymore.
161       */
162
163      if (timeout != NULL)
164        {
165          /* 1000000 : Convert seconds to microseconds. */
166
167          if ((long long)timeout->tv_sec * 1000000 > 0xffffffff)
168            {
169              (void)sleep(timeout->tv_sec);
170            }
171          else
172            {
173              (void)usleep(timeout->tv_sec * 1000000 + timeout->tv_usec);
174            }
175          return OK;
176        }
177      else
178        {
179          set_errno(EINVAL);
180          return VFS_ERROR;
181        }
182    }
183
184  /* Initialize the descriptor list for poll() */
185
186  for (fd = 0, ndx = 0; fd < nfds; fd++)
187    {
188      int incr = 0;
189
190      /* The readfs set holds the set of FDs that the caller can be assured
191       * of reading from without blocking.  Note that POLLHUP is included as
192       * a read-able condition.  POLLHUP will be reported at the end-of-file
193       * or when a connection is lost.  In either case, the read() can then
194       * be performed without blocking.
195       */
196
197      if (readfds && FD_ISSET(fd, readfds))
198        {
199          pollset[ndx].fd = fd;
200          pollset[ndx].events |= (POLLIN | POLLRDNORM);
201          incr = 1;
202        }
203
204      /* The writefds set holds the set of FDs that the caller can be assured
205       * of writing to without blocking.
206       */
207
208      if (writefds && FD_ISSET(fd, writefds))
209        {
210          pollset[ndx].fd      = fd;
211          pollset[ndx].events |= (POLLOUT | POLLWRNORM);
212          incr                 = 1;
213        }
214
215      /* The exceptfds set holds the set of FDs that are watched for exceptions */
216
217      if (exceptfds && FD_ISSET(fd, exceptfds))
218        {
219          pollset[ndx].fd      = fd;
220          incr                  = 1;
221        }
222
223      ndx += incr;
224    }
225
226  DEBUGASSERT(ndx == npfds);
227
228  /* Convert the timeout to milliseconds */
229
230  if (timeout)
231    {
232      /* Calculate the timeout in milliseconds */
233
234      msec = timeout->tv_sec * 1000 + timeout->tv_usec / 1000;
235    }
236  else
237    {
238      /* Any negative value of msec means no timeout */
239
240      msec = -1;
241    }
242
243  /* Then let poll do all of the real work. */
244
245  ret = poll(pollset, npfds, msec);
246
247  /* Now set up the return values */
248
249  if (readfds)
250    {
251      (void)memset_s(readfds, sizeof(fd_set), 0, sizeof(fd_set));
252    }
253
254  if (writefds)
255    {
256      (void)memset_s(writefds, sizeof(fd_set), 0, sizeof(fd_set));
257    }
258
259  if (exceptfds)
260    {
261      (void)memset_s(exceptfds, sizeof(fd_set), 0, sizeof(fd_set));
262    }
263
264  /* Convert the poll descriptor list back into selects 3 bitsets */
265
266  if (ret > 0)
267    {
268      ret = 0;
269      for (ndx = 0; ndx < npfds; ndx++)
270        {
271          /* Check for read conditions.  Note that POLLHUP is included as a
272           * read condition.  POLLHUP will be reported when no more data will
273           * be available (such as when a connection is lost).  In either
274           * case, the read() can then be performed without blocking.
275           */
276
277          if (readfds)
278            {
279              if (pollset[ndx].revents & POLL_IN_SET)
280                {
281                  FD_SET(pollset[ndx].fd, readfds);
282                  ret++;
283                }
284            }
285
286          /* Check for write conditions */
287
288          if (writefds)
289            {
290              if (pollset[ndx].revents & POLL_OUT_SET)
291                {
292                  FD_SET(pollset[ndx].fd, writefds);
293                  ret++;
294                }
295            }
296
297          /* Check for exceptions */
298
299          if (exceptfds)
300            {
301              if (pollset[ndx].revents & POLL_EX_SET)
302                {
303                  FD_SET(pollset[ndx].fd, exceptfds);
304                  ret++;
305                }
306            }
307        }
308    }
309
310  if (pfd_alloc_flag)
311    {
312      free(pollset);
313    }
314  return ret;
315}
316
317int select(int nfds, fd_set *readfds, fd_set *writefds,
318           fd_set *exceptfds, struct timeval *timeout)
319{
320  return do_select(nfds, readfds, writefds, exceptfds, timeout, poll);
321}
322
323#endif /* CONFIG_DISABLE_POLL */
324