Add a .clang-format
[akaros.git] / user / iplib / select.c
1 /* Copyright (c) 2016 Google Inc.
2  * Barret Rhoden <brho@cs.berkeley.edu>
3  * See LICENSE for details.
4  *
5  * select()
6  *
7  * Our select() is super spurious and will only work with apps that use
8  * non-blocking I/O.
9  *
10  * Under the hood, our select() is implemented with epoll (and under that, FD
11  * taps).  Those can only detect edges (e.g. a socket becomes readable).
12  *
13  * The problem is that we want to detect a level status (e.g. socket is
14  * readable) with an edge event (e.g. socket *becomes* readable).  To do this,
15  * when someone initially selects, the FD gets tracked with epoll and we
16  * immediately return saying the FD is ready for whatever they asked for.  This
17  * is usually not true, and the application will need to poll all of its FDs
18  * once after the initial select() call.  Subsequent selects() will still be
19  * tracking the FD in the epoll set.  If any edge events that come after the
20  * poll (which eventually returns EAGAIN) will be caught by epoll, and a
21  * subsequent select will wake up (or never block in the first place) due to the
22  * reception of that edge event.
23  *
24  * We maintain one FD set per program.  It tracks *any* FD being tracked by
25  * *any* select call.  Regardless of whether the user asked for
26  * read/write/except, the FD gets watched for anything until it closes.  This
27  * will result in spurious wakeups.
28  *
29  * One issue with the global FD set is that one thread may consume the epoll
30  * events intended for another thread.  To get around this, only one thread is
31  * the actual epoller, and the others block on a mutex.  An alternative is to
32  * use a per-thread FD set, using TLS, but not every 2LS uses TLS, and
33  * performance is not a concern for code using select().
34  *
35  * Notes:
36  * - pselect might be racy
37  * - if the user has no read/write/except sets, we won't wait.  some users of
38  *   select use it as a timer only.  if that comes up, we can expand this.
39  * - if you epoll or FD tap an FD, then try to use select on it, you'll get an
40  *   error (only one tap per FD).  select() only knows about the FDs in its set.
41  */
42
43 #include <sys/select.h>
44 #include <sys/time.h>
45 #include <sys/types.h>
46 #include <unistd.h>
47
48 #include <ros/common.h>
49 #include <parlib/uthread.h>
50 #include <sys/close_cb.h>
51 #include <sys/epoll.h>
52 #include <malloc.h>
53 #include <stdlib.h>
54 #include <errno.h>
55 #include <signal.h>
56
57 static int epoll_fd;
58 static fd_set all_fds;
59 static uth_mutex_t fdset_mtx;
60 static struct uthread *owner;
61 static uth_mutex_t sleep_mtx;
62
63 static bool fd_is_set(unsigned int fd, fd_set *set)
64 {
65         if (fd > FD_SETSIZE)
66                 return FALSE;
67         if (!set)
68                 return FALSE;
69         return FD_ISSET(fd, set);
70 }
71
72 static void select_fd_closed(int fd)
73 {
74         /* Slightly racy, but anything concurrently added will be closed later, and
75          * after it is_set. */
76         if (!fd_is_set(fd, &all_fds))
77                 return;
78         /* We just need to stop tracking FD.  We do not need to remove it from the
79          * epoll set, since that will happen automatically on close(). */
80         uth_mutex_lock(fdset_mtx);
81         FD_CLR(fd, &all_fds);
82         uth_mutex_unlock(fdset_mtx);
83 }
84
85 static void select_init(void)
86 {
87         static struct close_cb select_close_cb = {.func = select_fd_closed};
88
89         register_close_cb(&select_close_cb);
90         epoll_fd = epoll_create(FD_SETSIZE);
91         if (epoll_fd < 0) {
92                 perror("select failed epoll_create");
93                 exit(-1);
94         }
95         fdset_mtx = uth_mutex_alloc();
96         sleep_mtx = uth_mutex_alloc();
97 }
98
99 static int select_tv_to_ep_timeout(struct timeval *tv)
100 {
101         if (!tv)
102                 return -1;
103         return tv->tv_sec * 1000 + DIV_ROUND_UP(tv->tv_usec, 1000);
104 }
105
106 int select(int nfds, fd_set *readfds, fd_set *writefds,
107            fd_set *exceptfds, struct timeval *timeout)
108 {
109         bool changed_set = FALSE;
110         struct epoll_event ep_ev;
111         struct epoll_event *ep_results;
112         int ep_timeout = select_tv_to_ep_timeout(timeout);
113
114         run_once(select_init());
115         /* good thing nfds is a signed int... */
116         if (nfds < 0) {
117                 errno = EINVAL;
118                 return -1;
119         }
120         uth_mutex_lock(fdset_mtx);
121         for (int i = 0; i < nfds; i++) {
122                 if ((fd_is_set(i, readfds) || fd_is_set(i, writefds) ||
123                     fd_is_set(i, exceptfds)) && !fd_is_set(i, &all_fds)) {
124
125                         changed_set = TRUE;
126                         FD_SET(i, &all_fds);
127                         /* FDs that we track for *any* reason with select will be
128                          * tracked for *all* reasons with epoll. */
129                         ep_ev.events = EPOLLET | EPOLLIN | EPOLLOUT | EPOLLHUP |
130                                        EPOLLERR;
131                         ep_ev.data.fd = i;
132                         if (epoll_ctl(epoll_fd, EPOLL_CTL_ADD, i, &ep_ev)) {
133                                 /* We might have failed because we tried to set up too many
134                                  * FD tap types.  Listen FDs, for instance, can only be
135                                  * tapped for READABLE and HANGUP.  Let's try for one of
136                                  * those. */
137                                 if (errno == ENOSYS) {
138                                         ep_ev.events = EPOLLET | EPOLLIN | EPOLLHUP;
139                                         if (!epoll_ctl(epoll_fd, EPOLL_CTL_ADD, i, &ep_ev))
140                                                 continue;
141                                 }
142                                 /* Careful to unlock before calling perror.  perror calls
143                                  * close, which calls our CB, which grabs the lock. */
144                                 uth_mutex_unlock(fdset_mtx);
145                                 perror("select epoll_ctl failed");
146                                 return -1;
147                         }
148                 }
149         }
150         uth_mutex_unlock(fdset_mtx);
151         /* Since we just added some FD to our tracking set, we don't know if its
152          * readable or not.  We'll only catch edge-triggered changes in the future.
153          * We can spuriously tell the user all FDs are ready, and next time they
154          * can block until there is edge activity. */
155         if (changed_set)
156                 return nfds;
157         /* Since there is a global epoll set, we could have multiple threads
158          * epolling at a time and one thread could consume the events that should
159          * wake another thread.  We don't know when the 'other' thread last polled,
160          * so we'll need to assume its event was consumed and just return.  If a
161          * thread selects again and no other thread has since selected, then we know
162          * no one consumed the events.  We use an 'owner' to track which thread most
163          * recently selected.  We use a mutex so that the extra threads sleep. */
164         uth_mutex_lock(sleep_mtx);
165         if (owner != current_uthread) {
166                 /* Could thrash, if we fight with another uth for owner */
167                 owner = current_uthread;
168                 uth_mutex_unlock(sleep_mtx);
169                 return nfds;
170         }
171         /* Need to check for up to FD_SETSIZE - nfds isn't the size of all FDs
172          * tracked; it's the size of only our current select call */
173         ep_results = malloc(sizeof(struct epoll_event) * FD_SETSIZE);
174         if (!ep_results) {
175                 uth_mutex_unlock(sleep_mtx);
176                 errno = ENOMEM;
177                 return -1;
178         }
179         /* Don't care which ones were set; we'll just tell the user they all were
180          * set.  If they can't handle that, this whole plan won't work. */
181         epoll_wait(epoll_fd, ep_results, FD_SETSIZE, ep_timeout);
182         uth_mutex_unlock(sleep_mtx);
183         free(ep_results);
184         /* TODO: consider updating timeval.  It's not mandatory (POSIX). */
185         return nfds;
186 }
187
188 int pselect(int nfds, fd_set *readfds, fd_set *writefds,
189             fd_set *exceptfds, const struct timespec *timeout,
190             const sigset_t *sigmask)
191 {
192         int ready;
193         sigset_t origmask;
194         struct timeval local_tv, *tv = &local_tv;
195
196         if (!timeout) {
197                 tv = 0;
198         } else {
199                 tv->tv_sec = timeout->tv_sec;
200                 tv->tv_usec = DIV_ROUND_UP(timeout->tv_nsec, 1000);
201         }
202         /* TODO: this is probably racy */
203         sigprocmask(SIG_SETMASK, sigmask, &origmask);
204         ready = select(nfds, readfds, writefds, exceptfds, tv);
205         sigprocmask(SIG_SETMASK, &origmask, NULL);
206         return ready;
207 }