Merge pull request #1668 from alexanderkyte/bug1856
[mono.git] / mono / metadata / threadpool-ms-io-poll.c
1
2 #define POLL_NEVENTS 1024
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         guint i;
20
21         poll_fds_size = 1;
22         poll_fds_capacity = POLL_NEVENTS;
23         poll_fds = g_new0 (mono_pollfd, poll_fds_capacity);
24
25         POLL_INIT_FD (poll_fds, wakeup_pipe_fd, MONO_POLLIN);
26         for (i = 1; i < poll_fds_capacity; ++i)
27                 POLL_INIT_FD (poll_fds + i, -1, 0);
28
29         return TRUE;
30 }
31
32 static void
33 poll_cleanup (void)
34 {
35         g_free (poll_fds);
36 }
37
38 static inline gint
39 poll_mark_bad_fds (mono_pollfd *poll_fds, gint poll_fds_size)
40 {
41         gint i;
42         gint ret;
43         gint ready = 0;
44         mono_pollfd *poll_fd;
45
46         for (i = 0; i < poll_fds_size; i++) {
47                 poll_fd = poll_fds + i;
48                 if (poll_fd->fd == -1)
49                         continue;
50
51                 ret = mono_poll (poll_fd, 1, 0);
52                 if (ret == 1)
53                         ready++;
54                 if (ret == -1) {
55 #if !defined(HOST_WIN32)
56                         if (errno == EBADF)
57 #else
58                         if (WSAGetLastError () == WSAEBADF)
59 #endif
60                         {
61                                 poll_fd->revents |= MONO_POLLNVAL;
62                                 ready++;
63                         }
64                 }
65         }
66
67         return ready;
68 }
69
70 static void
71 poll_update_add (ThreadPoolIOUpdate *update)
72 {
73         gboolean found = FALSE;
74         gint j, k;
75
76         for (j = 1; j < poll_fds_size; ++j) {
77                 mono_pollfd *poll_fd = poll_fds + j;
78                 if (poll_fd->fd == update->fd) {
79                         found = TRUE;
80                         break;
81                 }
82         }
83
84         if (!found) {
85                 for (j = 1; j < poll_fds_capacity; ++j) {
86                         mono_pollfd *poll_fd = poll_fds + j;
87                         if (poll_fd->fd == -1)
88                                 break;
89                 }
90         }
91
92         if (j == poll_fds_capacity) {
93                 poll_fds_capacity += POLL_NEVENTS;
94                 poll_fds = g_renew (mono_pollfd, poll_fds, poll_fds_capacity);
95                 for (k = j; k < poll_fds_capacity; ++k)
96                         POLL_INIT_FD (poll_fds + k, -1, 0);
97         }
98
99         POLL_INIT_FD (poll_fds + j, update->fd, update->events);
100
101         if (j >= poll_fds_size)
102                 poll_fds_size = j + 1;
103 }
104
105 static gint
106 poll_event_wait (void)
107 {
108         gint ready;
109
110         ready = mono_poll (poll_fds, poll_fds_size, -1);
111         if (ready == -1) {
112                 /*
113                  * Apart from EINTR, we only check EBADF, for the rest:
114                  *  EINVAL: mono_poll() 'protects' us from descriptor
115                  *      numbers above the limit if using select() by marking
116                  *      then as MONO_POLLERR.  If a system poll() is being
117                  *      used, the number of descriptor we're passing will not
118                  *      be over sysconf(_SC_OPEN_MAX), as the error would have
119                  *      happened when opening.
120                  *
121                  *  EFAULT: we own the memory pointed by pfds.
122                  *  ENOMEM: we're doomed anyway
123                  *
124                  */
125 #if !defined(HOST_WIN32)
126                 switch (errno)
127 #else
128                 switch (WSAGetLastError ())
129 #endif
130                 {
131 #if !defined(HOST_WIN32)
132                 case EINTR:
133 #else
134                 case WSAEINTR:
135 #endif
136                         check_for_interruption_critical ();
137                         ready = 0;
138                         break;
139 #if !defined(HOST_WIN32)
140                 case EBADF:
141 #else
142                 case WSAEBADF:
143 #endif
144                         ready = poll_mark_bad_fds (poll_fds, poll_fds_size);
145                         break;
146                 default:
147 #if !defined(HOST_WIN32)
148                         g_warning ("poll_event_wait: mono_poll () failed, error (%d) %s", errno, g_strerror (errno));
149 #else
150                         g_warning ("poll_event_wait: mono_poll () failed, error (%d)\n", WSAGetLastError ());
151 #endif
152                         break;
153                 }
154         }
155
156         return ready;
157 }
158
159 static inline gint
160 poll_event_fd_at (guint i)
161 {
162         return poll_fds [i].fd;
163 }
164
165 static gint
166 poll_event_max (void)
167 {
168         return poll_fds_size;
169 }
170
171 static gboolean
172 poll_event_create_sockares_at (guint i, gint fd, MonoMList **list)
173 {
174         mono_pollfd *poll_fd;
175
176         g_assert (list);
177
178         poll_fd = &poll_fds [i];
179         g_assert (poll_fd);
180
181         g_assert (fd == poll_fd->fd);
182
183         if (fd == -1 || poll_fd->revents == 0)
184                 return FALSE;
185
186         if (*list && (poll_fd->revents & (MONO_POLLIN | MONO_POLLERR | MONO_POLLHUP | MONO_POLLNVAL)) != 0) {
187                 MonoSocketAsyncResult *io_event = get_sockares_for_event (list, MONO_POLLIN);
188                 if (io_event)
189                         mono_threadpool_ms_enqueue_work_item (((MonoObject*) io_event)->vtable->domain, (MonoObject*) io_event);
190         }
191         if (*list && (poll_fd->revents & (MONO_POLLOUT | MONO_POLLERR | MONO_POLLHUP | MONO_POLLNVAL)) != 0) {
192                 MonoSocketAsyncResult *io_event = get_sockares_for_event (list, MONO_POLLOUT);
193                 if (io_event)
194                         mono_threadpool_ms_enqueue_work_item (((MonoObject*) io_event)->vtable->domain, (MonoObject*) io_event);
195         }
196
197         if (*list)
198                 poll_fd->events = get_events (*list);
199         else
200                 POLL_INIT_FD (poll_fd, -1, 0);
201
202         return TRUE;
203 }
204
205 static ThreadPoolIOBackend backend_poll = {
206         .init = poll_init,
207         .cleanup = poll_cleanup,
208         .update_add = poll_update_add,
209         .event_wait = poll_event_wait,
210         .event_max = poll_event_max,
211         .event_fd_at = poll_event_fd_at,
212         .event_create_sockares_at = poll_event_create_sockares_at,
213 };