[libcamera-devel] [PATCH v3 4/4] simple-cam: Provide event-loop backed by libevent
Laurent Pinchart
laurent.pinchart at ideasonboard.com
Fri Dec 4 08:28:18 CET 2020
Hi Umang,
On Fri, Dec 04, 2020 at 12:30:10PM +0530, Umang Jain wrote:
> 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
This is correct, you can't pass a non-static member function, but you
can pass a static member function. This could thus be written
void EventLoop::timeoutTriggered(int fd, short event, void *arg)
{
EventLoop *self = static_cast<EventLoop *>(arg);
self->exit();
}
with the function declared as
static void timeoutTriggered(int fd, short event, void *arg);
in the private section of the EventLoop class.
> > +
> > +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
--
Regards,
Laurent Pinchart
More information about the libcamera-devel
mailing list