• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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