[libcamera-devel] [PATCH 3/4] libcamera: event_dispatcher: Add interrupt() function

Kieran Bingham kieran.bingham at ideasonboard.com
Wed Jan 23 12:43:21 CET 2019


Hi Laurent,

On 23/01/2019 08:59, Laurent Pinchart wrote:
> The new interrupt() function allows interrupting in-progress blocking
> processEvents() calls. This is useful to stop running event loops.


Reviewed-by: Kieran Bingham <kieran.bingham at ideasonboard.com>

> Signed-off-by: Laurent Pinchart <laurent.pinchart at ideasonboard.com>
> ---
>  include/libcamera/event_dispatcher.h          |  2 ++
>  src/libcamera/event_dispatcher.cpp            | 10 ++++++
>  src/libcamera/event_dispatcher_poll.cpp       | 32 ++++++++++++++++++-
>  src/libcamera/include/event_dispatcher_poll.h |  3 ++
>  4 files changed, 46 insertions(+), 1 deletion(-)
> 
> diff --git a/include/libcamera/event_dispatcher.h b/include/libcamera/event_dispatcher.h
> index c20518c6866d..cb06bf208e12 100644
> --- a/include/libcamera/event_dispatcher.h
> +++ b/include/libcamera/event_dispatcher.h
> @@ -26,6 +26,8 @@ public:
>  	virtual void unregisterTimer(Timer *timer) = 0;
>  
>  	virtual void processEvents() = 0;
> +
> +	virtual void interrupt() = 0;
>  };
>  
>  } /* namespace libcamera */
> diff --git a/src/libcamera/event_dispatcher.cpp b/src/libcamera/event_dispatcher.cpp
> index f7c40734095e..b82c59c3f5dc 100644
> --- a/src/libcamera/event_dispatcher.cpp
> +++ b/src/libcamera/event_dispatcher.cpp
> @@ -104,4 +104,14 @@ EventDispatcher::~EventDispatcher()
>   * 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 */
> diff --git a/src/libcamera/event_dispatcher_poll.cpp b/src/libcamera/event_dispatcher_poll.cpp
> index 6e0609c34ddc..a2674ab31135 100644
> --- a/src/libcamera/event_dispatcher_poll.cpp
> +++ b/src/libcamera/event_dispatcher_poll.cpp
> @@ -8,7 +8,10 @@
>  #include <algorithm>
>  #include <iomanip>
>  #include <poll.h>
> +#include <stdint.h>
>  #include <string.h>
> +#include <sys/eventfd.h>
> +#include <unistd.h>
>  
>  #include <libcamera/event_notifier.h>
>  #include <libcamera/timer.h>
> @@ -43,10 +46,18 @@ static const char *notifierType(EventNotifier::Type type)
>  
>  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);

Hrm ... I didn't know about eventfd. I'd always used a pair of pipes.

Every day's a school day :)

The man page says it's linux-specific - but I think we're OK with that :-)


> +	if (eventfd_ < 0)
> +		LOG(Event, Fatal) << "Unable to create eventfd";
>  }
>  
>  EventDispatcherPoll::~EventDispatcherPoll()
>  {
> +	close(eventfd_);
>  }
>  
>  void EventDispatcherPoll::registerEventNotifier(EventNotifier *notifier)
> @@ -123,11 +134,13 @@ void EventDispatcherPoll::processEvents()
>  
>  	/* Create the pollfd array. */
>  	std::vector<struct pollfd> pollfds;
> -	pollfds.reserve(notifiers_.size());
> +	pollfds.reserve(notifiers_.size() + 1);
>  
>  	for (auto notifier : notifiers_)
>  		pollfds.push_back({ notifier.first, notifier.second.events(), 0 });
>  
> +	pollfds.push_back({ eventfd_, POLLIN, 0 });
> +
>  	/* Wait for events and process notifiers and timers. */
>  	do {
>  		ret = poll(&pollfds);
> @@ -137,12 +150,20 @@ void EventDispatcherPoll::processEvents()
>  		ret = -errno;
>  		LOG(Event, Warning) << "poll() failed with " << strerror(-ret);
>  	} else if (ret > 0) {
> +		processInterrupt(pollfds.back());
> +		pollfds.pop_back();
>  		processNotifiers(pollfds);
>  	}
>  
>  	processTimers();
>  }
>  
> +void EventDispatcherPoll::interrupt()
> +{
> +	uint64_t value = 1;
> +	write(eventfd_, &value, sizeof(value));
> +}
> +
>  short EventDispatcherPoll::EventNotifierSetPoll::events() const
>  {
>  	short events = 0;
> @@ -186,6 +207,15 @@ int EventDispatcherPoll::poll(std::vector<struct pollfd> *pollfds)
>  		     nextTimer ? &timeout : nullptr, nullptr);
>  }
>  
> +void EventDispatcherPoll::processInterrupt(const struct pollfd &pfd)
> +{
> +	if (!pfd.revents & POLLIN)
> +		return;
> +
> +	uint64_t value;
> +	read(eventfd_, &value, sizeof(value));

Should we check that the value read was expected?

I guess this could be expanded upon to use an events bitfield or enum to
actually pass meaningful messages too.

I'm not sure what those event messages would be yet - so I don't think
we need to implement without a use-case.

--
Regards

Kieran


> +}
> +
>  void EventDispatcherPoll::processNotifiers(const std::vector<struct pollfd> &pollfds)
>  {
>  	static const struct {
> diff --git a/src/libcamera/include/event_dispatcher_poll.h b/src/libcamera/include/event_dispatcher_poll.h
> index ac3efde082b4..1c0066c24dc8 100644
> --- a/src/libcamera/include/event_dispatcher_poll.h
> +++ b/src/libcamera/include/event_dispatcher_poll.h
> @@ -31,6 +31,7 @@ public:
>  	void unregisterTimer(Timer *timer);
>  
>  	void processEvents();
> +	void interrupt();
>  
>  private:
>  	struct EventNotifierSetPoll {
> @@ -40,8 +41,10 @@ private:
>  
>  	std::map<int, EventNotifierSetPoll> notifiers_;
>  	std::list<Timer *> timers_;
> +	int eventfd_;
>  
>  	int poll(std::vector<struct pollfd> *pollfds);
> +	void processInterrupt(const struct pollfd &pfd);
>  	void processNotifiers(const std::vector<struct pollfd> &pollfds);
>  	void processTimers();
>  };
> 

-- 
Regards
--
Kieran


More information about the libcamera-devel mailing list