summaryrefslogtreecommitdiff
path: root/qcom/qrtr/src/waiter.c
diff options
context:
space:
mode:
Diffstat (limited to 'qcom/qrtr/src/waiter.c')
-rw-r--r--qcom/qrtr/src/waiter.c378
1 files changed, 378 insertions, 0 deletions
diff --git a/qcom/qrtr/src/waiter.c b/qcom/qrtr/src/waiter.c
new file mode 100644
index 0000000..f21896f
--- /dev/null
+++ b/qcom/qrtr/src/waiter.c
@@ -0,0 +1,378 @@
+/*
+ * Copyright (c) 2013-2014, Sony Mobile Communications Inc.
+ * Copyright (c) 2014, Courtney Cavin
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * - Neither the name of the organization nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+#include <poll.h>
+
+#include "list.h"
+#include "waiter.h"
+#include "util.h"
+
+struct pollset {
+ int nfds;
+ int cause;
+};
+
+static struct pollset *pollset_create(int count)
+{
+ struct pollset *ps;
+
+ ps = calloc(1, sizeof(*ps) + sizeof(struct pollfd) * count);
+ if (ps == NULL)
+ return NULL;
+
+ return ps;
+}
+
+static void pollset_destroy(struct pollset *ps)
+{
+ free(ps);
+}
+
+static void pollset_reset(struct pollset *ps)
+{
+ ps->nfds = 0;
+}
+
+static void pollset_add_fd(struct pollset *ps, int fd)
+{
+ struct pollfd *pfd = (struct pollfd *)(ps + 1);
+ pfd[ps->nfds].fd = fd;
+ pfd[ps->nfds].events = POLLERR | POLLIN;
+ ps->nfds++;
+}
+
+static int pollset_wait(struct pollset *ps, int ms)
+{
+ struct pollfd *pfd = (struct pollfd *)(ps + 1);
+ int rc;
+ int i;
+
+ rc = poll(pfd, ps->nfds, ms);
+ if (rc <= 0)
+ return rc;
+
+ ps->cause = -1;
+ for (i = 0; i < ps->nfds; ++i) {
+ if (pfd[i].revents & (POLLERR | POLLIN)) {
+ ps->cause = i;
+ break;
+ }
+ }
+ return rc;
+
+}
+
+static int pollset_cause_fd(struct pollset *ps, int fd)
+{
+ struct pollfd *pfd = (struct pollfd *)(ps + 1);
+ return (ps->cause >= 0 && pfd[ps->cause].fd == fd);
+}
+
+enum waiter_type {
+ WATCH_TYPE_NULL,
+ WATCH_TYPE_FD,
+ WATCH_TYPE_TIMEOUT,
+};
+
+struct waiter_ticket {
+ enum waiter_type type;
+ union {
+ int filedes;
+ unsigned int event;
+ unsigned int interval;
+ };
+ struct {
+ void (* fn)(void *data, struct waiter_ticket *);
+ void *data;
+ } callback;
+
+ uint64_t start;
+ int updated;
+ struct waiter *waiter;
+ struct list_item list_item;
+};
+
+struct waiter {
+ struct list tickets;
+ struct pollset *pollset;
+ int count;
+};
+
+struct waiter *waiter_create(void)
+{
+ struct waiter *w;
+
+ w = calloc(1, sizeof(*w));
+ if (w == NULL)
+ return NULL;
+
+ list_init(&w->tickets);
+ return w;
+}
+
+void waiter_destroy(struct waiter *w)
+{
+ struct waiter_ticket *ticket;
+ struct list_item *safe;
+ struct list_item *node;
+
+ list_for_each_safe(&w->tickets, node, safe) {
+ ticket = list_entry(node, struct waiter_ticket, list_item);
+ free(ticket);
+ }
+
+ if (w->pollset)
+ pollset_destroy(w->pollset);
+ free(w);
+}
+
+void waiter_synchronize(struct waiter *w)
+{
+ struct waiter_ticket *oticket;
+ struct waiter_ticket *ticket;
+ struct list_item *node;
+
+ list_for_each(&w->tickets, node) {
+ struct list_item *onode;
+ ticket = list_entry(node, struct waiter_ticket, list_item);
+
+ if (ticket->type != WATCH_TYPE_TIMEOUT)
+ continue;
+
+ list_for_each_after(node, onode) {
+ oticket = list_entry(onode, struct waiter_ticket, list_item);
+ if (oticket->type != WATCH_TYPE_TIMEOUT)
+ continue;
+
+ if (oticket->interval == ticket->interval) {
+ oticket->start = ticket->start;
+ break;
+ }
+ }
+ }
+}
+
+void waiter_wait(struct waiter *w)
+{
+ struct pollset *ps = w->pollset;
+ struct waiter_ticket *ticket;
+ struct list_item *node;
+ uint64_t term_time;
+ uint64_t now;
+ int rc;
+
+ pollset_reset(ps);
+
+ term_time = (uint64_t)-1;
+ list_for_each(&w->tickets, node) {
+ ticket = list_entry(node, struct waiter_ticket, list_item);
+ switch (ticket->type) {
+ case WATCH_TYPE_TIMEOUT:
+ if (ticket->start + ticket->interval < term_time)
+ term_time = ticket->start + ticket->interval;
+ break;
+ case WATCH_TYPE_FD:
+ pollset_add_fd(ps, ticket->filedes);
+ break;
+ case WATCH_TYPE_NULL:
+ break;
+ }
+ }
+
+ if (term_time == (uint64_t)-1) { /* wait forever */
+ rc = pollset_wait(ps, -1);
+ } else {
+ now = time_ms();
+ if (now >= term_time) { /* already past timeout, skip poll */
+ rc = 0;
+ } else {
+ uint64_t delta;
+
+ delta = term_time - now;
+ if (delta > ((1u << 31) - 1))
+ delta = ((1u << 31) - 1);
+ rc = pollset_wait(ps, (int)delta);
+ }
+ }
+
+ if (rc < 0)
+ return;
+
+ now = time_ms();
+ list_for_each(&w->tickets, node) {
+ int fresh = 0;
+
+ ticket = list_entry(node, struct waiter_ticket, list_item);
+ switch (ticket->type) {
+ case WATCH_TYPE_TIMEOUT:
+ if (now >= ticket->start + ticket->interval) {
+ ticket->start = now;
+ fresh = !ticket->updated;
+ }
+ break;
+ case WATCH_TYPE_FD:
+ if (rc == 0) /* timed-out */
+ break;
+ if (pollset_cause_fd(ps, ticket->filedes))
+ fresh = !ticket->updated;
+ break;
+ case WATCH_TYPE_NULL:
+ break;
+ }
+ if (fresh) {
+ ticket->updated = 1;
+ if (ticket->callback.fn)
+ (* ticket->callback.fn)(
+ ticket->callback.data,
+ ticket
+ );
+ }
+ }
+}
+
+int waiter_wait_timeout(struct waiter *w, unsigned int ms)
+{
+ struct waiter_ticket ticket;
+ int rc;
+
+ memset(&ticket, 0, sizeof(ticket));
+ waiter_ticket_set_timeout(&ticket, ms);
+ list_append(&w->tickets, &ticket.list_item);
+ w->count++;
+
+ waiter_wait(w);
+ rc = waiter_ticket_check(&ticket);
+
+ list_remove(&w->tickets, &ticket.list_item);
+ w->count--;
+
+ return -!rc;
+}
+
+void waiter_ticket_set_null(struct waiter_ticket *ticket)
+{
+ ticket->type = WATCH_TYPE_NULL;
+}
+
+void waiter_ticket_set_fd(struct waiter_ticket *ticket, int fd)
+{
+ ticket->type = WATCH_TYPE_FD;
+ ticket->filedes = fd;
+}
+
+void waiter_ticket_set_timeout(struct waiter_ticket *ticket, unsigned int ms)
+{
+ ticket->type = WATCH_TYPE_TIMEOUT;
+ ticket->interval = ms;
+ ticket->start = time_ms();
+}
+
+struct waiter_ticket *waiter_add_null(struct waiter *w)
+{
+ struct waiter_ticket *ticket;
+
+ ticket = calloc(1, sizeof(*ticket));
+ if (ticket == NULL)
+ return NULL;
+ ticket->waiter = w;
+
+ list_append(&w->tickets, &ticket->list_item);
+ if ((w->count % 32) == 0) {
+ if (w->pollset)
+ pollset_destroy(w->pollset);
+ w->pollset = pollset_create(w->count + 33);
+ if (w->pollset == NULL)
+ return NULL;
+ }
+ w->count++;
+
+ waiter_ticket_set_null(ticket);
+
+ return ticket;
+}
+
+struct waiter_ticket *waiter_add_fd(struct waiter *w, int fd)
+{
+ struct waiter_ticket *ticket;
+
+ ticket = waiter_add_null(w);
+ if (ticket == NULL)
+ return NULL;
+
+ waiter_ticket_set_fd(ticket, fd);
+
+ return ticket;
+}
+
+struct waiter_ticket *waiter_add_timeout(struct waiter *w, unsigned int ms)
+{
+ struct waiter_ticket *ticket;
+
+ ticket = waiter_add_null(w);
+ if (ticket == NULL)
+ return NULL;
+
+ waiter_ticket_set_timeout(ticket, ms);
+
+ return ticket;
+}
+
+void waiter_ticket_delete(struct waiter_ticket *ticket)
+{
+ struct waiter *w = ticket->waiter;
+ list_remove(&w->tickets, &ticket->list_item);
+ w->count--;
+ free(ticket);
+}
+
+void waiter_ticket_callback(struct waiter_ticket *ticket, waiter_ticket_cb_t cb_fn, void *data)
+{
+ ticket->callback.fn = cb_fn;
+ ticket->callback.data = data;
+}
+
+int waiter_ticket_check(const struct waiter_ticket *ticket)
+{
+ return -(ticket->updated == 0);
+}
+
+int waiter_ticket_clear(struct waiter_ticket *ticket)
+{
+ int ret;
+
+ ret = waiter_ticket_check(ticket);
+ ticket->updated = 0;
+
+ return ret;
+}