size_t network_poll(network_poll_t* pollobj, network_poll_event_t* events, size_t capacity, unsigned int timeoutms) { int avail = 0; size_t num_events = 0; #if FOUNDATION_PLATFORM_WINDOWS //TODO: Refactor to keep fd_set across loop and rebuild on change (add/remove) int num_fd = 0; int ret = 0; size_t islot; fd_set fdread, fdwrite, fderr; #endif if (!pollobj->num_sockets) return num_events; #if FOUNDATION_PLATFORM_APPLE int ret = poll(pollobj->pollfds, pollobj->num_sockets, timeoutms); #elif FOUNDATION_PLATFORM_LINUX || FOUNDATION_PLATFORM_ANDROID int ret = epoll_wait(pollobj->fd_poll, pollobj->events, pollobj->num_sockets + 1, timeoutms); int num_polled = ret; #elif FOUNDATION_PLATFORM_WINDOWS FD_ZERO(&fdread); FD_ZERO(&fdwrite); FD_ZERO(&fderr); for (islot = 0; islot < pollobj->num_sockets; ++islot) { int fd = pollobj->slots[islot].fd; socket_base_t* sockbase = _socket_base + pollobj->slots[islot].base; FD_SET(fd, &fdread); if (sockbase->state == SOCKETSTATE_CONNECTING) FD_SET(fd, &fdwrite); FD_SET(fd, &fderr); if (fd >= num_fd) num_fd = fd + 1; } if (!num_fd) { return num_events; } else { struct timeval tv; tv.tv_sec = timeoutms / 1000; tv.tv_usec = (timeoutms % 1000) * 1000; ret = select(num_fd, &fdread, &fdwrite, &fderr, &tv); } #else # error Not implemented #endif if (ret < 0) { int err = NETWORK_SOCKET_ERROR; string_const_t errmsg = system_error_message(err); log_warnf(HASH_NETWORK, WARNING_SUSPICIOUS, STRING_CONST("Error in socket poll: %.*s (%d)"), STRING_FORMAT(errmsg), err); if (!avail) return num_events; ret = avail; } if (!avail && !ret) return num_events; #if FOUNDATION_PLATFORM_APPLE struct pollfd* pfd = pollobj->pollfds; network_poll_slot_t* slot = pollobj->slots; for (size_t i = 0; i < pollobj->num_sockets; ++i, ++pfd, ++slot) { socket_t* sock = slot->sock; socket_base_t* sockbase = _socket_base + slot->base; int fd = slot->fd; if (pfd->revents & POLLIN) { if (sockbase->state == SOCKETSTATE_LISTENING) { network_poll_push_event(events, capacity, num_events, NETWORKEVENT_CONNECTION, sock); } else { network_poll_push_event(events, capacity, num_events, NETWORKEVENT_DATAIN, sock); } } if ((sockbase->state == SOCKETSTATE_CONNECTING) && (pfd->revents & POLLOUT)) { sockbase->state = SOCKETSTATE_CONNECTED; pfd->events = POLLIN | POLLERR | POLLHUP; network_poll_push_event(events, capacity, num_events, NETWORKEVENT_CONNECTED, sock); } if (pfd->revents & POLLERR) { pfd->events = POLLOUT | POLLERR | POLLHUP; network_poll_push_event(events, capacity, num_events, NETWORKEVENT_ERROR, sock); socket_close(sock); } if (pfd->revents & POLLHUP) { pfd->events = POLLOUT | POLLERR | POLLHUP; network_poll_push_event(events, capacity, num_events, NETWORKEVENT_HANGUP, sock); socket_close(sock); } } #elif FOUNDATION_PLATFORM_LINUX || FOUNDATION_PLATFORM_ANDROID struct epoll_event* event = pollobj->events; for (int i = 0; i < num_polled; ++i, ++event) { FOUNDATION_ASSERT(pollobj->slots[ event->data.fd ].base >= 0); socket_t* sock = pollobj->slots[ event->data.fd ].sock; socket_base_t* sockbase = _socket_base + pollobj->slots[ event->data.fd ].base; int fd = pollobj->slots[ event->data.fd ].fd; if (event->events & EPOLLIN) { if (sockbase->state == SOCKETSTATE_LISTENING) { network_poll_push_event(events, capacity, num_events, NETWORKEVENT_CONNECTION, sock); } else { network_poll_push_event(events, capacity, num_events, NETWORKEVENT_DATAIN, sock); } } if ((sockbase->state == SOCKETSTATE_CONNECTING) && (event->events & EPOLLOUT)) { sockbase->state = SOCKETSTATE_CONNECTED; struct epoll_event mod_event; mod_event.events = EPOLLIN | EPOLLERR | EPOLLHUP; mod_event.data.fd = event->data.fd; epoll_ctl(pollobj->fd_poll, EPOLL_CTL_MOD, fd, &mod_event); network_poll_push_event(events, capacity, num_events, NETWORKEVENT_CONNECTED, sock); } if (event->events & EPOLLERR) { struct epoll_event del_event; epoll_ctl(pollobj->fd_poll, EPOLL_CTL_DEL, fd, &del_event); network_poll_push_event(events, capacity, num_events, NETWORKEVENT_ERROR, sock); socket_close(sock); } if (event->events & EPOLLHUP) { struct epoll_event del_event; epoll_ctl(pollobj->fd_poll, EPOLL_CTL_DEL, fd, &del_event); network_poll_push_event(events, capacity, num_events, NETWORKEVENT_HANGUP, sock); socket_close(sock); } } #elif FOUNDATION_PLATFORM_WINDOWS for (islot = 0; islot < pollobj->num_sockets; ++islot) { int fd = pollobj->slots[islot].fd; socket_t* sock = pollobj->slots[islot].sock; socket_base_t* sockbase = _socket_base + pollobj->slots[islot].base; if (sockbase->fd != fd) continue; if (FD_ISSET(fd, &fdread)) { if (sockbase->state == SOCKETSTATE_LISTENING) { network_poll_push_event(events, capacity, num_events, NETWORKEVENT_CONNECTION, sock); } else { //SOCKETSTATE_CONNECTED network_poll_push_event(events, capacity, num_events, NETWORKEVENT_DATAIN, sock); } } if ((sockbase->state == SOCKETSTATE_CONNECTING) && FD_ISSET(fd, &fdwrite)) { sockbase->state = SOCKETSTATE_CONNECTED; network_poll_push_event(events, capacity, num_events, NETWORKEVENT_CONNECTED, sock); } if (FD_ISSET(fd, &fderr)) { network_poll_push_event(events, capacity, num_events, NETWORKEVENT_HANGUP, sock); socket_close(sock); } } #else # error Not implemented #endif return num_events; }
size_t network_poll(network_poll_t* pollobj, network_poll_event_t* events, size_t capacity, unsigned int timeoutms) { int avail = 0; size_t num_events = 0; #if FOUNDATION_PLATFORM_WINDOWS //TODO: Refactor to keep fd_set across loop and rebuild on change (add/remove) int num_fd = 0; int ret = 0; size_t islot; fd_set fdread, fdwrite, fderr; #endif if (!pollobj->num_sockets) return num_events; #if FOUNDATION_PLATFORM_APPLE int ret = poll(pollobj->pollfds, (nfds_t)pollobj->num_sockets, (int)timeoutms); #elif FOUNDATION_PLATFORM_LINUX || FOUNDATION_PLATFORM_ANDROID int ret = epoll_wait(pollobj->fd_poll, pollobj->events, (int)pollobj->num_sockets + 1, (int)timeoutms); int num_polled = ret; #elif FOUNDATION_PLATFORM_WINDOWS FD_ZERO(&fdread); FD_ZERO(&fdwrite); FD_ZERO(&fderr); for (islot = 0; islot < pollobj->num_sockets; ++islot) { int fd = pollobj->slots[islot].fd; if (fd != NETWORK_SOCKET_INVALID) { socket_t* sock = pollobj->slots[islot].sock; FD_SET(fd, &fdread); if (sock->state == SOCKETSTATE_CONNECTING) FD_SET(fd, &fdwrite); FD_SET(fd, &fderr); if (fd >= num_fd) num_fd = fd + 1; } } if (!num_fd) { return num_events; } else { struct timeval tv; tv.tv_sec = timeoutms / 1000; tv.tv_usec = (timeoutms % 1000) * 1000; ret = select(num_fd, &fdread, &fdwrite, &fderr, (timeoutms != NETWORK_TIMEOUT_INFINITE) ? &tv : nullptr); } #else # error Not implemented #endif if (ret < 0) { int err = NETWORK_SOCKET_ERROR; string_const_t errmsg = system_error_message(err); log_warnf(HASH_NETWORK, WARNING_SUSPICIOUS, STRING_CONST("Error in socket poll: %.*s (%d)"), STRING_FORMAT(errmsg), err); if (!avail) return num_events; ret = avail; } if (!avail && !ret) return num_events; #if FOUNDATION_PLATFORM_APPLE struct pollfd* pfd = pollobj->pollfds; network_poll_slot_t* slot = pollobj->slots; for (size_t islot = 0; islot < pollobj->num_sockets; ++islot, ++pfd, ++slot) { socket_t* sock = slot->sock; bool update_slot = false; bool had_error = false; if (pfd->revents & POLLERR) { update_slot = true; had_error = true; network_poll_push_event(events, capacity, num_events, NETWORKEVENT_ERROR, sock); socket_close(sock); } if (pfd->revents & POLLHUP) { update_slot = true; had_error = true; network_poll_push_event(events, capacity, num_events, NETWORKEVENT_HANGUP, sock); socket_close(sock); } if (!had_error && (pfd->revents & POLLIN)) { if (sock->state == SOCKETSTATE_LISTENING) { network_poll_push_event(events, capacity, num_events, NETWORKEVENT_CONNECTION, sock); } else { network_poll_push_event(events, capacity, num_events, NETWORKEVENT_DATAIN, sock); } } if (!had_error && (sock->state == SOCKETSTATE_CONNECTING) && (pfd->revents & POLLOUT)) { int serr = 0; socklen_t slen = sizeof(int); getsockopt(sock->fd, SOL_SOCKET, SO_ERROR, (void*)&serr, &slen); if (!serr) { sock->state = SOCKETSTATE_CONNECTED; network_poll_push_event(events, capacity, num_events, NETWORKEVENT_CONNECTED, sock); } else { had_error = true; network_poll_push_event(events, capacity, num_events, NETWORKEVENT_ERROR, sock); socket_close(sock); } update_slot = true; } if (update_slot) network_poll_update_slot(pollobj, islot, sock); } #elif FOUNDATION_PLATFORM_LINUX || FOUNDATION_PLATFORM_ANDROID struct epoll_event* event = pollobj->events; for (int i = 0; i < num_polled; ++i, ++event) { socket_t* sock = pollobj->slots[ event->data.fd ].sock; bool update_slot = false; bool had_error = false; if (event->events & EPOLLERR) { update_slot = true; had_error = true; network_poll_push_event(events, capacity, num_events, NETWORKEVENT_ERROR, sock); socket_close(sock); } if (event->events & EPOLLHUP) { update_slot = true; had_error = true; network_poll_push_event(events, capacity, num_events, NETWORKEVENT_HANGUP, sock); socket_close(sock); } if (!had_error && (event->events & EPOLLIN)) { if (sock->state == SOCKETSTATE_LISTENING) { network_poll_push_event(events, capacity, num_events, NETWORKEVENT_CONNECTION, sock); } else { network_poll_push_event(events, capacity, num_events, NETWORKEVENT_DATAIN, sock); } } if (!had_error && (sock->state == SOCKETSTATE_CONNECTING) && (event->events & EPOLLOUT)) { int serr = 0; socklen_t slen = sizeof(int); getsockopt(sock->fd, SOL_SOCKET, SO_ERROR, (void*)&serr, &slen); if (!serr) { sock->state = SOCKETSTATE_CONNECTED; network_poll_push_event(events, capacity, num_events, NETWORKEVENT_CONNECTED, sock); } else { had_error = true; network_poll_push_event(events, capacity, num_events, NETWORKEVENT_ERROR, sock); socket_close(sock); } update_slot = true; } if (update_slot) network_poll_update_slot(pollobj, (size_t)event->data.fd, sock); } #elif FOUNDATION_PLATFORM_WINDOWS for (islot = 0; islot < pollobj->num_sockets; ++islot) { int fd = pollobj->slots[islot].fd; socket_t* sock = pollobj->slots[islot].sock; bool update_slot = false; if (FD_ISSET(fd, &fdread)) { if (sock->state == SOCKETSTATE_LISTENING) { network_poll_push_event(events, capacity, num_events, NETWORKEVENT_CONNECTION, sock); } else { //SOCKETSTATE_CONNECTED network_poll_push_event(events, capacity, num_events, NETWORKEVENT_DATAIN, sock); } } if ((sock->state == SOCKETSTATE_CONNECTING) && FD_ISSET(fd, &fdwrite)) { update_slot = true; sock->state = SOCKETSTATE_CONNECTED; network_poll_push_event(events, capacity, num_events, NETWORKEVENT_CONNECTED, sock); } if (FD_ISSET(fd, &fderr)) { update_slot = true; network_poll_push_event(events, capacity, num_events, NETWORKEVENT_HANGUP, sock); socket_close(sock); } if (update_slot) network_poll_update_slot(pollobj, islot, sock); } #else # error Not implemented #endif return num_events; }