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
31 struct select_state {
32 fd_set rfds_rd;
33 fd_set rfds_wr;
34 fd_set rfds_ex;
35 };
36
select_prepare(struct waiter_context * waiter)37 static int select_prepare(struct waiter_context *waiter)
38 {
39 return 0;
40 }
41
select_wait_event(struct waiter_context * waiter,int timeout_msec)42 static 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 < 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 < 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
select_release(struct waiter_context * waiter)97 static void select_release(struct waiter_context *waiter)
98 {
99 return;
100 }
101
102 const 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