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>
This commit is contained in:
Laurent Pinchart 2019-01-04 21:26:14 +02:00
parent 1a57bcb8d1
commit 8356f8a6ab
5 changed files with 302 additions and 5 deletions

View file

@ -10,5 +10,6 @@
#include <libcamera/camera.h>
#include <libcamera/camera_manager.h>
#include <libcamera/event_dispatcher.h>
#include <libcamera/event_dispatcher_poll.h>
#endif /* __LIBCAMERA_LIBCAMERA_H__ */

View file

@ -9,6 +9,7 @@
#include <libcamera/event_dispatcher.h>
#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.
@ -207,11 +209,18 @@ void CameraManager::setEventDispatcher(EventDispatcher *dispatcher)
/**
* \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, and no custom event dispatcher may be installed anymore.
*
* \return Pointer to the event dispatcher
*/
EventDispatcher *CameraManager::eventDispatcher()
{
if (!dispatcher_)
dispatcher_ = new EventDispatcherPoll();
return dispatcher_;
}

View file

@ -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 */

View file

@ -0,0 +1,50 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
/*
* Copyright (C) 2019, Google Inc.
*
* event_dispatcher_poll.h - Poll-based event dispatcher
*/
#ifndef __LIBCAMERA_EVENT_DISPATCHER_POLL_H__
#define __LIBCAMERA_EVENT_DISPATCHER_POLL_H__
#include <libcamera/event_dispatcher.h>
#include <list>
#include <map>
#include <vector>
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<int, EventNotifierSetPoll> notifiers_;
std::list<Timer *> timers_;
void processNotifiers(std::vector<struct pollfd> &pollfds);
void processTimers();
};
} /* namespace libcamera */
#endif /* __LIBCAMERA_EVENT_DISPATCHER_POLL_H__ */

View file

@ -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',