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

Kieran Bingham kieran.bingham at ideasonboard.com
Fri Dec 4 11:32:31 CET 2020


Hi Umang,

On 04/12/2020 07:52, 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>

Thanks, this all fixes the build and works nicely.

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

Merging this series now.

> ---
> Updated patch as per Laurent's suggestion on v3 (4/4).
> ---
> 
>  event_loop.cpp | 99 ++++++++++++++++++++++++++++++++++++++++++++++++++
>  event_loop.h   | 45 +++++++++++++++++++++++
>  meson.build    |  2 +
>  simple-cam.cpp | 35 +++++++++++-------
>  4 files changed, 168 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..7b6ea65
> --- /dev/null
> +++ b/event_loop.cpp
> @@ -0,0 +1,99 @@
> +/* 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(int fd, short event, void *arg)
> +{
> +	EventLoop *self = static_cast<EventLoop *>(arg);
> +	self->exit();
> +}
> +
> +void EventLoop::timeout(unsigned int sec)
> +{
> +	struct event *ev;
> +	struct timeval tv;
> +
> +	tv.tv_sec = sec;
> +	tv.tv_usec = 0;
> +	ev = evtimer_new(event_, &timeoutTriggered, 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..003c3d8
> --- /dev/null
> +++ b/event_loop.h
> @@ -0,0 +1,45 @@
> +/* 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 callLater(const std::function<void()> &func);
> +
> +private:
> +	static EventLoop *instance_;
> +
> +	static void timeoutTriggered(int fd, short event, void *arg);
> +
> +	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
> 

-- 
Regards
--
Kieran


More information about the libcamera-devel mailing list