blob: f21896f4878600883f7cc32a75849ef9154aca02 [file] [log] [blame]
Amit Pundird477f822020-02-07 22:26:08 +05301/*
2 * Copyright (c) 2013-2014, Sony Mobile Communications Inc.
3 * Copyright (c) 2014, Courtney Cavin
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
7 *
8 * - Redistributions of source code must retain the above copyright notice,
9 * this list of conditions and the following disclaimer.
10 *
11 * - Redistributions in binary form must reproduce the above copyright notice,
12 * this list of conditions and the following disclaimer in the documentation
13 * and/or other materials provided with the distribution.
14 *
15 * - Neither the name of the organization nor the names of its contributors
16 * may be used to endorse or promote products derived from this software
17 * without specific prior written permission.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
20 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
23 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29 * POSSIBILITY OF SUCH DAMAGE.
30 */
31
32#include <stdint.h>
33#include <stdlib.h>
34#include <string.h>
35#include <poll.h>
36
37#include "list.h"
38#include "waiter.h"
39#include "util.h"
40
41struct pollset {
42 int nfds;
43 int cause;
44};
45
46static struct pollset *pollset_create(int count)
47{
48 struct pollset *ps;
49
50 ps = calloc(1, sizeof(*ps) + sizeof(struct pollfd) * count);
51 if (ps == NULL)
52 return NULL;
53
54 return ps;
55}
56
57static void pollset_destroy(struct pollset *ps)
58{
59 free(ps);
60}
61
62static void pollset_reset(struct pollset *ps)
63{
64 ps->nfds = 0;
65}
66
67static void pollset_add_fd(struct pollset *ps, int fd)
68{
69 struct pollfd *pfd = (struct pollfd *)(ps + 1);
70 pfd[ps->nfds].fd = fd;
71 pfd[ps->nfds].events = POLLERR | POLLIN;
72 ps->nfds++;
73}
74
75static int pollset_wait(struct pollset *ps, int ms)
76{
77 struct pollfd *pfd = (struct pollfd *)(ps + 1);
78 int rc;
79 int i;
80
81 rc = poll(pfd, ps->nfds, ms);
82 if (rc <= 0)
83 return rc;
84
85 ps->cause = -1;
86 for (i = 0; i < ps->nfds; ++i) {
87 if (pfd[i].revents & (POLLERR | POLLIN)) {
88 ps->cause = i;
89 break;
90 }
91 }
92 return rc;
93
94}
95
96static int pollset_cause_fd(struct pollset *ps, int fd)
97{
98 struct pollfd *pfd = (struct pollfd *)(ps + 1);
99 return (ps->cause >= 0 && pfd[ps->cause].fd == fd);
100}
101
102enum waiter_type {
103 WATCH_TYPE_NULL,
104 WATCH_TYPE_FD,
105 WATCH_TYPE_TIMEOUT,
106};
107
108struct waiter_ticket {
109 enum waiter_type type;
110 union {
111 int filedes;
112 unsigned int event;
113 unsigned int interval;
114 };
115 struct {
116 void (* fn)(void *data, struct waiter_ticket *);
117 void *data;
118 } callback;
119
120 uint64_t start;
121 int updated;
122 struct waiter *waiter;
123 struct list_item list_item;
124};
125
126struct waiter {
127 struct list tickets;
128 struct pollset *pollset;
129 int count;
130};
131
132struct waiter *waiter_create(void)
133{
134 struct waiter *w;
135
136 w = calloc(1, sizeof(*w));
137 if (w == NULL)
138 return NULL;
139
140 list_init(&w->tickets);
141 return w;
142}
143
144void waiter_destroy(struct waiter *w)
145{
146 struct waiter_ticket *ticket;
147 struct list_item *safe;
148 struct list_item *node;
149
150 list_for_each_safe(&w->tickets, node, safe) {
151 ticket = list_entry(node, struct waiter_ticket, list_item);
152 free(ticket);
153 }
154
155 if (w->pollset)
156 pollset_destroy(w->pollset);
157 free(w);
158}
159
160void waiter_synchronize(struct waiter *w)
161{
162 struct waiter_ticket *oticket;
163 struct waiter_ticket *ticket;
164 struct list_item *node;
165
166 list_for_each(&w->tickets, node) {
167 struct list_item *onode;
168 ticket = list_entry(node, struct waiter_ticket, list_item);
169
170 if (ticket->type != WATCH_TYPE_TIMEOUT)
171 continue;
172
173 list_for_each_after(node, onode) {
174 oticket = list_entry(onode, struct waiter_ticket, list_item);
175 if (oticket->type != WATCH_TYPE_TIMEOUT)
176 continue;
177
178 if (oticket->interval == ticket->interval) {
179 oticket->start = ticket->start;
180 break;
181 }
182 }
183 }
184}
185
186void waiter_wait(struct waiter *w)
187{
188 struct pollset *ps = w->pollset;
189 struct waiter_ticket *ticket;
190 struct list_item *node;
191 uint64_t term_time;
192 uint64_t now;
193 int rc;
194
195 pollset_reset(ps);
196
197 term_time = (uint64_t)-1;
198 list_for_each(&w->tickets, node) {
199 ticket = list_entry(node, struct waiter_ticket, list_item);
200 switch (ticket->type) {
201 case WATCH_TYPE_TIMEOUT:
202 if (ticket->start + ticket->interval < term_time)
203 term_time = ticket->start + ticket->interval;
204 break;
205 case WATCH_TYPE_FD:
206 pollset_add_fd(ps, ticket->filedes);
207 break;
208 case WATCH_TYPE_NULL:
209 break;
210 }
211 }
212
213 if (term_time == (uint64_t)-1) { /* wait forever */
214 rc = pollset_wait(ps, -1);
215 } else {
216 now = time_ms();
217 if (now >= term_time) { /* already past timeout, skip poll */
218 rc = 0;
219 } else {
220 uint64_t delta;
221
222 delta = term_time - now;
223 if (delta > ((1u << 31) - 1))
224 delta = ((1u << 31) - 1);
225 rc = pollset_wait(ps, (int)delta);
226 }
227 }
228
229 if (rc < 0)
230 return;
231
232 now = time_ms();
233 list_for_each(&w->tickets, node) {
234 int fresh = 0;
235
236 ticket = list_entry(node, struct waiter_ticket, list_item);
237 switch (ticket->type) {
238 case WATCH_TYPE_TIMEOUT:
239 if (now >= ticket->start + ticket->interval) {
240 ticket->start = now;
241 fresh = !ticket->updated;
242 }
243 break;
244 case WATCH_TYPE_FD:
245 if (rc == 0) /* timed-out */
246 break;
247 if (pollset_cause_fd(ps, ticket->filedes))
248 fresh = !ticket->updated;
249 break;
250 case WATCH_TYPE_NULL:
251 break;
252 }
253 if (fresh) {
254 ticket->updated = 1;
255 if (ticket->callback.fn)
256 (* ticket->callback.fn)(
257 ticket->callback.data,
258 ticket
259 );
260 }
261 }
262}
263
264int waiter_wait_timeout(struct waiter *w, unsigned int ms)
265{
266 struct waiter_ticket ticket;
267 int rc;
268
269 memset(&ticket, 0, sizeof(ticket));
270 waiter_ticket_set_timeout(&ticket, ms);
271 list_append(&w->tickets, &ticket.list_item);
272 w->count++;
273
274 waiter_wait(w);
275 rc = waiter_ticket_check(&ticket);
276
277 list_remove(&w->tickets, &ticket.list_item);
278 w->count--;
279
280 return -!rc;
281}
282
283void waiter_ticket_set_null(struct waiter_ticket *ticket)
284{
285 ticket->type = WATCH_TYPE_NULL;
286}
287
288void waiter_ticket_set_fd(struct waiter_ticket *ticket, int fd)
289{
290 ticket->type = WATCH_TYPE_FD;
291 ticket->filedes = fd;
292}
293
294void waiter_ticket_set_timeout(struct waiter_ticket *ticket, unsigned int ms)
295{
296 ticket->type = WATCH_TYPE_TIMEOUT;
297 ticket->interval = ms;
298 ticket->start = time_ms();
299}
300
301struct waiter_ticket *waiter_add_null(struct waiter *w)
302{
303 struct waiter_ticket *ticket;
304
305 ticket = calloc(1, sizeof(*ticket));
306 if (ticket == NULL)
307 return NULL;
308 ticket->waiter = w;
309
310 list_append(&w->tickets, &ticket->list_item);
311 if ((w->count % 32) == 0) {
312 if (w->pollset)
313 pollset_destroy(w->pollset);
314 w->pollset = pollset_create(w->count + 33);
315 if (w->pollset == NULL)
316 return NULL;
317 }
318 w->count++;
319
320 waiter_ticket_set_null(ticket);
321
322 return ticket;
323}
324
325struct waiter_ticket *waiter_add_fd(struct waiter *w, int fd)
326{
327 struct waiter_ticket *ticket;
328
329 ticket = waiter_add_null(w);
330 if (ticket == NULL)
331 return NULL;
332
333 waiter_ticket_set_fd(ticket, fd);
334
335 return ticket;
336}
337
338struct waiter_ticket *waiter_add_timeout(struct waiter *w, unsigned int ms)
339{
340 struct waiter_ticket *ticket;
341
342 ticket = waiter_add_null(w);
343 if (ticket == NULL)
344 return NULL;
345
346 waiter_ticket_set_timeout(ticket, ms);
347
348 return ticket;
349}
350
351void waiter_ticket_delete(struct waiter_ticket *ticket)
352{
353 struct waiter *w = ticket->waiter;
354 list_remove(&w->tickets, &ticket->list_item);
355 w->count--;
356 free(ticket);
357}
358
359void waiter_ticket_callback(struct waiter_ticket *ticket, waiter_ticket_cb_t cb_fn, void *data)
360{
361 ticket->callback.fn = cb_fn;
362 ticket->callback.data = data;
363}
364
365int waiter_ticket_check(const struct waiter_ticket *ticket)
366{
367 return -(ticket->updated == 0);
368}
369
370int waiter_ticket_clear(struct waiter_ticket *ticket)
371{
372 int ret;
373
374 ret = waiter_ticket_check(ticket);
375 ticket->updated = 0;
376
377 return ret;
378}