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 */
55 * Structure describing a upoll group
60 struct upoll *current;
61 pthread_mutex_t mutex;
65 static struct upollgrp global = {
69 .mutex = PTHREAD_MUTEX_INITIALIZER
73 static int pollfd = 0;
74 static struct upollfd *head = NULL;
75 static struct upoll *current = NULL;
76 static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
79 * Compute the events for the set of clients
81 static int update_flags_locked(struct upollfd *ufd)
88 /* compute expected events */
98 if (ufd->events == events)
103 rc = epoll_ctl(pollfd, EPOLL_CTL_MOD, ufd->fd, &e);
105 ufd->events = events;
107 pthread_mutex_unlock(&mutex);
112 * Compute the events for the set of clients
114 static int update_flags(struct upollfd *ufd)
116 pthread_mutex_lock(&mutex);
117 return update_flags_locked(ufd);
123 static int update(struct upollfd *ufd)
125 struct upollfd **prv;
127 pthread_mutex_lock(&mutex);
128 if (ufd->head != NULL)
129 return update_flags_locked(ufd);
131 /* no more watchers */
140 pthread_mutex_unlock(&mutex);
141 epoll_ctl(pollfd, EPOLL_CTL_DEL, ufd->fd, NULL);
146 static struct upollfd *get_fd(int fd)
148 struct epoll_event e;
149 struct upollfd *result;
152 /* opens the epoll stream */
154 pollfd = epoll_create1(EPOLL_CLOEXEC);
167 while (result != NULL) {
168 if (result->fd == fd)
170 result = result->next;
174 result = calloc(1, sizeof *result);
180 pthread_mutex_lock(&mutex);
183 pthread_mutex_unlock(&mutex);
188 rc = epoll_ctl(pollfd, EPOLL_CTL_ADD, fd, &e);
192 /* revert on error */
199 int upoll_is_valid(struct upoll *upoll)
201 struct upollfd *itfd = head;
203 while (itfd != NULL) {
215 struct upoll *upoll_open(int fd, void *closure)
218 struct upoll *result;
221 result = calloc(1, sizeof *result);
234 result->closure = closure;
235 pthread_mutex_lock(&mutex);
236 result->next = ufd->head;
238 pthread_mutex_unlock(&mutex);
242 int upoll_on_readable(struct upoll *upoll, void (*process)(void *))
245 assert(upoll_is_valid(upoll));
247 upoll->read = process;
248 return update_flags(upoll->fd);
251 int upoll_on_writable(struct upoll *upoll, void (*process)(void *))
254 assert(upoll_is_valid(upoll));
256 upoll->write = process;
257 return update_flags(upoll->fd);
260 void upoll_on_hangup(struct upoll *upoll, void (*process)(void *))
263 assert(upoll_is_valid(upoll));
265 upoll->hangup = process;
268 void upoll_close(struct upoll *upoll)
274 assert(upoll_is_valid(upoll));
277 pthread_mutex_lock(&mutex);
278 if (current == upoll)
284 pthread_mutex_unlock(&mutex);
289 int upoll_wait(int timeout)
292 struct epoll_event e;
301 rc = epoll_wait(pollfd, &e, 1, timeout);
302 } while (rc < 0 && errno == EINTR);
306 e.events &= EPOLLIN | EPOLLOUT | EPOLLHUP;
307 while (current != NULL && e.events != 0) {
308 if ((e.events & EPOLLIN) && current->read) {
309 current->read(current->closure);
310 e.events &= (uint32_t)~EPOLLIN;
313 if ((e.events & EPOLLOUT) && current->write) {
314 current->write(current->closure);
315 e.events &= (uint32_t)~EPOLLOUT;
318 if ((e.events & EPOLLHUP) && current->hangup) {
319 current->hangup(current->closure);
323 current = current->next;
326 return rc < 0 ? rc : 0;