1// SPDX-License-Identifier: GPL-2.0 2// 3// waiter-select.c - Waiter for event notification by select(2). 4// 5// Copyright (c) 2018 Takashi Sakamoto <o-takashi@sakamocchi.jp> 6// 7// Licensed under the terms of the GNU General Public License, version 2. 8 9#include "waiter.h" 10 11#include <stdlib.h> 12#include <unistd.h> 13#include <string.h> 14#include <errno.h> 15#include <sys/select.h> 16 17// Except for POLLERR. 18#ifdef POLLRDNORM 19// This program is for userspace compliant to POSIX 2008 (IEEE 1003.1:2008). 20// This is the default compliance level since glibc-2.12 or later. 21# define POLLIN_SET (POLLRDNORM | POLLRDBAND | POLLIN | POLLHUP) 22# define POLLOUT_SET (POLLWRBAND | POLLWRNORM | POLLOUT) 23#else 24// However it's allowed to be for old compliance levels. 25# define POLLIN_SET (POLLIN | POLLHUP) 26# define POLLOUT_SET (POLLOUT) 27#endif 28#define POLLEX_SET (POLLPRI) 29 30 31struct select_state { 32 fd_set rfds_rd; 33 fd_set rfds_wr; 34 fd_set rfds_ex; 35}; 36 37static int select_prepare(struct waiter_context *waiter ATTRIBUTE_UNUSED) 38{ 39 return 0; 40} 41 42static int select_wait_event(struct waiter_context *waiter, int timeout_msec) 43{ 44 struct select_state *state = waiter->private_data; 45 struct pollfd *pfd; 46 int fd_max; 47 struct timeval tv, *tv_ptr; 48 int i; 49 int err; 50 51 FD_ZERO(&state->rfds_rd); 52 FD_ZERO(&state->rfds_wr); 53 FD_ZERO(&state->rfds_ex); 54 55 fd_max = 0; 56 for (i = 0; i < (int)waiter->pfd_count; ++i) { 57 pfd = &waiter->pfds[i]; 58 59 if (pfd->events & POLLIN_SET) 60 FD_SET(pfd->fd, &state->rfds_rd); 61 if (pfd->events & POLLOUT_SET) 62 FD_SET(pfd->fd, &state->rfds_wr); 63 if (pfd->events & POLLEX_SET) 64 FD_SET(pfd->fd, &state->rfds_ex); 65 if (pfd->fd > fd_max) 66 fd_max = pfd->fd; 67 } 68 69 if (timeout_msec < 0) { 70 tv_ptr = NULL; 71 } else { 72 tv.tv_sec = 0; 73 tv.tv_usec = timeout_msec * 1000; 74 tv_ptr = &tv; 75 } 76 77 err = select(fd_max + 1, &state->rfds_rd, &state->rfds_wr, 78 &state->rfds_ex, tv_ptr); 79 if (err < 0) 80 return -errno; 81 82 for (i = 0; i < (int)waiter->pfd_count; ++i) { 83 pfd = &waiter->pfds[i]; 84 85 pfd->revents = 0; 86 if (FD_ISSET(pfd->fd, &state->rfds_rd)) 87 pfd->revents |= POLLIN; 88 if (FD_ISSET(pfd->fd, &state->rfds_wr)) 89 pfd->revents |= POLLOUT; 90 if (FD_ISSET(pfd->fd, &state->rfds_ex)) 91 pfd->revents |= POLLHUP; 92 } 93 94 return err; 95} 96 97static void select_release(struct waiter_context *waiter ATTRIBUTE_UNUSED) 98{ 99 return; 100} 101 102const struct waiter_data waiter_select = { 103 .ops = { 104 .prepare = select_prepare, 105 .wait_event = select_wait_event, 106 .release = select_release, 107 }, 108 .private_size = sizeof(struct select_state), 109}; 110