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