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