2 * Copyright 2016 IoT.bzh
3 * Author: José Bollo <jose.bollo@iot.bzh>
5 * Licensed under the Apache License, Version 2.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at
9 * http://www.apache.org/licenses/LICENSE-2.0
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
18 #include <sys/epoll.h>
25 #include "utils-upoll.h"
30 * Structure describing one opened client
34 struct upollfd *fd; /* structure handling the file descriptor */
35 void (*read)(void *); /* callback for handling on_readable */
36 void (*write)(void *); /* callback for handling on_writable */
37 void (*hangup)(void *); /* callback for handling on_hangup */
38 void *closure; /* closure for callbacks */
39 struct upoll *next; /* next client of the same file descriptor */
43 * Structure describing a watched file descriptor
47 int fd; /* watch file descriptor */
48 uint32_t events; /* watched events */
49 struct upollfd *next; /* next watched file descriptor */
50 struct upoll *head; /* first client watching the file descriptor */
54 * Structure describing a upoll group
60 struct upoll *current;
61 pthread_mutex_t mutex;
64 static struct upollgrp global = {
68 .mutex = PTHREAD_MUTEX_INITIALIZER
71 static int pollfd = 0;
72 static struct upollfd *head = NULL;
73 static struct upoll *current = NULL;
74 static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
79 static int update(struct upollfd *ufd)
88 pthread_mutex_lock(&mutex);
91 /* no more watchers */
100 pthread_mutex_unlock(&mutex);
101 epoll_ctl(pollfd, EPOLL_CTL_DEL, ufd->fd, NULL);
105 /* compute the events for the watchers */
109 if (u->write != NULL)
113 pthread_mutex_unlock(&mutex);
114 if (ufd->events == events)
118 rc = epoll_ctl(pollfd, EPOLL_CTL_MOD, ufd->fd, &e);
120 ufd->events = events;
125 * Compute the events for the set of clients
127 static int update_flags(struct upollfd *ufd)
131 struct epoll_event e;
133 struct upollfd **prv;
135 /* compute expected events */
137 pthread_mutex_lock(&mutex);
143 if (u->write != NULL)
147 if (ufd->events == events)
152 rc = epoll_ctl(pollfd, EPOLL_CTL_MOD, ufd->fd, &e);
154 ufd->events = events;
156 pthread_mutex_unlock(&mutex);
160 static struct upollfd *get_fd(int fd)
162 struct epoll_event e;
163 struct upollfd *result;
166 /* opens the epoll stream */
168 pollfd = epoll_create1(EPOLL_CLOEXEC);
181 while (result != NULL) {
182 if (result->fd == fd)
184 result = result->next;
188 result = calloc(1, sizeof *result);
194 pthread_mutex_lock(&mutex);
197 pthread_mutex_unlock(&mutex);
202 rc = epoll_ctl(pollfd, EPOLL_CTL_ADD, fd, &e);
206 /* revert on error */
213 int upoll_is_valid(struct upoll *upoll)
215 struct upollfd *itfd = head;
217 while (itfd != NULL) {
229 struct upoll *upoll_open(int fd, void *closure)
232 struct upoll *result;
235 result = calloc(1, sizeof *result);
248 result->closure = closure;
249 pthread_mutex_lock(&mutex);
250 result->next = ufd->head;
252 pthread_mutex_unlock(&mutex);
256 int upoll_on_readable(struct upoll *upoll, void (*process)(void *))
259 assert(upoll_is_valid(upoll));
261 upoll->read = process;
262 return update(upoll->fd);
265 int upoll_on_writable(struct upoll *upoll, void (*process)(void *))
268 assert(upoll_is_valid(upoll));
270 upoll->write = process;
271 return update(upoll->fd);
274 void upoll_on_hangup(struct upoll *upoll, void (*process)(void *))
277 assert(upoll_is_valid(upoll));
279 upoll->hangup = process;
282 void upoll_close(struct upoll *upoll)
288 assert(upoll_is_valid(upoll));
291 pthread_mutex_lock(&mutex);
292 if (current == upoll)
298 pthread_mutex_unlock(&mutex);
303 int upoll_wait(int timeout)
306 struct epoll_event e;
315 rc = epoll_wait(pollfd, &e, 1, timeout);
316 } while (rc < 0 && errno == EINTR);
320 e.events &= EPOLLIN | EPOLLOUT | EPOLLHUP;
321 while (current != NULL && e.events != 0) {
322 if ((e.events & EPOLLIN) && current->read) {
323 current->read(current->closure);
324 e.events &= (uint32_t)~EPOLLIN;
327 if ((e.events & EPOLLOUT) && current->write) {
328 current->write(current->closure);
329 e.events &= (uint32_t)~EPOLLOUT;
332 if ((e.events & EPOLLHUP) && current->hangup) {
333 current->hangup(current->closure);
337 current = current->next;
340 return rc < 0 ? rc : 0;