/** * \file */ #include "utils/mono-poll.h" static mono_pollfd *poll_fds; static guint poll_fds_capacity; static guint poll_fds_size; static inline void POLL_INIT_FD (mono_pollfd *poll_fd, gint fd, gint events) { poll_fd->fd = fd; poll_fd->events = events; poll_fd->revents = 0; } static gboolean poll_init (gint wakeup_pipe_fd) { g_assert (wakeup_pipe_fd >= 0); poll_fds_size = 1; poll_fds_capacity = 64; poll_fds = g_new0 (mono_pollfd, poll_fds_capacity); POLL_INIT_FD (&poll_fds [0], wakeup_pipe_fd, MONO_POLLIN); return TRUE; } static void poll_register_fd (gint fd, gint events, gboolean is_new) { gint i; gint poll_event; g_assert (fd >= 0); g_assert (poll_fds_size <= poll_fds_capacity); g_assert ((events & ~(EVENT_IN | EVENT_OUT)) == 0); poll_event = 0; if (events & EVENT_IN) poll_event |= MONO_POLLIN; if (events & EVENT_OUT) poll_event |= MONO_POLLOUT; for (i = 0; i < poll_fds_size; ++i) { if (poll_fds [i].fd == fd) { g_assert (!is_new); POLL_INIT_FD (&poll_fds [i], fd, poll_event); return; } } g_assert (is_new); for (i = 0; i < poll_fds_size; ++i) { if (poll_fds [i].fd == -1) { POLL_INIT_FD (&poll_fds [i], fd, poll_event); return; } } poll_fds_size += 1; if (poll_fds_size > poll_fds_capacity) { poll_fds_capacity *= 2; g_assert (poll_fds_size <= poll_fds_capacity); poll_fds = (mono_pollfd *)g_renew (mono_pollfd, poll_fds, poll_fds_capacity); } POLL_INIT_FD (&poll_fds [poll_fds_size - 1], fd, poll_event); } static void poll_remove_fd (gint fd) { gint i; g_assert (fd >= 0); for (i = 0; i < poll_fds_size; ++i) { if (poll_fds [i].fd == fd) { POLL_INIT_FD (&poll_fds [i], -1, 0); break; } } /* if we don't find the fd in poll_fds, * it means we try to delete it twice */ g_assert (i < poll_fds_size); /* if we find it again, it means we added * it twice */ for (; i < poll_fds_size; ++i) g_assert (poll_fds [i].fd != fd); /* reduce the value of poll_fds_size so we * do not keep it too big */ while (poll_fds_size > 1 && poll_fds [poll_fds_size - 1].fd == -1) poll_fds_size -= 1; } static inline gint poll_mark_bad_fds (mono_pollfd *poll_fds, gint poll_fds_size) { gint i, ready = 0; for (i = 0; i < poll_fds_size; i++) { if (poll_fds [i].fd == -1) continue; switch (mono_poll (&poll_fds [i], 1, 0)) { case 1: ready++; break; case -1: if (errno == EBADF) { poll_fds [i].revents |= MONO_POLLNVAL; ready++; } break; } } return ready; } static gint poll_event_wait (void (*callback) (gint fd, gint events, gpointer user_data), gpointer user_data) { gint i, ready; for (i = 0; i < poll_fds_size; ++i) poll_fds [i].revents = 0; mono_gc_set_skip_thread (TRUE); MONO_ENTER_GC_SAFE; ready = mono_poll (poll_fds, poll_fds_size, -1); MONO_EXIT_GC_SAFE; mono_gc_set_skip_thread (FALSE); if (ready == -1) { /* * Apart from EINTR, we only check EBADF, for the rest: * EINVAL: mono_poll() 'protects' us from descriptor * numbers above the limit if using select() by marking * then as POLLERR. If a system poll() is being * used, the number of descriptor we're passing will not * be over sysconf(_SC_OPEN_MAX), as the error would have * happened when opening. * * EFAULT: we own the memory pointed by pfds. * ENOMEM: we're doomed anyway * */ switch (errno) { case EINTR: { ready = 0; break; } case EBADF: { ready = poll_mark_bad_fds (poll_fds, poll_fds_size); break; } default: g_error ("poll_event_wait: mono_poll () failed, error (%d) %s", errno, g_strerror (errno)); break; } } if (ready == -1) return -1; if (ready == 0) return 0; g_assert (ready > 0); for (i = 0; i < poll_fds_size; ++i) { gint fd, events = 0; if (poll_fds [i].fd == -1) continue; if (poll_fds [i].revents == 0) continue; fd = poll_fds [i].fd; if (poll_fds [i].revents & (MONO_POLLIN | MONO_POLLERR | MONO_POLLHUP | MONO_POLLNVAL)) events |= EVENT_IN; if (poll_fds [i].revents & (MONO_POLLOUT | MONO_POLLERR | MONO_POLLHUP | MONO_POLLNVAL)) events |= EVENT_OUT; if (poll_fds [i].revents & (MONO_POLLERR | MONO_POLLHUP | MONO_POLLNVAL)) events |= EVENT_ERR; callback (fd, events, user_data); if (--ready == 0) break; } return 0; } static ThreadPoolIOBackend backend_poll = { .init = poll_init, .register_fd = poll_register_fd, .remove_fd = poll_remove_fd, .event_wait = poll_event_wait, };