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