• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // SPDX-License-Identifier: GPL-2.0
2 //
3 // waiter-epoll.c - Waiter for event notification by epoll(7).
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 #include "misc.h"
11 
12 #include <stdlib.h>
13 #include <unistd.h>
14 #include <string.h>
15 #include <errno.h>
16 #include <sys/types.h>
17 #include <sys/epoll.h>
18 
19 struct epoll_state {
20 	int epfd;
21 	struct epoll_event *events;
22 	unsigned int ev_count;
23 };
24 
epoll_prepare(struct waiter_context * waiter)25 static int epoll_prepare(struct waiter_context *waiter)
26 {
27 	struct epoll_state *state = waiter->private_data;
28 	int i;
29 
30 	state->ev_count = waiter->pfd_count;
31 	state->events = calloc(state->ev_count, sizeof(*state->events));
32 	if (state->events == NULL)
33 		return -ENOMEM;
34 
35 	state->epfd = epoll_create(1);
36 	if (state->epfd < 0)
37 		return -errno;
38 
39 	for (i = 0; i < waiter->pfd_count; ++i) {
40 		struct epoll_event ev = {
41 			.data.fd = waiter->pfds[i].fd,
42 			.events = waiter->pfds[i].events,
43 		};
44 		if (epoll_ctl(state->epfd, EPOLL_CTL_ADD, ev.data.fd, &ev) < 0)
45 			return -errno;
46 	}
47 
48 	return 0;
49 }
50 
epoll_wait_event(struct waiter_context * waiter,int timeout_msec)51 static int epoll_wait_event(struct waiter_context *waiter, int timeout_msec)
52 {
53 	struct epoll_state *state = waiter->private_data;
54 	unsigned int ev_count;
55 	int i, j;
56 	int err;
57 
58 	memset(state->events, 0, state->ev_count * sizeof(*state->events));
59 	err = epoll_wait(state->epfd, state->events, state->ev_count,
60 			 timeout_msec);
61 	if (err < 0)
62 		return -errno;
63 	ev_count = (unsigned int)err;
64 
65 	if (ev_count > 0) {
66 		// Reconstruct data of pollfd structure.
67 		for (i = 0; i < ev_count; ++i) {
68 			struct epoll_event *ev = &state->events[i];
69 			for (j = 0; j < waiter->pfd_count; ++j) {
70 				if (waiter->pfds[i].fd == ev->data.fd) {
71 					waiter->pfds[i].revents = ev->events;
72 					break;
73 				}
74 			}
75 		}
76 	}
77 
78 	return ev_count;
79 }
80 
epoll_release(struct waiter_context * waiter)81 static void epoll_release(struct waiter_context *waiter)
82 {
83 	struct epoll_state *state = waiter->private_data;
84 	int i;
85 
86 	for (i = 0; i < waiter->pfd_count; ++i) {
87 		int fd = waiter->pfds[i].fd;
88 		epoll_ctl(state->epfd, EPOLL_CTL_DEL, fd, NULL);
89 	}
90 
91 	free(state->events);
92 	state->events = NULL;
93 
94 	close(state->epfd);
95 
96 	state->ev_count = 0;
97 	state->epfd = 0;
98 }
99 
100 const struct waiter_data waiter_epoll = {
101 	.ops = {
102 		.prepare	= epoll_prepare,
103 		.wait_event	= epoll_wait_event,
104 		.release	= epoll_release,
105 	},
106 	.private_size = sizeof(struct epoll_state),
107 };
108