libcamera: event_dispatcher: Add interrupt() function

The new interrupt() function allows interrupting in-progress blocking
processEvents() calls. This is useful to stop running event loops.

Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Reviewed-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
Reviewed-by: Niklas Söderlund <niklas.soderlund@ragnatech.se>
This commit is contained in:
Laurent Pinchart 2019-01-23 10:04:23 +02:00
parent d370c1b46e
commit 4d470eb37f
4 changed files with 46 additions and 1 deletions

View file

@ -26,6 +26,8 @@ public:
virtual void unregisterTimer(Timer *timer) = 0; virtual void unregisterTimer(Timer *timer) = 0;
virtual void processEvents() = 0; virtual void processEvents() = 0;
virtual void interrupt() = 0;
}; };
} /* namespace libcamera */ } /* namespace libcamera */

View file

@ -104,4 +104,14 @@ EventDispatcher::~EventDispatcher()
* it before returning. * it before returning.
*/ */
/**
* \fn EventDispatcher::interrupt()
* \brief Interrupt any running processEvents() call as soon as possible
*
* Calling this function interrupts any blocking processEvents() call in
* progress. The processEvents() function will return as soon as possible,
* after processing pending timers and events. If processEvents() isn't in
* progress, it will be interrupted immediately the next time it gets called.
*/
} /* namespace libcamera */ } /* namespace libcamera */

View file

@ -8,7 +8,10 @@
#include <algorithm> #include <algorithm>
#include <iomanip> #include <iomanip>
#include <poll.h> #include <poll.h>
#include <stdint.h>
#include <string.h> #include <string.h>
#include <sys/eventfd.h>
#include <unistd.h>
#include <libcamera/event_notifier.h> #include <libcamera/event_notifier.h>
#include <libcamera/timer.h> #include <libcamera/timer.h>
@ -43,10 +46,18 @@ static const char *notifierType(EventNotifier::Type type)
EventDispatcherPoll::EventDispatcherPoll() EventDispatcherPoll::EventDispatcherPoll()
{ {
/*
* Create the event fd. Failures are fatal as we can't implement an
* interruptible dispatcher without the fd.
*/
eventfd_ = eventfd(0, EFD_CLOEXEC | EFD_NONBLOCK);
if (eventfd_ < 0)
LOG(Event, Fatal) << "Unable to create eventfd";
} }
EventDispatcherPoll::~EventDispatcherPoll() EventDispatcherPoll::~EventDispatcherPoll()
{ {
close(eventfd_);
} }
void EventDispatcherPoll::registerEventNotifier(EventNotifier *notifier) void EventDispatcherPoll::registerEventNotifier(EventNotifier *notifier)
@ -123,11 +134,13 @@ void EventDispatcherPoll::processEvents()
/* Create the pollfd array. */ /* Create the pollfd array. */
std::vector<struct pollfd> pollfds; std::vector<struct pollfd> pollfds;
pollfds.reserve(notifiers_.size()); pollfds.reserve(notifiers_.size() + 1);
for (auto notifier : notifiers_) for (auto notifier : notifiers_)
pollfds.push_back({ notifier.first, notifier.second.events(), 0 }); pollfds.push_back({ notifier.first, notifier.second.events(), 0 });
pollfds.push_back({ eventfd_, POLLIN, 0 });
/* Wait for events and process notifiers and timers. */ /* Wait for events and process notifiers and timers. */
do { do {
ret = poll(&pollfds); ret = poll(&pollfds);
@ -137,12 +150,20 @@ void EventDispatcherPoll::processEvents()
ret = -errno; ret = -errno;
LOG(Event, Warning) << "poll() failed with " << strerror(-ret); LOG(Event, Warning) << "poll() failed with " << strerror(-ret);
} else if (ret > 0) { } else if (ret > 0) {
processInterrupt(pollfds.back());
pollfds.pop_back();
processNotifiers(pollfds); processNotifiers(pollfds);
} }
processTimers(); processTimers();
} }
void EventDispatcherPoll::interrupt()
{
uint64_t value = 1;
write(eventfd_, &value, sizeof(value));
}
short EventDispatcherPoll::EventNotifierSetPoll::events() const short EventDispatcherPoll::EventNotifierSetPoll::events() const
{ {
short events = 0; short events = 0;
@ -186,6 +207,15 @@ int EventDispatcherPoll::poll(std::vector<struct pollfd> *pollfds)
nextTimer ? &timeout : nullptr, nullptr); nextTimer ? &timeout : nullptr, nullptr);
} }
void EventDispatcherPoll::processInterrupt(const struct pollfd &pfd)
{
if (!pfd.revents & POLLIN)
return;
uint64_t value;
read(eventfd_, &value, sizeof(value));
}
void EventDispatcherPoll::processNotifiers(const std::vector<struct pollfd> &pollfds) void EventDispatcherPoll::processNotifiers(const std::vector<struct pollfd> &pollfds)
{ {
static const struct { static const struct {

View file

@ -31,6 +31,7 @@ public:
void unregisterTimer(Timer *timer); void unregisterTimer(Timer *timer);
void processEvents(); void processEvents();
void interrupt();
private: private:
struct EventNotifierSetPoll { struct EventNotifierSetPoll {
@ -40,8 +41,10 @@ private:
std::map<int, EventNotifierSetPoll> notifiers_; std::map<int, EventNotifierSetPoll> notifiers_;
std::list<Timer *> timers_; std::list<Timer *> timers_;
int eventfd_;
int poll(std::vector<struct pollfd> *pollfds); int poll(std::vector<struct pollfd> *pollfds);
void processInterrupt(const struct pollfd &pfd);
void processNotifiers(const std::vector<struct pollfd> &pollfds); void processNotifiers(const std::vector<struct pollfd> &pollfds);
void processTimers(); void processTimers();
}; };