Merge pull request #5714 from alexischr/update_bockbuild
[mono.git] / mono / metadata / threadpool-io-poll.c
1 /**
2  * \file
3  */
4
5 #include "utils/mono-poll.h"
6
7 static mono_pollfd *poll_fds;
8 static guint poll_fds_capacity;
9 static guint poll_fds_size;
10
11 static inline void
12 POLL_INIT_FD (mono_pollfd *poll_fd, gint fd, gint events)
13 {
14         poll_fd->fd = fd;
15         poll_fd->events = events;
16         poll_fd->revents = 0;
17 }
18
19 static gboolean
20 poll_init (gint wakeup_pipe_fd)
21 {
22         g_assert (wakeup_pipe_fd >= 0);
23
24         poll_fds_size = 1;
25         poll_fds_capacity = 64;
26
27         poll_fds = g_new0 (mono_pollfd, poll_fds_capacity);
28
29         POLL_INIT_FD (&poll_fds [0], wakeup_pipe_fd, MONO_POLLIN);
30
31         return TRUE;
32 }
33
34 static void
35 poll_register_fd (gint fd, gint events, gboolean is_new)
36 {
37         gint i;
38         gint poll_event;
39
40         g_assert (fd >= 0);
41         g_assert (poll_fds_size <= poll_fds_capacity);
42
43         g_assert ((events & ~(EVENT_IN | EVENT_OUT)) == 0);
44
45         poll_event = 0;
46         if (events & EVENT_IN)
47                 poll_event |= MONO_POLLIN;
48         if (events & EVENT_OUT)
49                 poll_event |= MONO_POLLOUT;
50
51         for (i = 0; i < poll_fds_size; ++i) {
52                 if (poll_fds [i].fd == fd) {
53                         g_assert (!is_new);
54                         POLL_INIT_FD (&poll_fds [i], fd, poll_event);
55                         return;
56                 }
57         }
58
59         g_assert (is_new);
60
61         for (i = 0; i < poll_fds_size; ++i) {
62                 if (poll_fds [i].fd == -1) {
63                         POLL_INIT_FD (&poll_fds [i], fd, poll_event);
64                         return;
65                 }
66         }
67
68         poll_fds_size += 1;
69
70         if (poll_fds_size > poll_fds_capacity) {
71                 poll_fds_capacity *= 2;
72                 g_assert (poll_fds_size <= poll_fds_capacity);
73
74                 poll_fds = (mono_pollfd *)g_renew (mono_pollfd, poll_fds, poll_fds_capacity);
75         }
76
77         POLL_INIT_FD (&poll_fds [poll_fds_size - 1], fd, poll_event);
78 }
79
80 static void
81 poll_remove_fd (gint fd)
82 {
83         gint i;
84
85         g_assert (fd >= 0);
86
87         for (i = 0; i < poll_fds_size; ++i) {
88                 if (poll_fds [i].fd == fd) {
89                         POLL_INIT_FD (&poll_fds [i], -1, 0);
90                         break;
91                 }
92         }
93
94         /* if we don't find the fd in poll_fds,
95          * it means we try to delete it twice */
96         g_assert (i < poll_fds_size);
97
98         /* if we find it again, it means we added
99          * it twice */
100         for (; i < poll_fds_size; ++i)
101                 g_assert (poll_fds [i].fd != fd);
102
103         /* reduce the value of poll_fds_size so we
104          * do not keep it too big */
105         while (poll_fds_size > 1 && poll_fds [poll_fds_size - 1].fd == -1)
106                 poll_fds_size -= 1;
107 }
108
109 static inline gint
110 poll_mark_bad_fds (mono_pollfd *poll_fds, gint poll_fds_size)
111 {
112         gint i, ready = 0;
113
114         for (i = 0; i < poll_fds_size; i++) {
115                 if (poll_fds [i].fd == -1)
116                         continue;
117
118                 switch (mono_poll (&poll_fds [i], 1, 0)) {
119                 case 1:
120                         ready++;
121                         break;
122                 case -1:
123                         if (errno == EBADF)
124                         {
125                                 poll_fds [i].revents |= MONO_POLLNVAL;
126                                 ready++;
127                         }
128                         break;
129                 }
130         }
131
132         return ready;
133 }
134
135 static gint
136 poll_event_wait (void (*callback) (gint fd, gint events, gpointer user_data), gpointer user_data)
137 {
138         gint i, ready;
139
140         for (i = 0; i < poll_fds_size; ++i)
141                 poll_fds [i].revents = 0;
142
143         mono_gc_set_skip_thread (TRUE);
144
145         MONO_ENTER_GC_SAFE;
146         ready = mono_poll (poll_fds, poll_fds_size, -1);
147         MONO_EXIT_GC_SAFE;
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                 switch (errno)
166                 {
167                 case EINTR:
168                 {
169                         ready = 0;
170                         break;
171                 }
172                 case EBADF:
173                 {
174                         ready = poll_mark_bad_fds (poll_fds, poll_fds_size);
175                         break;
176                 }
177                 default:
178                         g_error ("poll_event_wait: mono_poll () failed, error (%d) %s", errno, g_strerror (errno));
179                         break;
180                 }
181         }
182
183         if (ready == -1)
184                 return -1;
185         if (ready == 0)
186                 return 0;
187
188         g_assert (ready > 0);
189
190         for (i = 0; i < poll_fds_size; ++i) {
191                 gint fd, events = 0;
192
193                 if (poll_fds [i].fd == -1)
194                         continue;
195                 if (poll_fds [i].revents == 0)
196                         continue;
197
198                 fd = poll_fds [i].fd;
199                 if (poll_fds [i].revents & (MONO_POLLIN | MONO_POLLERR | MONO_POLLHUP | MONO_POLLNVAL))
200                         events |= EVENT_IN;
201                 if (poll_fds [i].revents & (MONO_POLLOUT | MONO_POLLERR | MONO_POLLHUP | MONO_POLLNVAL))
202                         events |= EVENT_OUT;
203                 if (poll_fds [i].revents & (MONO_POLLERR | MONO_POLLHUP | MONO_POLLNVAL))
204                         events |= EVENT_ERR;
205
206                 callback (fd, events, user_data);
207
208                 if (--ready == 0)
209                         break;
210         }
211
212         return 0;
213 }
214
215 static ThreadPoolIOBackend backend_poll = {
216         .init = poll_init,
217         .register_fd = poll_register_fd,
218         .remove_fd = poll_remove_fd,
219         .event_wait = poll_event_wait,
220 };