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