summaryrefslogtreecommitdiff
path: root/src/libcamera/event_dispatcher_poll.cpp
diff options
context:
space:
mode:
authorLaurent Pinchart <laurent.pinchart@ideasonboard.com>2019-01-04 21:26:14 +0200
committerLaurent Pinchart <laurent.pinchart@ideasonboard.com>2019-01-08 16:23:16 +0200
commit8356f8a6ab875680087032285c3b7bbfbdbddba9 (patch)
tree1288abd030c33fb9bb85c99eccabfe3958b3f7ce /src/libcamera/event_dispatcher_poll.cpp
parent1a57bcb8d1a7a49cc8a43596a79d57cd2fbb5dfe (diff)
libcamera: Add a poll-based event dispatcher
Provide a poll-based event dispatcher implementation as convenience for applications that don't need a custom event loop. The poll-based dispatcher is automatically instantiated if the application doesn't provide its own dispatcher. Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Diffstat (limited to 'src/libcamera/event_dispatcher_poll.cpp')
-rw-r--r--src/libcamera/event_dispatcher_poll.cpp235
1 files changed, 235 insertions, 0 deletions
diff --git a/src/libcamera/event_dispatcher_poll.cpp b/src/libcamera/event_dispatcher_poll.cpp
new file mode 100644
index 00000000..a497a7fe
--- /dev/null
+++ b/src/libcamera/event_dispatcher_poll.cpp
@@ -0,0 +1,235 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+/*
+ * Copyright (C) 2019, Google Inc.
+ *
+ * event_dispatcher_poll.cpp - Poll-based event dispatcher
+ */
+
+#include <algorithm>
+#include <iomanip>
+#include <poll.h>
+#include <string.h>
+
+#include <libcamera/event_notifier.h>
+#include <libcamera/timer.h>
+
+#include "event_dispatcher_poll.h"
+#include "log.h"
+
+/**
+ * \file event_dispatcher_poll.h
+ */
+
+namespace libcamera {
+
+static const char *notifierType(EventNotifier::Type type)
+{
+ if (type == EventNotifier::Read)
+ return "read";
+ if (type == EventNotifier::Write)
+ return "write";
+ if (type == EventNotifier::Exception)
+ return "exception";
+
+ return "";
+}
+
+/**
+ * \class EventDispatcherPoll
+ * \brief A poll-based event dispatcher
+ */
+
+EventDispatcherPoll::EventDispatcherPoll()
+{
+}
+
+EventDispatcherPoll::~EventDispatcherPoll()
+{
+}
+
+void EventDispatcherPoll::registerEventNotifier(EventNotifier *notifier)
+{
+ EventNotifierSetPoll &set = notifiers_[notifier->fd()];
+ EventNotifier::Type type = notifier->type();
+
+ if (set.notifiers[type] && set.notifiers[type] != notifier) {
+ LOG(Warning) << "Ignoring duplicate " << notifierType(type)
+ << " notifier for fd " << notifier->fd();
+ return;
+ }
+
+ set.notifiers[type] = notifier;
+}
+
+void EventDispatcherPoll::unregisterEventNotifier(EventNotifier *notifier)
+{
+ auto iter = notifiers_.find(notifier->fd());
+ if (iter == notifiers_.end())
+ return;
+
+ EventNotifierSetPoll &set = iter->second;
+ EventNotifier::Type type = notifier->type();
+
+ if (!set.notifiers[type])
+ return;
+
+ if (set.notifiers[type] != notifier) {
+ LOG(Warning) << notifierType(type) << " notifier for fd "
+ << notifier->fd() << " is not registered";
+ return;
+ }
+
+ set.notifiers[type] = nullptr;
+ if (!set.notifiers[0] && !set.notifiers[1] && !set.notifiers[2])
+ notifiers_.erase(iter);
+}
+
+void EventDispatcherPoll::registerTimer(Timer *timer)
+{
+ for (auto iter = timers_.begin(); iter != timers_.end(); ++iter) {
+ if ((*iter)->deadline() > timer->deadline()) {
+ timers_.insert(iter, timer);
+ return;
+ }
+ }
+
+ timers_.push_back(timer);
+}
+
+void EventDispatcherPoll::unregisterTimer(Timer *timer)
+{
+ for (auto iter = timers_.begin(); iter != timers_.end(); ++iter) {
+ if (*iter == timer) {
+ timers_.erase(iter);
+ return;
+ }
+
+ /*
+ * As the timers list is ordered, we can stop as soon as we go
+ * past the deadline.
+ */
+ if ((*iter)->deadline() > timer->deadline())
+ break;
+ }
+}
+
+void EventDispatcherPoll::processEvents()
+{
+ int ret;
+
+ /* Create the pollfd array. */
+ std::vector<struct pollfd> pollfds;
+ pollfds.reserve(notifiers_.size());
+
+ for (auto notifier : notifiers_)
+ pollfds.push_back({ notifier.first, notifier.second.events(), 0 });
+
+ /* Compute the timeout. */
+ Timer *nextTimer = !timers_.empty() ? timers_.front() : nullptr;
+ struct timespec timeout;
+
+ if (nextTimer) {
+ clock_gettime(CLOCK_MONOTONIC, &timeout);
+ uint64_t now = timeout.tv_sec * 1000000000ULL + timeout.tv_nsec;
+
+ if (nextTimer->deadline() > now) {
+ uint64_t delta = nextTimer->deadline() - now;
+ timeout.tv_sec = delta / 1000000000ULL;
+ timeout.tv_nsec = delta % 1000000000ULL;
+ } else {
+ timeout.tv_sec = 0;
+ timeout.tv_nsec = 0;
+ }
+
+ LOG(Debug) << "timeout " << timeout.tv_sec << "."
+ << std::setfill('0') << std::setw(9)
+ << timeout.tv_nsec;
+ }
+
+ /* Wait for events and process notifers and timers. */
+ ret = ppoll(pollfds.data(), pollfds.size(),
+ nextTimer ? &timeout : nullptr, nullptr);
+ if (ret < 0) {
+ ret = -errno;
+ LOG(Warning) << "poll() failed with " << strerror(-ret);
+ } else if (ret > 0) {
+ processNotifiers(pollfds);
+ }
+
+ processTimers();
+}
+
+short EventDispatcherPoll::EventNotifierSetPoll::events() const
+{
+ short events = 0;
+
+ if (notifiers[EventNotifier::Read])
+ events |= POLLIN;
+ if (notifiers[EventNotifier::Write])
+ events |= POLLOUT;
+ if (notifiers[EventNotifier::Exception])
+ events |= POLLPRI;
+
+ return events;
+}
+
+void EventDispatcherPoll::processNotifiers(std::vector<struct pollfd> &pollfds)
+{
+ static const struct {
+ EventNotifier::Type type;
+ short events;
+ } events[] = {
+ { EventNotifier::Read, POLLIN },
+ { EventNotifier::Write, POLLOUT },
+ { EventNotifier::Exception, POLLPRI },
+ };
+
+ for (const struct pollfd &pfd : pollfds) {
+ auto iter = notifiers_.find(pfd.fd);
+ ASSERT(iter != notifiers_.end());
+
+ EventNotifierSetPoll &set = iter->second;
+
+ for (const auto &event : events) {
+ EventNotifier *notifier = set.notifiers[event.type];
+
+ if (!notifier)
+ continue;
+
+ /*
+ * If the file descriptor is invalid, disable the
+ * notifier immediately.
+ */
+ if (pfd.revents & POLLNVAL) {
+ LOG(Warning) << "Disabling " << notifierType(event.type)
+ << " due to invalid file descriptor "
+ << pfd.fd;
+ unregisterEventNotifier(notifier);
+ continue;
+ }
+
+ if (pfd.revents & event.events)
+ notifier->activated.emit(notifier);
+ }
+ }
+}
+
+void EventDispatcherPoll::processTimers()
+{
+ struct timespec ts;
+ uint64_t now;
+ clock_gettime(CLOCK_MONOTONIC, &ts);
+ now = ts.tv_sec * 1000000000ULL + ts.tv_nsec;
+
+ while (!timers_.empty()) {
+ Timer *timer = timers_.front();
+ if (timer->deadline() > now)
+ break;
+
+ timers_.pop_front();
+ timer->stop();
+ timer->timeout.emit(timer);
+ }
+}
+
+} /* namespace libcamera */