[libcamera-devel] [PATCH v3 4/4] simple-cam: Provide event-loop backed by libevent

Umang Jain email at uajain.com
Fri Dec 4 08:00:10 CET 2020


Hi reviewers,

On 12/4/20 12:24 PM, Umang Jain wrote:
> libcamera moved its EventDispatcher and Timer API to its internal API,
> since providing an event loop to applications should not be the job of
> libcamera. Application utility like cam, were ported to use libevent,
> hence inspired from that, un-break simple-cam by using the similar
> implementation to replace the EventDispatcher and Timer functionality
> by libevent.
>
> Signed-off-by: Umang Jain <email at uajain.com>
> ---
>   event_loop.cpp | 103 +++++++++++++++++++++++++++++++++++++++++++++++++
>   event_loop.h   |  44 +++++++++++++++++++++
>   meson.build    |   2 +
>   simple-cam.cpp |  35 ++++++++++-------
>   4 files changed, 171 insertions(+), 13 deletions(-)
>   create mode 100644 event_loop.cpp
>   create mode 100644 event_loop.h
>
> diff --git a/event_loop.cpp b/event_loop.cpp
> new file mode 100644
> index 0000000..f40a635
> --- /dev/null
> +++ b/event_loop.cpp
> @@ -0,0 +1,103 @@
> +/* SPDX-License-Identifier: GPL-2.0-or-later */
> +/*
> + * Copyright (C) 2020, Google Inc.
> + *
> + * event_loop.cpp - Event loop based on cam
> + */
> +
> +#include "event_loop.h"
> +
> +#include <assert.h>
> +#include <event2/event.h>
> +#include <event2/thread.h>
> +
> +EventLoop *EventLoop::instance_ = nullptr;
> +
> +EventLoop::EventLoop()
> +{
> +	assert(!instance_);
> +
> +	evthread_use_pthreads();
> +	event_ = event_base_new();
> +	instance_ = this;
> +}
> +
> +EventLoop::~EventLoop()
> +{
> +	instance_ = nullptr;
> +
> +	event_base_free(event_);
> +	libevent_global_shutdown();
> +}
> +
> +int EventLoop::exec()
> +{
> +	exitCode_ = -1;
> +	exit_.store(false, std::memory_order_release);
> +
> +	while (!exit_.load(std::memory_order_acquire)) {
> +		dispatchCalls();
> +		event_base_loop(event_, EVLOOP_NO_EXIT_ON_EMPTY);
> +	}
> +
> +	return exitCode_;
> +}
> +
> +void EventLoop::exit(int code)
> +{
> +	exitCode_ = code;
> +	exit_.store(true, std::memory_order_release);
> +	interrupt();
> +}
> +
> +void EventLoop::interrupt()
> +{
> +	event_base_loopbreak(event_);
> +}
> +
> +void EventLoop::timeoutTriggered()
> +{
> +	exit();
> +}
> +
> +void timeoutCb(int fd, short event, void *arg)
> +{
> +	EventLoop *ptr = static_cast<EventLoop *>(arg);
> +	ptr->timeoutTriggered();
> +}
This is a bit special. A non-member function acting as proxy callback to 
the EventLoop::timeout(). I didn't find a way to pass a member function, 
apparently it seems impossible because you need a object to invoke the 
member function upon. Digging a bit more into this practice, I found 
this particular issue's solution/recommendation here:
https://isocpp.org/wiki/faq/pointers-to-members#memfnptr-vs-fnptr
> +
> +void EventLoop::timeout(unsigned int sec)
> +{
> +	struct event *ev;
> +	struct timeval tv;
> +
> +	tv.tv_sec = sec;
> +	tv.tv_usec = 0;
> +	ev = evtimer_new(event_, &timeoutCb, this);
> +	evtimer_add(ev, &tv);
> +}
> +
> +void EventLoop::callLater(const std::function<void()> &func)
> +{
> +	{
> +		std::unique_lock<std::mutex> locker(lock_);
> +		calls_.push_back(func);
> +	}
> +
> +	interrupt();
> +}
> +
> +void EventLoop::dispatchCalls()
> +{
> +	std::unique_lock<std::mutex> locker(lock_);
> +
> +	for (auto iter = calls_.begin(); iter != calls_.end(); ) {
> +		std::function<void()> call = std::move(*iter);
> +
> +		iter = calls_.erase(iter);
> +
> +		locker.unlock();
> +		call();
> +		locker.lock();
> +	}
> +}
> diff --git a/event_loop.h b/event_loop.h
> new file mode 100644
> index 0000000..fcb964f
> --- /dev/null
> +++ b/event_loop.h
> @@ -0,0 +1,44 @@
> +/* SPDX-License-Identifier: GPL-2.0-or-later */
> +/*
> + * Copyright (C) 2020, Google Inc.
> + *
> + * event_loop.h - Event loop based on cam's
> + */
> +#ifndef __SIMPLE_CAM_EVENT_LOOP_H__
> +#define __SIMPLE_CAM_EVENT_LOOP_H__
> +
> +#include <atomic>
> +#include <functional>
> +#include <list>
> +#include <mutex>
> +
> +struct event_base;
> +
> +class EventLoop
> +{
> +public:
> +	EventLoop();
> +	~EventLoop();
> +
> +	void exit(int code = 0);
> +	int exec();
> +
> +	void timeout(unsigned int sec);
> +	void timeoutTriggered();
> +	void callLater(const std::function<void()> &func);
> +
> +private:
> +	static EventLoop *instance_;
> +
> +	struct event_base *event_;
> +	std::atomic<bool> exit_;
> +	int exitCode_;
> +
> +	std::list<std::function<void()>> calls_;
> +	std::mutex lock_;
> +
> +	void interrupt();
> +	void dispatchCalls();
> +};
> +
> +#endif /* __SIMPLE_CAM_EVENT_LOOP_H__ */
> diff --git a/meson.build b/meson.build
> index c312f2c..4d580c2 100644
> --- a/meson.build
> +++ b/meson.build
> @@ -8,12 +8,14 @@ project('simple-cam', 'c', 'cpp',
>   # simple-cam.cpp is the fully commented application
>   src_files = files([
>   	'simple-cam.cpp',
> +	'event_loop.cpp',
>   ])
>   
>   # Point your PKG_CONFIG_PATH environment variable to the
>   # libcamera install path camera.pc file ($prefix/lib/pkgconfig/camera.pc)
>   libcamera_deps = [
>         dependency('camera', required : true),
> +      dependency('libevent_pthreads'),
>   ]
>   
>   cpp_arguments = [ '-Wno-unused-parameter', ]
> diff --git a/simple-cam.cpp b/simple-cam.cpp
> index bfe30d7..6d1d84f 100644
> --- a/simple-cam.cpp
> +++ b/simple-cam.cpp
> @@ -11,8 +11,13 @@
>   
>   #include <libcamera/libcamera.h>
>   
> +#include "event_loop.h"
> +
> +#define TIMEOUT_SEC 3
> +
>   using namespace libcamera;
>   std::shared_ptr<Camera> camera;
> +EventLoop loop;
>   
>   /*
>    * --------------------------------------------------------------------
> @@ -21,13 +26,26 @@ std::shared_ptr<Camera> camera;
>    * For each Camera::requestCompleted Signal emitted from the Camera the
>    * connected Slot is invoked.
>    *
> + * The Slot is invoked in the CameraManager's thread, hence one should avoid
> + * any heavy processing here. The processing of the request shall be re-directed
> + * to the application's thread instead, so as not to block the CameraManager's
> + * thread for large amount of time.
> + *
>    * The Slot receives the Request as a parameter.
>    */
> +
> +static void processRequest(Request *request);
> +
>   static void requestComplete(Request *request)
>   {
>   	if (request->status() == Request::RequestCancelled)
>   		return;
>   
> +	loop.callLater(std::bind(&processRequest, request));
> +}
> +
> +static void processRequest(Request *request)
> +{
>   	const Request::BufferMap &buffers = request->buffers();
>   
>   	for (auto bufferPair : buffers) {
> @@ -320,20 +338,11 @@ int main()
>   	 *
>   	 * In order to dispatch events received from the video devices, such
>   	 * as buffer completions, an event loop has to be run.
> -	 *
> -	 * Libcamera provides its own default event dispatcher realized by
> -	 * polling a set of file descriptors, but applications can integrate
> -	 * their own even loop with the Libcamera EventDispatcher.
> -	 *
> -	 * Here, as an example, run the poll-based EventDispatcher for 3
> -	 * seconds.
>   	 */
> -	EventDispatcher *dispatcher = cm->eventDispatcher();
> -	Timer timer;
> -	timer.start(3000);
> -	while (timer.isRunning())
> -		dispatcher->processEvents();
> -
> +	loop.timeout(TIMEOUT_SEC);
> +	int ret = loop.exec();
> +	std::cout << "Capture ran for " << TIMEOUT_SEC << " seconds and "
> +		  << "stopped with exit status: " << ret << std::endl;
>   	/*
>   	 * --------------------------------------------------------------------
>   	 * Clean Up



More information about the libcamera-devel mailing list