Merge pull request #2057 from directhex/monolite-on-jenkins
[mono.git] / mono / metadata / threadpool-ms-io-poll.c
1
2 #include "utils/mono-poll.h"
3
4 static mono_pollfd *poll_fds;
5 static guint poll_fds_capacity;
6 static guint poll_fds_size;
7
8 static inline void
9 POLL_INIT_FD (mono_pollfd *poll_fd, gint fd, gint events)
10 {
11         poll_fd->fd = fd;
12         poll_fd->events = events;
13         poll_fd->revents = 0;
14 }
15
16 static gboolean
17 poll_init (gint wakeup_pipe_fd)
18 {
19         g_assert (wakeup_pipe_fd >= 0);
20
21         poll_fds_size = 1;
22         poll_fds_capacity = 64;
23
24         poll_fds = g_new0 (mono_pollfd, poll_fds_capacity);
25
26         POLL_INIT_FD (&poll_fds [0], wakeup_pipe_fd, MONO_POLLIN);
27
28         return TRUE;
29 }
30
31 static void
32 poll_cleanup (void)
33 {
34         g_free (poll_fds);
35 }
36
37 static void
38 poll_register_fd (gint fd, gint events, gboolean is_new)
39 {
40         gint i;
41         gint poll_event;
42
43         g_assert (fd >= 0);
44         g_assert (poll_fds_size <= poll_fds_capacity);
45
46         g_assert ((events & ~(EVENT_IN | EVENT_OUT)) == 0);
47
48         poll_event = 0;
49         if (events & EVENT_IN)
50                 poll_event |= MONO_POLLIN;
51         if (events & EVENT_OUT)
52                 poll_event |= MONO_POLLOUT;
53
54         for (i = 0; i < poll_fds_size; ++i) {
55                 if (poll_fds [i].fd == fd) {
56                         g_assert (!is_new);
57                         POLL_INIT_FD (&poll_fds [i], fd, poll_event);
58                         return;
59                 }
60         }
61
62         g_assert (is_new);
63
64         for (i = 0; i < poll_fds_size; ++i) {
65                 if (poll_fds [i].fd == -1) {
66                         POLL_INIT_FD (&poll_fds [i], fd, poll_event);
67                         return;
68                 }
69         }
70
71         poll_fds_size += 1;
72
73         if (poll_fds_size > poll_fds_capacity) {
74                 poll_fds_capacity *= 2;
75                 g_assert (poll_fds_size <= poll_fds_capacity);
76
77                 poll_fds = g_renew (mono_pollfd, poll_fds, poll_fds_capacity);
78         }
79
80         POLL_INIT_FD (&poll_fds [poll_fds_size - 1], fd, poll_event);
81 }
82
83 static void
84 poll_remove_fd (gint fd)
85 {
86         gint i;
87
88         g_assert (fd >= 0);
89
90         for (i = 0; i < poll_fds_size; ++i) {
91                 if (poll_fds [i].fd == fd) {
92                         POLL_INIT_FD (&poll_fds [i], -1, 0);
93                         break;
94                 }
95         }
96
97         /* if we don't find the fd in poll_fds,
98          * it means we try to delete it twice */
99         g_assert (i < poll_fds_size);
100
101         /* if we find it again, it means we added
102          * it twice */
103         for (; i < poll_fds_size; ++i)
104                 g_assert (poll_fds [i].fd != fd);
105
106         /* reduce the value of poll_fds_size so we
107          * do not keep it too big */
108         while (poll_fds_size > 1 && poll_fds [poll_fds_size - 1].fd == -1)
109                 poll_fds_size -= 1;
110 }
111
112 static inline gint
113 poll_mark_bad_fds (mono_pollfd *poll_fds, gint poll_fds_size)
114 {
115         gint i, ready = 0;
116
117         for (i = 0; i < poll_fds_size; i++) {
118                 if (poll_fds [i].fd == -1)
119                         continue;
120
121                 switch (mono_poll (&poll_fds [i], 1, 0)) {
122                 case 1:
123                         ready++;
124                         break;
125                 case -1:
126 #if !defined(HOST_WIN32)
127                         if (errno == EBADF)
128 #else
129                         if (WSAGetLastError () == WSAEBADF)
130 #endif
131                         {
132                                 poll_fds [i].revents |= MONO_POLLNVAL;
133                                 ready++;
134                         }
135                         break;
136                 }
137         }
138
139         return ready;
140 }
141
142 static gint
143 poll_event_wait (void (*callback) (gint fd, gint events, gpointer user_data), gpointer user_data)
144 {
145         gint i, ready;
146
147         for (i = 0; i < poll_fds_size; ++i)
148                 poll_fds [i].revents = 0;
149
150         mono_gc_set_skip_thread (TRUE);
151
152         ready = mono_poll (poll_fds, poll_fds_size, -1);
153
154         mono_gc_set_skip_thread (FALSE);
155
156         if (ready == -1) {
157                 /*
158                  * Apart from EINTR, we only check EBADF, for the rest:
159                  *  EINVAL: mono_poll() 'protects' us from descriptor
160                  *      numbers above the limit if using select() by marking
161                  *      then as POLLERR.  If a system poll() is being
162                  *      used, the number of descriptor we're passing will not
163                  *      be over sysconf(_SC_OPEN_MAX), as the error would have
164                  *      happened when opening.
165                  *
166                  *  EFAULT: we own the memory pointed by pfds.
167                  *  ENOMEM: we're doomed anyway
168                  *
169                  */
170 #if !defined(HOST_WIN32)
171                 switch (errno)
172 #else
173                 switch (WSAGetLastError ())
174 #endif
175                 {
176 #if !defined(HOST_WIN32)
177                 case EINTR:
178 #else
179                 case WSAEINTR:
180 #endif
181                 {
182                         mono_thread_internal_check_for_interruption_critical (mono_thread_internal_current ());
183                         ready = 0;
184                         break;
185                 }
186 #if !defined(HOST_WIN32)
187                 case EBADF:
188 #else
189                 case WSAEBADF:
190 #endif
191                 {
192                         ready = poll_mark_bad_fds (poll_fds, poll_fds_size);
193                         break;
194                 }
195                 default:
196 #if !defined(HOST_WIN32)
197                         g_error ("poll_event_wait: mono_poll () failed, error (%d) %s", errno, g_strerror (errno));
198 #else
199                         g_error ("poll_event_wait: mono_poll () failed, error (%d)\n", WSAGetLastError ());
200 #endif
201                         break;
202                 }
203         }
204
205         if (ready == -1)
206                 return -1;
207         if (ready == 0)
208                 return 0;
209
210         g_assert (ready > 0);
211
212         for (i = 0; i < poll_fds_size; ++i) {
213                 gint fd, events = 0;
214
215                 if (poll_fds [i].fd == -1)
216                         continue;
217                 if (poll_fds [i].revents == 0)
218                         continue;
219
220                 fd = poll_fds [i].fd;
221                 if (poll_fds [i].revents & (MONO_POLLIN | MONO_POLLERR | MONO_POLLHUP | MONO_POLLNVAL))
222                         events |= EVENT_IN;
223                 if (poll_fds [i].revents & (MONO_POLLOUT | MONO_POLLERR | MONO_POLLHUP | MONO_POLLNVAL))
224                         events |= EVENT_OUT;
225                 if (poll_fds [i].revents & (MONO_POLLERR | MONO_POLLHUP | MONO_POLLNVAL))
226                         events |= EVENT_ERR;
227
228                 callback (fd, events, user_data);
229
230                 if (--ready == 0)
231                         break;
232         }
233
234         return 0;
235 }
236
237 static ThreadPoolIOBackend backend_poll = {
238         .init = poll_init,
239         .cleanup = poll_cleanup,
240         .register_fd = poll_register_fd,
241         .remove_fd = poll_remove_fd,
242         .event_wait = poll_event_wait,
243 };