new main loop in place
[src/app-framework-binder.git] / src / utils-upoll.c
1 /*
2  * Copyright 2016 IoT.bzh
3  * Author: José Bollo <jose.bollo@iot.bzh>
4  *
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
8  *
9  *   http://www.apache.org/licenses/LICENSE-2.0
10  *
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.
16  */
17
18 #include <sys/epoll.h>
19 #include <pthread.h>
20 #include <stdlib.h>
21 #include <errno.h>
22 #include <unistd.h>
23 #include <assert.h>
24
25 #include "utils-upoll.h"
26
27
28 struct upoll
29 {
30         int fd;
31         void (*read)(void *);
32         void (*write)(void *);
33         void (*hangup)(void *);
34         void *closure;
35         struct upoll *next;
36 };
37
38 static int pollfd = 0;
39 static struct upoll *head = NULL;
40 static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
41
42 int upoll_is_valid(struct upoll *upoll)
43 {
44         struct upoll *it = head;
45         while (it != NULL) {
46                 if (it == upoll)
47                         return 1;
48                 it = it->next;
49         }
50         return 0;
51 }
52
53 struct upoll *upoll_open(int fd, void *closure)
54 {
55         struct epoll_event e;
56         struct upoll *result;
57         int rc;
58
59         /* opens the epoll stream */
60         if (pollfd == 0) {
61                 pollfd = epoll_create1(EPOLL_CLOEXEC);
62                 if (pollfd == 0) {
63                         pollfd = dup(0);
64                         close(0);
65                 }
66                 if (pollfd < 0) {
67                         pollfd = 0;
68                         return NULL;
69                 }
70         }
71
72         /* allocates */
73         result = calloc(1, sizeof *result);
74         if (result == NULL)
75                 return NULL;
76
77         /* init */
78         result->fd = fd;
79         result->closure = closure;
80         pthread_mutex_lock(&mutex);
81         result->next = head;
82         head = result;
83         pthread_mutex_unlock(&mutex);
84
85         /* records */
86         e.events = 0;
87         e.data.ptr = result;
88         rc = epoll_ctl(pollfd, EPOLL_CTL_ADD, fd, &e);
89         if (rc == 0)
90                 return result;
91
92         /* revert on error */
93         rc = errno;
94         upoll_close(result);
95         errno = rc;
96         return NULL;
97 }
98
99 static int update(struct upoll *upoll)
100 {
101         struct epoll_event e;
102         e.events = (upoll->read != NULL ? EPOLLIN : 0 )
103                  | (upoll->write != NULL ? EPOLLOUT : 0);
104         e.data.ptr = upoll;
105         return epoll_ctl(pollfd, EPOLL_CTL_MOD, upoll->fd, &e);
106 }
107
108 int upoll_on_readable(struct upoll *upoll, void (*process)(void *))
109 {
110         assert(pollfd != 0);
111         assert(upoll_is_valid(upoll));
112
113         upoll->read = process;
114         return update(upoll);
115 }
116
117 int upoll_on_writable(struct upoll *upoll, void (*process)(void *))
118 {
119         assert(pollfd != 0);
120         assert(upoll_is_valid(upoll));
121
122         upoll->write = process;
123         return update(upoll);
124 }
125
126 void upoll_on_hangup(struct upoll *upoll, void (*process)(void *))
127 {
128         assert(pollfd != 0);
129         assert(upoll_is_valid(upoll));
130
131         upoll->hangup = process;
132 }
133
134 void upoll_close(struct upoll *upoll)
135 {
136         struct upoll **it;
137
138         assert(pollfd != 0);
139         assert(upoll_is_valid(upoll));
140
141         epoll_ctl(pollfd, EPOLL_CTL_DEL, upoll->fd, NULL);
142         pthread_mutex_lock(&mutex);
143         it = &head;
144         while (*it != upoll)
145                 it = &(*it)->next;
146         *it = upoll->next;
147         pthread_mutex_unlock(&mutex);
148         free(upoll);
149 }
150
151 void upoll_wait(int timeout)
152 {
153         int rc;
154         struct epoll_event e;
155         struct upoll *upoll;
156
157         if (pollfd == 0)
158                 return;
159
160         rc = epoll_wait(pollfd, &e, 1, timeout);
161         if (rc == 1) {
162                 upoll = e.data.ptr;
163                 if ((e.events & EPOLLIN) && upoll->read)
164                         upoll->read(upoll->closure);
165                 if ((e.events & EPOLLOUT) && upoll->write)
166                         upoll->write(upoll->closure);
167                 if ((e.events & EPOLLHUP) && upoll->hangup)
168                         upoll->hangup(upoll->closure);
169         }
170 }
171