From patchwork Mon Jan 7 23:11:47 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Laurent Pinchart X-Patchwork-Id: 170 Return-Path: Received: from perceval.ideasonboard.com (perceval.ideasonboard.com [IPv6:2001:4b98:dc2:55:216:3eff:fef7:d647]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id EFDD760B3A for ; Tue, 8 Jan 2019 00:10:49 +0100 (CET) Received: from avalon.bb.dnainternet.fi (dfj612ybrt5fhg77mgycy-3.rev.dnainternet.fi [IPv6:2001:14ba:21f5:5b00:2e86:4862:ef6a:2804]) by perceval.ideasonboard.com (Postfix) with ESMTPSA id 86F16E51 for ; Tue, 8 Jan 2019 00:10:49 +0100 (CET) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1546902649; bh=SiRsLN0Zc/8GSpeiZorpxXuDQ+BXneo3nlJDf1BZCEM=; h=From:To:Subject:Date:In-Reply-To:References:From; b=bWmuUJmmBx4zO2qDRw0R2qodq/KS2i8hluOhDMOvq1ZzQIoP1U2EtpHEmGWX/IclI j0IWFVN/b2q+skj2oP5WQnuyiLKoG1sTlnpxq/vY6mkokvAsJvIAAgrNXoqnlWb+CA M42A2Fdoj5l5KWo4JZnf+H6cROH8IbTpr0Ap3JW0= From: Laurent Pinchart To: libcamera-devel@lists.libcamera.org Date: Tue, 8 Jan 2019 01:11:47 +0200 Message-Id: <20190107231151.23291-8-laurent.pinchart@ideasonboard.com> X-Mailer: git-send-email 2.19.2 In-Reply-To: <20190107231151.23291-1-laurent.pinchart@ideasonboard.com> References: <20190107231151.23291-1-laurent.pinchart@ideasonboard.com> MIME-Version: 1.0 Subject: [libcamera-devel] [PATCH v2 07/11] libcamera: Add a poll-based event dispatcher X-BeenThere: libcamera-devel@lists.libcamera.org X-Mailman-Version: 2.1.23 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Mon, 07 Jan 2019 23:10:50 -0000 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 --- Changes since v1: - Fix ASSERT() condition check - Ignore duplicate notifiers - Clarify warning messages for event notifier (un)registration errors --- include/libcamera/libcamera.h | 1 + src/libcamera/camera_manager.cpp | 19 +- src/libcamera/event_dispatcher_poll.cpp | 234 ++++++++++++++++++ src/libcamera/include/event_dispatcher_poll.h | 50 ++++ src/libcamera/meson.build | 2 + 5 files changed, 301 insertions(+), 5 deletions(-) create mode 100644 src/libcamera/event_dispatcher_poll.cpp create mode 100644 src/libcamera/include/event_dispatcher_poll.h diff --git a/include/libcamera/libcamera.h b/include/libcamera/libcamera.h index 785babefa1c8..2dcaeda49812 100644 --- a/include/libcamera/libcamera.h +++ b/include/libcamera/libcamera.h @@ -10,5 +10,6 @@ #include #include #include +#include #endif /* __LIBCAMERA_LIBCAMERA_H__ */ diff --git a/src/libcamera/camera_manager.cpp b/src/libcamera/camera_manager.cpp index cbfd977f1e3c..348ea2fedf64 100644 --- a/src/libcamera/camera_manager.cpp +++ b/src/libcamera/camera_manager.cpp @@ -9,6 +9,7 @@ #include #include "device_enumerator.h" +#include "event_dispatcher_poll.h" #include "log.h" #include "pipeline_handler.h" @@ -188,9 +189,10 @@ CameraManager *CameraManager::instance() * \param dispatcher Pointer to the event dispatcher * * libcamera requires an event dispatcher to integrate event notification and - * timers with the application event loop. Applications shall call this function - * once and only once before the camera manager is started with start() to set - * the event dispatcher. + * timers with the application event loop. Applications that want to provide + * their own event dispatcher shall call this function once and only once before + * the camera manager is started with start(). If no event dispatcher is + * provided, a default poll-based implementation will be used. * * The CameraManager takes ownership of the event dispatcher and will delete it * when the application terminates. @@ -208,11 +210,18 @@ void CameraManager::setEventDispatcher(EventDispatcher *dispatcher) /** * \fn CameraManager::eventDispatcher() * \brief Retrieve the event dispatcher - * \return Pointer to the event dispatcher, or nullptr if no event dispatcher - * has been set + * + * This function retrieves the event dispatcher set with setEventDispatcher(). + * If no dispatcher has been set, a default poll-based implementation is created + * and returned. + * + * \return Pointer to the event dispatcher */ EventDispatcher *CameraManager::eventDispatcher() { + if (!dispatcher_) + dispatcher_ = new EventDispatcherPoll(); + return dispatcher_; } diff --git a/src/libcamera/event_dispatcher_poll.cpp b/src/libcamera/event_dispatcher_poll.cpp new file mode 100644 index 000000000000..0cd08b2b4cc1 --- /dev/null +++ b/src/libcamera/event_dispatcher_poll.cpp @@ -0,0 +1,234 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* + * Copyright (C) 2019, Google Inc. + * + * event_dispatcher_poll.cpp - Poll-based event dispatcher + */ + +#include +#include +#include +#include + +#include +#include + +#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 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 &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); + } + + 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 */ diff --git a/src/libcamera/include/event_dispatcher_poll.h b/src/libcamera/include/event_dispatcher_poll.h new file mode 100644 index 000000000000..600289e455e2 --- /dev/null +++ b/src/libcamera/include/event_dispatcher_poll.h @@ -0,0 +1,50 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* + * Copyright (C) 2019, Google Inc. + * + * event_loop.h - Camera object interface + */ +#ifndef __LIBCAMERA_EVENT_DISPATCHER_POLL_H__ +#define __LIBCAMERA_EVENT_DISPATCHER_POLL_H__ + +#include + +#include +#include +#include + +namespace libcamera { + +class EventNotifier; +class Timer; + +class EventDispatcherPoll final : public EventDispatcher +{ +public: + EventDispatcherPoll(); + ~EventDispatcherPoll(); + + void registerEventNotifier(EventNotifier *notifier); + void unregisterEventNotifier(EventNotifier *notifier); + + void registerTimer(Timer *timer); + void unregisterTimer(Timer *timer); + + void processEvents(); + +private: + struct EventNotifierSetPoll { + short events() const; + EventNotifier *notifiers[3]; + }; + + std::map notifiers_; + std::list timers_; + + void processNotifiers(std::vector &pollfds); + void processTimers(); +}; + +} /* namespace libcamera */ + +#endif /* __LIBCAMERA_EVENT_DISPATCHER_POLL_H__ */ diff --git a/src/libcamera/meson.build b/src/libcamera/meson.build index 61fb93205b34..abf9a71d4172 100644 --- a/src/libcamera/meson.build +++ b/src/libcamera/meson.build @@ -3,6 +3,7 @@ libcamera_sources = files([ 'camera_manager.cpp', 'device_enumerator.cpp', 'event_dispatcher.cpp', + 'event_dispatcher_poll.cpp', 'event_notifier.cpp', 'log.cpp', 'media_device.cpp', @@ -14,6 +15,7 @@ libcamera_sources = files([ libcamera_headers = files([ 'include/device_enumerator.h', + 'include/event_dispatcher_poll.h', 'include/log.h', 'include/media_device.h', 'include/media_object.h',