#include "il2cpp-config.h" #include "gc/GarbageCollector.h" #include "mono/ThreadPool/threadpool-ms-io-poll.h" #include "os/Socket.h" #include "utils/Memory.h" #include "vm/Thread.h" #include static std::vector *poll_fds; static unsigned int poll_fds_capacity; static unsigned int poll_fds_size; static inline void POLL_INIT_FD(il2cpp::os::PollRequest *poll_fd, int fd, il2cpp::os::PollFlags events) { poll_fd->fd = fd; poll_fd->events = events; poll_fd->revents = il2cpp::os::kPollFlagsNone; } bool poll_init(int wakeup_pipe_fd) { IL2CPP_ASSERT(wakeup_pipe_fd >= 0); poll_fds_size = 1; poll_fds_capacity = 64; poll_fds = new std::vector(poll_fds_capacity); POLL_INIT_FD(&(*poll_fds)[0], wakeup_pipe_fd, il2cpp::os::kPollFlagsIn); return true; } void poll_register_fd(int fd, int events, bool is_new) { unsigned int i; il2cpp::os::PollFlags poll_event; IL2CPP_ASSERT(fd >= 0); IL2CPP_ASSERT(poll_fds_size <= poll_fds_capacity); IL2CPP_ASSERT((events & ~(EVENT_IN | EVENT_OUT)) == 0); poll_event = il2cpp::os::kPollFlagsNone; if (events & EVENT_IN) poll_event |= il2cpp::os::kPollFlagsIn; if (events & EVENT_OUT) poll_event |= il2cpp::os::kPollFlagsOut; for (i = 0; i < poll_fds_size; ++i) { if ((*poll_fds)[i].fd == fd) { IL2CPP_ASSERT(!is_new); POLL_INIT_FD(&(*poll_fds)[i], fd, poll_event); return; } } IL2CPP_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; IL2CPP_ASSERT(poll_fds_size <= poll_fds_capacity); poll_fds->resize(poll_fds_capacity, il2cpp::os::PollRequest(-1)); } POLL_INIT_FD(&(*poll_fds)[poll_fds_size - 1], fd, poll_event); } void poll_remove_fd(int fd) { unsigned int i; IL2CPP_ASSERT(fd >= 0); for (i = 0; i < poll_fds_size; ++i) { if ((*poll_fds)[i].fd == fd) { POLL_INIT_FD(&(*poll_fds)[i], -1, il2cpp::os::kPollFlagsNone); break; } } /* if we don't find the fd in poll_fds, * it means we try to delete it twice */ IL2CPP_ASSERT(i < poll_fds_size); /* if we find it again, it means we added * it twice */ for (; i < poll_fds_size; ++i) IL2CPP_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 int poll_mark_bad_fds(std::vector *poll_fds, int poll_fds_size) { int i, ready = 0; int32_t result, error = 0; for (i = 0; i < poll_fds_size; i++) { if ((*poll_fds)[i].fd == -1) continue; il2cpp::os::WaitStatus status = il2cpp::os::Socket::Poll((*poll_fds)[i], 0, &result, &error); if (status == kWaitStatusFailure) { if ((il2cpp::os::SocketError)error == il2cpp::os::kInvalidHandle) { (*poll_fds)[i].revents |= il2cpp::os::kPollFlagsNVal; ready++; } } else if (result > 0) ready++; } return ready; } int poll_event_wait(void (*callback)(int fd, int events, void* user_data), void* user_data) { unsigned int i; for (i = 0; i < poll_fds_size; ++i) (*poll_fds)[i].revents = il2cpp::os::kPollFlagsNone; il2cpp::gc::GarbageCollector::SetSkipThread(true); int32_t ready; int32_t error; il2cpp::os::WaitStatus status = il2cpp::os::Socket::Poll((*poll_fds), poll_fds_size , -1, &ready, &error); il2cpp::gc::GarbageCollector::SetSkipThread(false); if (ready == -1 || status == kWaitStatusFailure) { /* * 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 * */ if ((il2cpp::os::SocketError)error == il2cpp::os::kInterrupted) { il2cpp::vm::Thread::CheckCurrentThreadForInterruptAndThrowIfNecessary(); ready = 0; } else if ((il2cpp::os::SocketError)error == il2cpp::os::kInvalidHandle) { ready = poll_mark_bad_fds(poll_fds, poll_fds_size); } } if (ready == -1) return -1; if (ready == 0) return 0; IL2CPP_ASSERT(ready > 0); for (i = 0; i < poll_fds_size; ++i) { int fd, events = 0; if ((*poll_fds)[i].fd == -1) continue; if ((*poll_fds)[i].revents == 0) continue; fd = (int)(*poll_fds)[i].fd; if ((*poll_fds)[i].revents & (il2cpp::os::kPollFlagsIn | il2cpp::os::kPollFlagsErr | il2cpp::os::kPollFlagsHup | il2cpp::os::kPollFlagsNVal)) events |= EVENT_IN; if ((*poll_fds)[i].revents & (il2cpp::os::kPollFlagsOut | il2cpp::os::kPollFlagsErr | il2cpp::os::kPollFlagsHup | il2cpp::os::kPollFlagsNVal)) events |= EVENT_OUT; if ((*poll_fds)[i].revents & (il2cpp::os::kPollFlagsErr | il2cpp::os::kPollFlagsHup | il2cpp::os::kPollFlagsNVal)) events |= EVENT_ERR; callback(fd, events, user_data); if (--ready == 0) break; } return 0; }