[{"id":14035,"web_url":"https://patchwork.libcamera.org/comment/14035/","msgid":"<4ca38784-ca11-cc5b-6b9b-2057465a371b@uajain.com>","date":"2020-12-04T07:00:10","subject":"Re: [libcamera-devel] [PATCH v3 4/4] simple-cam: Provide event-loop\n\tbacked by libevent","submitter":{"id":1,"url":"https://patchwork.libcamera.org/api/people/1/","name":"Umang Jain","email":"email@uajain.com"},"content":"Hi reviewers,\n\nOn 12/4/20 12:24 PM, Umang Jain wrote:\n> libcamera moved its EventDispatcher and Timer API to its internal API,\n> since providing an event loop to applications should not be the job of\n> libcamera. Application utility like cam, were ported to use libevent,\n> hence inspired from that, un-break simple-cam by using the similar\n> implementation to replace the EventDispatcher and Timer functionality\n> by libevent.\n>\n> Signed-off-by: Umang Jain <email@uajain.com>\n> ---\n>   event_loop.cpp | 103 +++++++++++++++++++++++++++++++++++++++++++++++++\n>   event_loop.h   |  44 +++++++++++++++++++++\n>   meson.build    |   2 +\n>   simple-cam.cpp |  35 ++++++++++-------\n>   4 files changed, 171 insertions(+), 13 deletions(-)\n>   create mode 100644 event_loop.cpp\n>   create mode 100644 event_loop.h\n>\n> diff --git a/event_loop.cpp b/event_loop.cpp\n> new file mode 100644\n> index 0000000..f40a635\n> --- /dev/null\n> +++ b/event_loop.cpp\n> @@ -0,0 +1,103 @@\n> +/* SPDX-License-Identifier: GPL-2.0-or-later */\n> +/*\n> + * Copyright (C) 2020, Google Inc.\n> + *\n> + * event_loop.cpp - Event loop based on cam\n> + */\n> +\n> +#include \"event_loop.h\"\n> +\n> +#include <assert.h>\n> +#include <event2/event.h>\n> +#include <event2/thread.h>\n> +\n> +EventLoop *EventLoop::instance_ = nullptr;\n> +\n> +EventLoop::EventLoop()\n> +{\n> +\tassert(!instance_);\n> +\n> +\tevthread_use_pthreads();\n> +\tevent_ = event_base_new();\n> +\tinstance_ = this;\n> +}\n> +\n> +EventLoop::~EventLoop()\n> +{\n> +\tinstance_ = nullptr;\n> +\n> +\tevent_base_free(event_);\n> +\tlibevent_global_shutdown();\n> +}\n> +\n> +int EventLoop::exec()\n> +{\n> +\texitCode_ = -1;\n> +\texit_.store(false, std::memory_order_release);\n> +\n> +\twhile (!exit_.load(std::memory_order_acquire)) {\n> +\t\tdispatchCalls();\n> +\t\tevent_base_loop(event_, EVLOOP_NO_EXIT_ON_EMPTY);\n> +\t}\n> +\n> +\treturn exitCode_;\n> +}\n> +\n> +void EventLoop::exit(int code)\n> +{\n> +\texitCode_ = code;\n> +\texit_.store(true, std::memory_order_release);\n> +\tinterrupt();\n> +}\n> +\n> +void EventLoop::interrupt()\n> +{\n> +\tevent_base_loopbreak(event_);\n> +}\n> +\n> +void EventLoop::timeoutTriggered()\n> +{\n> +\texit();\n> +}\n> +\n> +void timeoutCb(int fd, short event, void *arg)\n> +{\n> +\tEventLoop *ptr = static_cast<EventLoop *>(arg);\n> +\tptr->timeoutTriggered();\n> +}\nThis is a bit special. A non-member function acting as proxy callback to \nthe EventLoop::timeout(). I didn't find a way to pass a member function, \napparently it seems impossible because you need a object to invoke the \nmember function upon. Digging a bit more into this practice, I found \nthis particular issue's solution/recommendation here:\nhttps://isocpp.org/wiki/faq/pointers-to-members#memfnptr-vs-fnptr\n> +\n> +void EventLoop::timeout(unsigned int sec)\n> +{\n> +\tstruct event *ev;\n> +\tstruct timeval tv;\n> +\n> +\ttv.tv_sec = sec;\n> +\ttv.tv_usec = 0;\n> +\tev = evtimer_new(event_, &timeoutCb, this);\n> +\tevtimer_add(ev, &tv);\n> +}\n> +\n> +void EventLoop::callLater(const std::function<void()> &func)\n> +{\n> +\t{\n> +\t\tstd::unique_lock<std::mutex> locker(lock_);\n> +\t\tcalls_.push_back(func);\n> +\t}\n> +\n> +\tinterrupt();\n> +}\n> +\n> +void EventLoop::dispatchCalls()\n> +{\n> +\tstd::unique_lock<std::mutex> locker(lock_);\n> +\n> +\tfor (auto iter = calls_.begin(); iter != calls_.end(); ) {\n> +\t\tstd::function<void()> call = std::move(*iter);\n> +\n> +\t\titer = calls_.erase(iter);\n> +\n> +\t\tlocker.unlock();\n> +\t\tcall();\n> +\t\tlocker.lock();\n> +\t}\n> +}\n> diff --git a/event_loop.h b/event_loop.h\n> new file mode 100644\n> index 0000000..fcb964f\n> --- /dev/null\n> +++ b/event_loop.h\n> @@ -0,0 +1,44 @@\n> +/* SPDX-License-Identifier: GPL-2.0-or-later */\n> +/*\n> + * Copyright (C) 2020, Google Inc.\n> + *\n> + * event_loop.h - Event loop based on cam's\n> + */\n> +#ifndef __SIMPLE_CAM_EVENT_LOOP_H__\n> +#define __SIMPLE_CAM_EVENT_LOOP_H__\n> +\n> +#include <atomic>\n> +#include <functional>\n> +#include <list>\n> +#include <mutex>\n> +\n> +struct event_base;\n> +\n> +class EventLoop\n> +{\n> +public:\n> +\tEventLoop();\n> +\t~EventLoop();\n> +\n> +\tvoid exit(int code = 0);\n> +\tint exec();\n> +\n> +\tvoid timeout(unsigned int sec);\n> +\tvoid timeoutTriggered();\n> +\tvoid callLater(const std::function<void()> &func);\n> +\n> +private:\n> +\tstatic EventLoop *instance_;\n> +\n> +\tstruct event_base *event_;\n> +\tstd::atomic<bool> exit_;\n> +\tint exitCode_;\n> +\n> +\tstd::list<std::function<void()>> calls_;\n> +\tstd::mutex lock_;\n> +\n> +\tvoid interrupt();\n> +\tvoid dispatchCalls();\n> +};\n> +\n> +#endif /* __SIMPLE_CAM_EVENT_LOOP_H__ */\n> diff --git a/meson.build b/meson.build\n> index c312f2c..4d580c2 100644\n> --- a/meson.build\n> +++ b/meson.build\n> @@ -8,12 +8,14 @@ project('simple-cam', 'c', 'cpp',\n>   # simple-cam.cpp is the fully commented application\n>   src_files = files([\n>   \t'simple-cam.cpp',\n> +\t'event_loop.cpp',\n>   ])\n>   \n>   # Point your PKG_CONFIG_PATH environment variable to the\n>   # libcamera install path camera.pc file ($prefix/lib/pkgconfig/camera.pc)\n>   libcamera_deps = [\n>         dependency('camera', required : true),\n> +      dependency('libevent_pthreads'),\n>   ]\n>   \n>   cpp_arguments = [ '-Wno-unused-parameter', ]\n> diff --git a/simple-cam.cpp b/simple-cam.cpp\n> index bfe30d7..6d1d84f 100644\n> --- a/simple-cam.cpp\n> +++ b/simple-cam.cpp\n> @@ -11,8 +11,13 @@\n>   \n>   #include <libcamera/libcamera.h>\n>   \n> +#include \"event_loop.h\"\n> +\n> +#define TIMEOUT_SEC 3\n> +\n>   using namespace libcamera;\n>   std::shared_ptr<Camera> camera;\n> +EventLoop loop;\n>   \n>   /*\n>    * --------------------------------------------------------------------\n> @@ -21,13 +26,26 @@ std::shared_ptr<Camera> camera;\n>    * For each Camera::requestCompleted Signal emitted from the Camera the\n>    * connected Slot is invoked.\n>    *\n> + * The Slot is invoked in the CameraManager's thread, hence one should avoid\n> + * any heavy processing here. The processing of the request shall be re-directed\n> + * to the application's thread instead, so as not to block the CameraManager's\n> + * thread for large amount of time.\n> + *\n>    * The Slot receives the Request as a parameter.\n>    */\n> +\n> +static void processRequest(Request *request);\n> +\n>   static void requestComplete(Request *request)\n>   {\n>   \tif (request->status() == Request::RequestCancelled)\n>   \t\treturn;\n>   \n> +\tloop.callLater(std::bind(&processRequest, request));\n> +}\n> +\n> +static void processRequest(Request *request)\n> +{\n>   \tconst Request::BufferMap &buffers = request->buffers();\n>   \n>   \tfor (auto bufferPair : buffers) {\n> @@ -320,20 +338,11 @@ int main()\n>   \t *\n>   \t * In order to dispatch events received from the video devices, such\n>   \t * as buffer completions, an event loop has to be run.\n> -\t *\n> -\t * Libcamera provides its own default event dispatcher realized by\n> -\t * polling a set of file descriptors, but applications can integrate\n> -\t * their own even loop with the Libcamera EventDispatcher.\n> -\t *\n> -\t * Here, as an example, run the poll-based EventDispatcher for 3\n> -\t * seconds.\n>   \t */\n> -\tEventDispatcher *dispatcher = cm->eventDispatcher();\n> -\tTimer timer;\n> -\ttimer.start(3000);\n> -\twhile (timer.isRunning())\n> -\t\tdispatcher->processEvents();\n> -\n> +\tloop.timeout(TIMEOUT_SEC);\n> +\tint ret = loop.exec();\n> +\tstd::cout << \"Capture ran for \" << TIMEOUT_SEC << \" seconds and \"\n> +\t\t  << \"stopped with exit status: \" << ret << std::endl;\n>   \t/*\n>   \t * --------------------------------------------------------------------\n>   \t * Clean Up","headers":{"Return-Path":"<libcamera-devel-bounces@lists.libcamera.org>","X-Original-To":"parsemail@patchwork.libcamera.org","Delivered-To":"parsemail@patchwork.libcamera.org","Received":["from lancelot.ideasonboard.com (lancelot.ideasonboard.com\n\t[92.243.16.209])\n\tby patchwork.libcamera.org (Postfix) with ESMTPS id 9D848BE176\n\tfor <parsemail@patchwork.libcamera.org>;\n\tFri,  4 Dec 2020 07:00:26 +0000 (UTC)","from lancelot.ideasonboard.com (localhost [IPv6:::1])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTP id 23EF1635D1;\n\tFri,  4 Dec 2020 08:00:26 +0100 (CET)","from mail.uajain.com (static.126.159.217.95.clients.your-server.de\n\t[95.217.159.126])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTPS id 39509635C4\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tFri,  4 Dec 2020 08:00:24 +0100 (CET)"],"Authentication-Results":"lancelot.ideasonboard.com;\n\tdkim=fail reason=\"signature verification failed\" (2048-bit key;\n\tunprotected) header.d=uajain.com header.i=@uajain.com\n\theader.b=\"VJNZCmRE\"; dkim-atps=neutral","DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/simple; d=uajain.com; s=mail;\n\tt=1607065223; bh=1pCFjgsx3931XIlKA+taHH1G1+/prPvTMOX5m9gQWRE=;\n\th=Subject:To:Cc:References:From:In-Reply-To;\n\tb=VJNZCmREVCWFu7/ljAObJg7IrFR+CwK3R6221nqCJ3RXvUcc0pD99Uqtz0slrOBZB\n\tHKfN8dZQO9+Zhi87nZu7RhbGuVuJXn1IruVDOTC73+fH2srCVFfmHWwobvrVc7rEJH\n\t/3+cM3TcnfSgRh7KYLQ48uoqPia0/uQqvynNvUbSTgaaon86woIsh66dGqyMyUi15C\n\t5NZ9HOTX3yKWO81YOmAlemL83mnfGts9rSkttqXt56ZI5DkvX+MVzWuAoxbNxubu0S\n\tOgkQ6uvvbT2qTR3/iyxz5vBglSBbzvjvu0bQQ0bGKaEGMwo1MDQlO8SU2hIr5gJJWP\n\t13cc8K9LoahAw==","To":"libcamera-devel@lists.libcamera.org","References":"<20201204065452.2764628-1-email@uajain.com>\n\t<20201204065452.2764628-5-email@uajain.com>","From":"Umang Jain <email@uajain.com>","Message-ID":"<4ca38784-ca11-cc5b-6b9b-2057465a371b@uajain.com>","Date":"Fri, 4 Dec 2020 12:30:10 +0530","Mime-Version":"1.0","In-Reply-To":"<20201204065452.2764628-5-email@uajain.com>","Content-Language":"en-US","Subject":"Re: [libcamera-devel] [PATCH v3 4/4] simple-cam: Provide event-loop\n\tbacked by libevent","X-BeenThere":"libcamera-devel@lists.libcamera.org","X-Mailman-Version":"2.1.29","Precedence":"list","List-Id":"<libcamera-devel.lists.libcamera.org>","List-Unsubscribe":"<https://lists.libcamera.org/options/libcamera-devel>,\n\t<mailto:libcamera-devel-request@lists.libcamera.org?subject=unsubscribe>","List-Archive":"<https://lists.libcamera.org/pipermail/libcamera-devel/>","List-Post":"<mailto:libcamera-devel@lists.libcamera.org>","List-Help":"<mailto:libcamera-devel-request@lists.libcamera.org?subject=help>","List-Subscribe":"<https://lists.libcamera.org/listinfo/libcamera-devel>,\n\t<mailto:libcamera-devel-request@lists.libcamera.org?subject=subscribe>","Content-Transfer-Encoding":"7bit","Content-Type":"text/plain; charset=\"us-ascii\"; Format=\"flowed\"","Errors-To":"libcamera-devel-bounces@lists.libcamera.org","Sender":"\"libcamera-devel\" <libcamera-devel-bounces@lists.libcamera.org>"}},{"id":14036,"web_url":"https://patchwork.libcamera.org/comment/14036/","msgid":"<20201204072818.GB4109@pendragon.ideasonboard.com>","date":"2020-12-04T07:28:18","subject":"Re: [libcamera-devel] [PATCH v3 4/4] simple-cam: Provide event-loop\n\tbacked by libevent","submitter":{"id":2,"url":"https://patchwork.libcamera.org/api/people/2/","name":"Laurent Pinchart","email":"laurent.pinchart@ideasonboard.com"},"content":"Hi Umang,\n\nOn Fri, Dec 04, 2020 at 12:30:10PM +0530, Umang Jain wrote:\n> Hi reviewers,\n> \n> On 12/4/20 12:24 PM, Umang Jain wrote:\n> > libcamera moved its EventDispatcher and Timer API to its internal API,\n> > since providing an event loop to applications should not be the job of\n> > libcamera. Application utility like cam, were ported to use libevent,\n> > hence inspired from that, un-break simple-cam by using the similar\n> > implementation to replace the EventDispatcher and Timer functionality\n> > by libevent.\n> >\n> > Signed-off-by: Umang Jain <email@uajain.com>\n> > ---\n> >   event_loop.cpp | 103 +++++++++++++++++++++++++++++++++++++++++++++++++\n> >   event_loop.h   |  44 +++++++++++++++++++++\n> >   meson.build    |   2 +\n> >   simple-cam.cpp |  35 ++++++++++-------\n> >   4 files changed, 171 insertions(+), 13 deletions(-)\n> >   create mode 100644 event_loop.cpp\n> >   create mode 100644 event_loop.h\n> >\n> > diff --git a/event_loop.cpp b/event_loop.cpp\n> > new file mode 100644\n> > index 0000000..f40a635\n> > --- /dev/null\n> > +++ b/event_loop.cpp\n> > @@ -0,0 +1,103 @@\n> > +/* SPDX-License-Identifier: GPL-2.0-or-later */\n> > +/*\n> > + * Copyright (C) 2020, Google Inc.\n> > + *\n> > + * event_loop.cpp - Event loop based on cam\n> > + */\n> > +\n> > +#include \"event_loop.h\"\n> > +\n> > +#include <assert.h>\n> > +#include <event2/event.h>\n> > +#include <event2/thread.h>\n> > +\n> > +EventLoop *EventLoop::instance_ = nullptr;\n> > +\n> > +EventLoop::EventLoop()\n> > +{\n> > +\tassert(!instance_);\n> > +\n> > +\tevthread_use_pthreads();\n> > +\tevent_ = event_base_new();\n> > +\tinstance_ = this;\n> > +}\n> > +\n> > +EventLoop::~EventLoop()\n> > +{\n> > +\tinstance_ = nullptr;\n> > +\n> > +\tevent_base_free(event_);\n> > +\tlibevent_global_shutdown();\n> > +}\n> > +\n> > +int EventLoop::exec()\n> > +{\n> > +\texitCode_ = -1;\n> > +\texit_.store(false, std::memory_order_release);\n> > +\n> > +\twhile (!exit_.load(std::memory_order_acquire)) {\n> > +\t\tdispatchCalls();\n> > +\t\tevent_base_loop(event_, EVLOOP_NO_EXIT_ON_EMPTY);\n> > +\t}\n> > +\n> > +\treturn exitCode_;\n> > +}\n> > +\n> > +void EventLoop::exit(int code)\n> > +{\n> > +\texitCode_ = code;\n> > +\texit_.store(true, std::memory_order_release);\n> > +\tinterrupt();\n> > +}\n> > +\n> > +void EventLoop::interrupt()\n> > +{\n> > +\tevent_base_loopbreak(event_);\n> > +}\n> > +\n> > +void EventLoop::timeoutTriggered()\n> > +{\n> > +\texit();\n> > +}\n> > +\n> > +void timeoutCb(int fd, short event, void *arg)\n> > +{\n> > +\tEventLoop *ptr = static_cast<EventLoop *>(arg);\n> > +\tptr->timeoutTriggered();\n> > +}\n>\n> This is a bit special. A non-member function acting as proxy callback to \n> the EventLoop::timeout(). I didn't find a way to pass a member function, \n> apparently it seems impossible because you need a object to invoke the \n> member function upon. Digging a bit more into this practice, I found \n> this particular issue's solution/recommendation here:\n> https://isocpp.org/wiki/faq/pointers-to-members#memfnptr-vs-fnptr\n\nThis is correct, you can't pass a non-static member function, but you\ncan pass a static member function. This could thus be written\n\nvoid EventLoop::timeoutTriggered(int fd, short event, void *arg)\n{\n\tEventLoop *self = static_cast<EventLoop *>(arg);\n\tself->exit();\n}\n\nwith the function declared as\n\n\tstatic void timeoutTriggered(int fd, short event, void *arg);\n\nin the private section of the EventLoop class.\n\n> > +\n> > +void EventLoop::timeout(unsigned int sec)\n> > +{\n> > +\tstruct event *ev;\n> > +\tstruct timeval tv;\n> > +\n> > +\ttv.tv_sec = sec;\n> > +\ttv.tv_usec = 0;\n> > +\tev = evtimer_new(event_, &timeoutCb, this);\n> > +\tevtimer_add(ev, &tv);\n> > +}\n> > +\n> > +void EventLoop::callLater(const std::function<void()> &func)\n> > +{\n> > +\t{\n> > +\t\tstd::unique_lock<std::mutex> locker(lock_);\n> > +\t\tcalls_.push_back(func);\n> > +\t}\n> > +\n> > +\tinterrupt();\n> > +}\n> > +\n> > +void EventLoop::dispatchCalls()\n> > +{\n> > +\tstd::unique_lock<std::mutex> locker(lock_);\n> > +\n> > +\tfor (auto iter = calls_.begin(); iter != calls_.end(); ) {\n> > +\t\tstd::function<void()> call = std::move(*iter);\n> > +\n> > +\t\titer = calls_.erase(iter);\n> > +\n> > +\t\tlocker.unlock();\n> > +\t\tcall();\n> > +\t\tlocker.lock();\n> > +\t}\n> > +}\n> > diff --git a/event_loop.h b/event_loop.h\n> > new file mode 100644\n> > index 0000000..fcb964f\n> > --- /dev/null\n> > +++ b/event_loop.h\n> > @@ -0,0 +1,44 @@\n> > +/* SPDX-License-Identifier: GPL-2.0-or-later */\n> > +/*\n> > + * Copyright (C) 2020, Google Inc.\n> > + *\n> > + * event_loop.h - Event loop based on cam's\n> > + */\n> > +#ifndef __SIMPLE_CAM_EVENT_LOOP_H__\n> > +#define __SIMPLE_CAM_EVENT_LOOP_H__\n> > +\n> > +#include <atomic>\n> > +#include <functional>\n> > +#include <list>\n> > +#include <mutex>\n> > +\n> > +struct event_base;\n> > +\n> > +class EventLoop\n> > +{\n> > +public:\n> > +\tEventLoop();\n> > +\t~EventLoop();\n> > +\n> > +\tvoid exit(int code = 0);\n> > +\tint exec();\n> > +\n> > +\tvoid timeout(unsigned int sec);\n> > +\tvoid timeoutTriggered();\n> > +\tvoid callLater(const std::function<void()> &func);\n> > +\n> > +private:\n> > +\tstatic EventLoop *instance_;\n> > +\n> > +\tstruct event_base *event_;\n> > +\tstd::atomic<bool> exit_;\n> > +\tint exitCode_;\n> > +\n> > +\tstd::list<std::function<void()>> calls_;\n> > +\tstd::mutex lock_;\n> > +\n> > +\tvoid interrupt();\n> > +\tvoid dispatchCalls();\n> > +};\n> > +\n> > +#endif /* __SIMPLE_CAM_EVENT_LOOP_H__ */\n> > diff --git a/meson.build b/meson.build\n> > index c312f2c..4d580c2 100644\n> > --- a/meson.build\n> > +++ b/meson.build\n> > @@ -8,12 +8,14 @@ project('simple-cam', 'c', 'cpp',\n> >   # simple-cam.cpp is the fully commented application\n> >   src_files = files([\n> >   \t'simple-cam.cpp',\n> > +\t'event_loop.cpp',\n> >   ])\n> >   \n> >   # Point your PKG_CONFIG_PATH environment variable to the\n> >   # libcamera install path camera.pc file ($prefix/lib/pkgconfig/camera.pc)\n> >   libcamera_deps = [\n> >         dependency('camera', required : true),\n> > +      dependency('libevent_pthreads'),\n> >   ]\n> >   \n> >   cpp_arguments = [ '-Wno-unused-parameter', ]\n> > diff --git a/simple-cam.cpp b/simple-cam.cpp\n> > index bfe30d7..6d1d84f 100644\n> > --- a/simple-cam.cpp\n> > +++ b/simple-cam.cpp\n> > @@ -11,8 +11,13 @@\n> >   \n> >   #include <libcamera/libcamera.h>\n> >   \n> > +#include \"event_loop.h\"\n> > +\n> > +#define TIMEOUT_SEC 3\n> > +\n> >   using namespace libcamera;\n> >   std::shared_ptr<Camera> camera;\n> > +EventLoop loop;\n> >   \n> >   /*\n> >    * --------------------------------------------------------------------\n> > @@ -21,13 +26,26 @@ std::shared_ptr<Camera> camera;\n> >    * For each Camera::requestCompleted Signal emitted from the Camera the\n> >    * connected Slot is invoked.\n> >    *\n> > + * The Slot is invoked in the CameraManager's thread, hence one should avoid\n> > + * any heavy processing here. The processing of the request shall be re-directed\n> > + * to the application's thread instead, so as not to block the CameraManager's\n> > + * thread for large amount of time.\n> > + *\n> >    * The Slot receives the Request as a parameter.\n> >    */\n> > +\n> > +static void processRequest(Request *request);\n> > +\n> >   static void requestComplete(Request *request)\n> >   {\n> >   \tif (request->status() == Request::RequestCancelled)\n> >   \t\treturn;\n> >   \n> > +\tloop.callLater(std::bind(&processRequest, request));\n> > +}\n> > +\n> > +static void processRequest(Request *request)\n> > +{\n> >   \tconst Request::BufferMap &buffers = request->buffers();\n> >   \n> >   \tfor (auto bufferPair : buffers) {\n> > @@ -320,20 +338,11 @@ int main()\n> >   \t *\n> >   \t * In order to dispatch events received from the video devices, such\n> >   \t * as buffer completions, an event loop has to be run.\n> > -\t *\n> > -\t * Libcamera provides its own default event dispatcher realized by\n> > -\t * polling a set of file descriptors, but applications can integrate\n> > -\t * their own even loop with the Libcamera EventDispatcher.\n> > -\t *\n> > -\t * Here, as an example, run the poll-based EventDispatcher for 3\n> > -\t * seconds.\n> >   \t */\n> > -\tEventDispatcher *dispatcher = cm->eventDispatcher();\n> > -\tTimer timer;\n> > -\ttimer.start(3000);\n> > -\twhile (timer.isRunning())\n> > -\t\tdispatcher->processEvents();\n> > -\n> > +\tloop.timeout(TIMEOUT_SEC);\n> > +\tint ret = loop.exec();\n> > +\tstd::cout << \"Capture ran for \" << TIMEOUT_SEC << \" seconds and \"\n> > +\t\t  << \"stopped with exit status: \" << ret << std::endl;\n> >   \t/*\n> >   \t * --------------------------------------------------------------------\n> >   \t * Clean Up","headers":{"Return-Path":"<libcamera-devel-bounces@lists.libcamera.org>","X-Original-To":"parsemail@patchwork.libcamera.org","Delivered-To":"parsemail@patchwork.libcamera.org","Received":["from lancelot.ideasonboard.com (lancelot.ideasonboard.com\n\t[92.243.16.209])\n\tby patchwork.libcamera.org (Postfix) with ESMTPS id 544BFBE177\n\tfor <parsemail@patchwork.libcamera.org>;\n\tFri,  4 Dec 2020 07:28:22 +0000 (UTC)","from lancelot.ideasonboard.com (localhost [IPv6:::1])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTP id C1E8F635D6;\n\tFri,  4 Dec 2020 08:28:21 +0100 (CET)","from perceval.ideasonboard.com (perceval.ideasonboard.com\n\t[IPv6:2001:4b98:dc2:55:216:3eff:fef7:d647])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTPS id D12A5635C4\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tFri,  4 Dec 2020 08:28:19 +0100 (CET)","from pendragon.ideasonboard.com (62-78-145-57.bb.dnainternet.fi\n\t[62.78.145.57])\n\tby perceval.ideasonboard.com (Postfix) with ESMTPSA id 539B099A;\n\tFri,  4 Dec 2020 08:28:19 +0100 (CET)"],"Authentication-Results":"lancelot.ideasonboard.com;\n\tdkim=fail reason=\"signature verification failed\" (1024-bit key;\n\tunprotected) header.d=ideasonboard.com header.i=@ideasonboard.com\n\theader.b=\"p2hCgFlt\"; dkim-atps=neutral","DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com;\n\ts=mail; t=1607066899;\n\tbh=DUmoQMSuwvnoFf9PT80tEcy6ZQARnHoDZrFfGiT67YE=;\n\th=Date:From:To:Cc:Subject:References:In-Reply-To:From;\n\tb=p2hCgFlti/woiuSVljf+U9IJjVaFnwWv3nrV+QP7SC3KM3k5BVuOUDbcOtOQd/jS2\n\t08etqwK7KJ8J8L4ZN6ZmU99P5WnFhFZntu73dtHXf5rEvWA8yHESUFkOy8GPbGgaGz\n\tQRh5hD18j4Wo57Nj9LK2yeP7jcqPaXApG24P0Rj4=","Date":"Fri, 4 Dec 2020 09:28:18 +0200","From":"Laurent Pinchart <laurent.pinchart@ideasonboard.com>","To":"Umang Jain <email@uajain.com>","Message-ID":"<20201204072818.GB4109@pendragon.ideasonboard.com>","References":"<20201204065452.2764628-1-email@uajain.com>\n\t<20201204065452.2764628-5-email@uajain.com>\n\t<4ca38784-ca11-cc5b-6b9b-2057465a371b@uajain.com>","MIME-Version":"1.0","Content-Disposition":"inline","In-Reply-To":"<4ca38784-ca11-cc5b-6b9b-2057465a371b@uajain.com>","Subject":"Re: [libcamera-devel] [PATCH v3 4/4] simple-cam: Provide event-loop\n\tbacked by libevent","X-BeenThere":"libcamera-devel@lists.libcamera.org","X-Mailman-Version":"2.1.29","Precedence":"list","List-Id":"<libcamera-devel.lists.libcamera.org>","List-Unsubscribe":"<https://lists.libcamera.org/options/libcamera-devel>,\n\t<mailto:libcamera-devel-request@lists.libcamera.org?subject=unsubscribe>","List-Archive":"<https://lists.libcamera.org/pipermail/libcamera-devel/>","List-Post":"<mailto:libcamera-devel@lists.libcamera.org>","List-Help":"<mailto:libcamera-devel-request@lists.libcamera.org?subject=help>","List-Subscribe":"<https://lists.libcamera.org/listinfo/libcamera-devel>,\n\t<mailto:libcamera-devel-request@lists.libcamera.org?subject=subscribe>","Cc":"libcamera-devel@lists.libcamera.org","Content-Type":"text/plain; charset=\"us-ascii\"","Content-Transfer-Encoding":"7bit","Errors-To":"libcamera-devel-bounces@lists.libcamera.org","Sender":"\"libcamera-devel\" <libcamera-devel-bounces@lists.libcamera.org>"}}]