[{"id":21081,"web_url":"https://patchwork.libcamera.org/comment/21081/","msgid":"<YZq8iqyS9dalBQ0b@pendragon.ideasonboard.com>","date":"2021-11-21T21:39:22","subject":"Re: [libcamera-devel] [PATCH v2 08/12] libcamera: request: Add\n\tRequest::Private::prepare()","submitter":{"id":2,"url":"https://patchwork.libcamera.org/api/people/2/","name":"Laurent Pinchart","email":"laurent.pinchart@ideasonboard.com"},"content":"Hi Jacopo,\n\nThank you for the patch.\n\nOn Sat, Nov 20, 2021 at 12:13:09PM +0100, Jacopo Mondi wrote:\n> Add a prepare() function to the Private Request representation.\n> \n> The prepare() function is used by the PipelineHandler class to\n> prepare a Request to be queued to the hardware.\n> \n> The current implementation of prepare() handles the fences associated\n> with the Framebuffers part of a Request. The function starts an event\n> notifier for each of those and notifies the Request as Ready to be queued\n> once all the fences have been signalled.\n> \n> An optional timeout allows to interrupt blocked waits and notify the\n> Request as failed.\n> \n> Signed-off-by: Jacopo Mondi <jacopo@jmondi.org>\n> ---\n>  include/libcamera/internal/request.h |  21 ++++\n>  src/libcamera/request.cpp            | 150 +++++++++++++++++++++++++++\n>  2 files changed, 171 insertions(+)\n> \n> diff --git a/include/libcamera/internal/request.h b/include/libcamera/internal/request.h\n> index 59bddde3a090..26b25fb12261 100644\n> --- a/include/libcamera/internal/request.h\n> +++ b/include/libcamera/internal/request.h\n> @@ -7,10 +7,16 @@\n>  #ifndef __LIBCAMERA_INTERNAL_REQUEST_H__\n>  #define __LIBCAMERA_INTERNAL_REQUEST_H__\n>  \n> +#include <chrono>\n>  #include <memory>\n>  \n> +#include <libcamera/base/event_notifier.h>\n> +#include <libcamera/base/timer.h>\n> +\n>  #include <libcamera/request.h>\n>  \n> +using namespace std::chrono_literals;\n> +\n>  namespace libcamera {\n>  \n>  class Camera;\n> @@ -21,6 +27,12 @@ class Request::Private : public Extensible::Private\n>  \tLIBCAMERA_DECLARE_PUBLIC(Request)\n>  \n>  public:\n> +\tenum class Status {\n> +\t\tPending,\n> +\t\tReady,\n> +\t\tFailed\n> +\t};\n> +\n>  \tPrivate(Camera *camera);\n>  \t~Private();\n>  \n> @@ -29,21 +41,30 @@ public:\n>  \n>  \tuint64_t cookie() const;\n>  \tRequest::Status status() const;\n> +\tStatus privateStatus() const { return status_; }\n\nAs mentioned in the cover letter, status and privateStatus isn't nice\nindeed. I think one option to remove the privateStatus() could be to\ncall cancel() in Request::Private::timeout(), and then use\nRequest::status() in PipelineHandler::doQueueRequests(). There could be\nother options too.\n\n>  \n>  \tbool completeBuffer(FrameBuffer *buffer);\n>  \tvoid complete();\n>  \tvoid cancel();\n>  \tvoid reuse();\n>  \n> +\tStatus prepare(std::chrono::milliseconds timeout = 0ms);\n> +\tSignal<> prepared;\n> +\n>  \tuint32_t sequence_ = 0;\n>  \n>  private:\n>  \tvoid _cancel();\n> +\tvoid notifierActivated(const std::unique_ptr<EventNotifier> &notifier);\n> +\tvoid timeout();\n>  \n>  \tCamera *camera_;\n>  \tbool cancelled_;\n> +\tStatus status_ = Status::Pending;\n>  \n>  \tstd::unordered_set<FrameBuffer *> pending_;\n> +\tstd::unordered_set<std::unique_ptr<EventNotifier>> notifiers_;\n> +\tstd::unique_ptr<Timer> timer_;\n>  };\n>  \n>  } /* namespace libcamera */\n> diff --git a/src/libcamera/request.cpp b/src/libcamera/request.cpp\n> index 1d47698a6263..98f9719e5cf2 100644\n> --- a/src/libcamera/request.cpp\n> +++ b/src/libcamera/request.cpp\n> @@ -43,6 +43,22 @@ LOG_DEFINE_CATEGORY(Request)\n>   * subclasses).\n>   */\n>  \n> +/**\n> + * \\enum Request::Private::Status\n> + * \\brief Request private status\n> + *\n> + * The Request private status describes the lifecycle of the Request between\n> + * the time it is queued to the Camera and the time it is queued to the device.\n> + *\n> + * A Request is created in Status::Pending state. Before actually queueing the\n> + * Request the PipelineHandler base class prepare() the Request, whose status\n> + * transitions to either Status::Ready or Status::Failed.\n> + *\n> + * \\var Request::Private::Status::Pending\n> + * \\var Request::Private::Status::Ready\n> + * \\var Request::Private::Status::Failed\n> + */\n> +\n>  /**\n>   * \\brief Create a Request::Private\n>   * \\param camera The Camera that creates the request\n> @@ -95,6 +111,17 @@ Request::Status Request::Private::status() const\n>  \treturn _o<Request>()->status();\n>  }\n>  \n> +/**\n> + * \\fn Request::Private::privateStatus()\n> + * \\brief Retrieve the Request private status\n> + *\n> + * The private status, as described by the Request::Private:Status enumeration,\n> + * describes the Request status between the time it is queued to the Camera and\n> + * the time the Request is applied to the hardware.\n> + *\n> + * \\return The Request private state\n> + */\n> +\n>  /**\n>   * \\brief Complete a buffer for the request\n>   * \\param[in] buffer The buffer that has completed\n> @@ -155,6 +182,8 @@ void Request::Private::_cancel()\n>  \n>  \tcancelled_ = true;\n>  \tpending_.clear();\n> +\tnotifiers_.clear();\n> +\ttimer_.reset();\n>  }\n>  \n>  /**\n> @@ -182,8 +211,84 @@ void Request::Private::reuse()\n>  {\n>  \tsequence_ = 0;\n>  \tcancelled_ = false;\n> +\tstatus_ = Status::Pending;\n>  \tpending_.clear();\n> +\tnotifiers_.clear();\n> +\ttimer_.reset();\n> +}\n> +\n> +/**\n> + * \\brief Prepare the Request to be queued to the device\n> + * \\param[in] timeout Optional expiration timeout\n> + *\n> + * Prepare a Request to be queued to the hardware device it by ensuring it is\n\ns/it by/by/\n\n> + * ready for the incoming memory transfers.\n> + *\n> + * This currently means waiting on each frame buffer acquire fence to be\n> + * signalled. An optional expiration timeout can be specified. If not all the\n> + * fences have been signalled correctly before the timeout expires the Request\n> + * is marked as Failed, otherwise it is set to the Ready state.\n> + *\n> + * \\sa Request::Private::Status\n> + *\n> + * The function returns Status::Ready if all the prepare operations have been\n> + * completed synchronously. If Status::Ready is returned the Request can be\n> + * queued immediately and the prepared signal is not emitted. If instead the\n> + * prepare operation requires to wait the completion of asynchronous events,\n> + * such as fence notifications or timer expiration this function returns\n> + * Status::Pending and the asynchronous event completion is notified by emitting\n> + * the prepared signal.\n> + *\n> + * As we currently only handle fences, the function return Status::Ready if\n> + * there are no fences to wait on. Status::Prepared is otherwise returned and\n> + * the prepared signal is emitted when all fences have been signalled or the\n> + * optional timeout has expired.\n> + *\n> + * The intended user of this function is the PipelineHandler base class, which\n> + * 'prepares' a Request before queuing it to the hardware device.\n> + *\n> + * \\return The Request status\n> + */\n> +Request::Private::Status Request::Private::prepare(std::chrono::milliseconds timeout)\n> +{\n> +\tFrameBuffer::Private *bufferData;\n> +\n> +\t/* Create and connect one notifier for each synchronization fence. */\n> +\tfor (FrameBuffer *buffer : pending_) {\n> +\t\tbufferData = buffer->_d();\n> +\n> +\t\tif (!bufferData->fence())\n> +\t\t\tcontinue;\n> +\n> +\t\tint fenceFd = bufferData->fence()->fd().fd();\n> +\t\tnotifiers_.emplace(new EventNotifier(fenceFd, EventNotifier::Read));\n\nThere's no bug in your code, but usage of new() indicates potential\nmemory leaks, so it's best to minimize its usage. I'd write\n\n\t\tnotifiers_.insert(std::make_unique<EventNotifier>(fenceFd, EventNotifier::Read));\n\n> +\t}\n> +\n> +\tif (notifiers_.empty()) {\n> +\t\tstatus_ = Status::Ready;\n> +\t\treturn status_;\n> +\t}\n> +\n> +\tfor (const std::unique_ptr<EventNotifier> &notifier : notifiers_)\n> +\t\tnotifier->activated.connect(this, [this, &notifier] {\n> +\t\t\t\t\t\t\tnotifierActivated(notifier);\n> +\t\t\t\t\t        });\n> +\n> +\t/* In case a timeout is specified, create a timer and set it up. */\n\nLet's add\n\n\t * The timer must be created here instead of in the Request constructor,\n\t * in order to be bound to the pipeline handler thread.\n\n> +\tif (timeout != 0ms) {\n> +\t\ttimer_ = std::make_unique<Timer>();\n> +\t\ttimer_->timeout.connect(this, &Request::Private::timeout);\n> +\t\ttimer_->start(timeout);\n> +\t}\n> +\n> +\treturn Status::Pending;\n>  }\n> +\n> +/**\n> + * \\var Request::Private::prepared\n> + * \\brief Request preparation completed Signal\n> + */\n> +\n>  /**\n>   * \\var Request::Private::sequence_\n>   * \\brief The request sequence number\n> @@ -191,6 +296,51 @@ void Request::Private::reuse()\n>   * \\copydoc Request::sequence()\n>   */\n>  \n> +void Request::Private::notifierActivated(const std::unique_ptr<EventNotifier> &notifier)\n> +{\n> +\tauto it = notifiers_.find(notifier);\n> +\tASSERT(it != notifiers_.end());\n> +\n> +\t/* We need to close the fence if successfully signalled. */\n> +\tint fd = notifier->fd();\n> +\tbool found = false;\n> +\tfor (FrameBuffer *buffer : pending_) {\n> +\t\tFrameBuffer::Private *bufferData = buffer->_d();\n> +\n> +\t\tif (!bufferData->fence())\n> +\t\t\tcontinue;\n> +\n> +\t\tif (bufferData->fence()->fd().fd() != fd)\n> +\t\t\tcontinue;\n\nIf you turned notifiers_ into a map indexed by FrameBuffer *, you could\npass the FrameBuffer pointer instead of the notifier to the\nnotifierActivated() function, and avoid this loop.\n\n> +\n> +\t\tbufferData->closeFence();\n> +\t\tfound = true;\n> +\t\tbreak;\n> +\t}\n> +\tASSERT(found);\n> +\n> +\tnotifiers_.erase(it);\n> +\tif (!notifiers_.empty())\n> +\t\treturn;\n> +\n> +\t/* All fences completed, delete the timer and move to state Ready. */\n> +\ttimer_.reset();\n> +\tstatus_ = Status::Ready;\n> +\tprepared.emit();\n> +}\n> +\n> +void Request::Private::timeout()\n> +{\n> +\t/* A timeout can only happen if there are fences not yet signalled. */\n> +\tASSERT(!notifiers_.empty());\n> +\tnotifiers_.clear();\n> +\n> +\tLOG(Request, Error) << \"Request prepare timeout\";\n\nCould you print the cookie to ease debugging ?\n\n> +\n> +\tstatus_ = Status::Failed;\n> +\tprepared.emit();\n> +}\n> +\n>  /**\n>   * \\enum Request::Status\n>   * Request completion status","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 CEBB1BDB13\n\tfor <parsemail@patchwork.libcamera.org>;\n\tSun, 21 Nov 2021 21:39:47 +0000 (UTC)","from lancelot.ideasonboard.com (localhost [IPv6:::1])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTP id 3437A60371;\n\tSun, 21 Nov 2021 22:39:47 +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 9E57B60233\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tSun, 21 Nov 2021 22:39:45 +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 134209DD;\n\tSun, 21 Nov 2021 22:39:45 +0100 (CET)"],"Authentication-Results":"lancelot.ideasonboard.com; dkim=pass (1024-bit key;\n\tunprotected) header.d=ideasonboard.com header.i=@ideasonboard.com\n\theader.b=\"TBaHrPQB\"; dkim-atps=neutral","DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com;\n\ts=mail; t=1637530785;\n\tbh=gAH/llsHLJoDMKCNQnZjSc8j9LBSamILyZQKjxBF+nU=;\n\th=Date:From:To:Cc:Subject:References:In-Reply-To:From;\n\tb=TBaHrPQBeb77UmTYUPOF6bjoE9SZbXE5hMalNgwJgJV3OW6YakdZxNW9uDXoaYDRc\n\th2SXBokoaReg8pYh+34kDm4pBrLBsVIj/oC7tkGQrHsLgf6pKlsfx0sUuUpdOTyOXT\n\t5ZoayydZ/OXlGdrXzVpIJyEPcK8L72dhOPdxPjNY=","Date":"Sun, 21 Nov 2021 23:39:22 +0200","From":"Laurent Pinchart <laurent.pinchart@ideasonboard.com>","To":"Jacopo Mondi <jacopo@jmondi.org>","Message-ID":"<YZq8iqyS9dalBQ0b@pendragon.ideasonboard.com>","References":"<20211120111313.106621-1-jacopo@jmondi.org>\n\t<20211120111313.106621-9-jacopo@jmondi.org>","MIME-Version":"1.0","Content-Type":"text/plain; charset=utf-8","Content-Disposition":"inline","In-Reply-To":"<20211120111313.106621-9-jacopo@jmondi.org>","Subject":"Re: [libcamera-devel] [PATCH v2 08/12] libcamera: request: Add\n\tRequest::Private::prepare()","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","Errors-To":"libcamera-devel-bounces@lists.libcamera.org","Sender":"\"libcamera-devel\" <libcamera-devel-bounces@lists.libcamera.org>"}},{"id":21288,"web_url":"https://patchwork.libcamera.org/comment/21288/","msgid":"<20211127085026.yyfra2x6bm4uw6vx@uno.localdomain>","date":"2021-11-27T08:50:26","subject":"Re: [libcamera-devel] [PATCH v2 08/12] libcamera: request: Add\n\tRequest::Private::prepare()","submitter":{"id":3,"url":"https://patchwork.libcamera.org/api/people/3/","name":"Jacopo Mondi","email":"jacopo@jmondi.org"},"content":"Hi Laurent\n\nOn Sun, Nov 21, 2021 at 11:39:22PM +0200, Laurent Pinchart wrote:\n> Hi Jacopo,\n>\n> Thank you for the patch.\n>\n> On Sat, Nov 20, 2021 at 12:13:09PM +0100, Jacopo Mondi wrote:\n> > Add a prepare() function to the Private Request representation.\n> >\n> > The prepare() function is used by the PipelineHandler class to\n> > prepare a Request to be queued to the hardware.\n> >\n> > The current implementation of prepare() handles the fences associated\n> > with the Framebuffers part of a Request. The function starts an event\n> > notifier for each of those and notifies the Request as Ready to be queued\n> > once all the fences have been signalled.\n> >\n> > An optional timeout allows to interrupt blocked waits and notify the\n> > Request as failed.\n> >\n> > Signed-off-by: Jacopo Mondi <jacopo@jmondi.org>\n> > ---\n> >  include/libcamera/internal/request.h |  21 ++++\n> >  src/libcamera/request.cpp            | 150 +++++++++++++++++++++++++++\n> >  2 files changed, 171 insertions(+)\n> >\n> > diff --git a/include/libcamera/internal/request.h b/include/libcamera/internal/request.h\n> > index 59bddde3a090..26b25fb12261 100644\n> > --- a/include/libcamera/internal/request.h\n> > +++ b/include/libcamera/internal/request.h\n> > @@ -7,10 +7,16 @@\n> >  #ifndef __LIBCAMERA_INTERNAL_REQUEST_H__\n> >  #define __LIBCAMERA_INTERNAL_REQUEST_H__\n> >\n> > +#include <chrono>\n> >  #include <memory>\n> >\n> > +#include <libcamera/base/event_notifier.h>\n> > +#include <libcamera/base/timer.h>\n> > +\n> >  #include <libcamera/request.h>\n> >\n> > +using namespace std::chrono_literals;\n> > +\n> >  namespace libcamera {\n> >\n> >  class Camera;\n> > @@ -21,6 +27,12 @@ class Request::Private : public Extensible::Private\n> >  \tLIBCAMERA_DECLARE_PUBLIC(Request)\n> >\n> >  public:\n> > +\tenum class Status {\n> > +\t\tPending,\n> > +\t\tReady,\n> > +\t\tFailed\n> > +\t};\n> > +\n> >  \tPrivate(Camera *camera);\n> >  \t~Private();\n> >\n> > @@ -29,21 +41,30 @@ public:\n> >\n> >  \tuint64_t cookie() const;\n> >  \tRequest::Status status() const;\n> > +\tStatus privateStatus() const { return status_; }\n>\n> As mentioned in the cover letter, status and privateStatus isn't nice\n> indeed. I think one option to remove the privateStatus() could be to\n> call cancel() in Request::Private::timeout(), and then use\n> Request::status() in PipelineHandler::doQueueRequests(). There could be\n> other options too.\n\nI didn't do so as I thought it could have caused out-of-order\ncompletions but I was probably wrong as just buffes are completed in\nerror state by Request::cancel() not the request!\n\nAnyway, I need somehow to keep the state of when a request has been\nprepared (ie or fences signalled or timeout) to pass it to\nqueueRequestDevice.\n\nProbably a single boolean prepared_ flag in Private would do but I\nreally dislike flags like this as keeping track of them is hell when\nsomething goes wrong..\n\n>\n> >\n> >  \tbool completeBuffer(FrameBuffer *buffer);\n> >  \tvoid complete();\n> >  \tvoid cancel();\n> >  \tvoid reuse();\n> >\n> > +\tStatus prepare(std::chrono::milliseconds timeout = 0ms);\n> > +\tSignal<> prepared;\n> > +\n> >  \tuint32_t sequence_ = 0;\n> >\n> >  private:\n> >  \tvoid _cancel();\n> > +\tvoid notifierActivated(const std::unique_ptr<EventNotifier> &notifier);\n> > +\tvoid timeout();\n> >\n> >  \tCamera *camera_;\n> >  \tbool cancelled_;\n> > +\tStatus status_ = Status::Pending;\n> >\n> >  \tstd::unordered_set<FrameBuffer *> pending_;\n> > +\tstd::unordered_set<std::unique_ptr<EventNotifier>> notifiers_;\n> > +\tstd::unique_ptr<Timer> timer_;\n> >  };\n> >\n> >  } /* namespace libcamera */\n> > diff --git a/src/libcamera/request.cpp b/src/libcamera/request.cpp\n> > index 1d47698a6263..98f9719e5cf2 100644\n> > --- a/src/libcamera/request.cpp\n> > +++ b/src/libcamera/request.cpp\n> > @@ -43,6 +43,22 @@ LOG_DEFINE_CATEGORY(Request)\n> >   * subclasses).\n> >   */\n> >\n> > +/**\n> > + * \\enum Request::Private::Status\n> > + * \\brief Request private status\n> > + *\n> > + * The Request private status describes the lifecycle of the Request between\n> > + * the time it is queued to the Camera and the time it is queued to the device.\n> > + *\n> > + * A Request is created in Status::Pending state. Before actually queueing the\n> > + * Request the PipelineHandler base class prepare() the Request, whose status\n> > + * transitions to either Status::Ready or Status::Failed.\n> > + *\n> > + * \\var Request::Private::Status::Pending\n> > + * \\var Request::Private::Status::Ready\n> > + * \\var Request::Private::Status::Failed\n> > + */\n> > +\n> >  /**\n> >   * \\brief Create a Request::Private\n> >   * \\param camera The Camera that creates the request\n> > @@ -95,6 +111,17 @@ Request::Status Request::Private::status() const\n> >  \treturn _o<Request>()->status();\n> >  }\n> >\n> > +/**\n> > + * \\fn Request::Private::privateStatus()\n> > + * \\brief Retrieve the Request private status\n> > + *\n> > + * The private status, as described by the Request::Private:Status enumeration,\n> > + * describes the Request status between the time it is queued to the Camera and\n> > + * the time the Request is applied to the hardware.\n> > + *\n> > + * \\return The Request private state\n> > + */\n> > +\n> >  /**\n> >   * \\brief Complete a buffer for the request\n> >   * \\param[in] buffer The buffer that has completed\n> > @@ -155,6 +182,8 @@ void Request::Private::_cancel()\n> >\n> >  \tcancelled_ = true;\n> >  \tpending_.clear();\n> > +\tnotifiers_.clear();\n> > +\ttimer_.reset();\n> >  }\n> >\n> >  /**\n> > @@ -182,8 +211,84 @@ void Request::Private::reuse()\n> >  {\n> >  \tsequence_ = 0;\n> >  \tcancelled_ = false;\n> > +\tstatus_ = Status::Pending;\n> >  \tpending_.clear();\n> > +\tnotifiers_.clear();\n> > +\ttimer_.reset();\n> > +}\n> > +\n> > +/**\n> > + * \\brief Prepare the Request to be queued to the device\n> > + * \\param[in] timeout Optional expiration timeout\n> > + *\n> > + * Prepare a Request to be queued to the hardware device it by ensuring it is\n>\n> s/it by/by/\n>\n> > + * ready for the incoming memory transfers.\n> > + *\n> > + * This currently means waiting on each frame buffer acquire fence to be\n> > + * signalled. An optional expiration timeout can be specified. If not all the\n> > + * fences have been signalled correctly before the timeout expires the Request\n> > + * is marked as Failed, otherwise it is set to the Ready state.\n> > + *\n> > + * \\sa Request::Private::Status\n> > + *\n> > + * The function returns Status::Ready if all the prepare operations have been\n> > + * completed synchronously. If Status::Ready is returned the Request can be\n> > + * queued immediately and the prepared signal is not emitted. If instead the\n> > + * prepare operation requires to wait the completion of asynchronous events,\n> > + * such as fence notifications or timer expiration this function returns\n> > + * Status::Pending and the asynchronous event completion is notified by emitting\n> > + * the prepared signal.\n> > + *\n> > + * As we currently only handle fences, the function return Status::Ready if\n> > + * there are no fences to wait on. Status::Prepared is otherwise returned and\n> > + * the prepared signal is emitted when all fences have been signalled or the\n> > + * optional timeout has expired.\n> > + *\n> > + * The intended user of this function is the PipelineHandler base class, which\n> > + * 'prepares' a Request before queuing it to the hardware device.\n> > + *\n> > + * \\return The Request status\n> > + */\n> > +Request::Private::Status Request::Private::prepare(std::chrono::milliseconds timeout)\n> > +{\n> > +\tFrameBuffer::Private *bufferData;\n> > +\n> > +\t/* Create and connect one notifier for each synchronization fence. */\n> > +\tfor (FrameBuffer *buffer : pending_) {\n> > +\t\tbufferData = buffer->_d();\n> > +\n> > +\t\tif (!bufferData->fence())\n> > +\t\t\tcontinue;\n> > +\n> > +\t\tint fenceFd = bufferData->fence()->fd().fd();\n> > +\t\tnotifiers_.emplace(new EventNotifier(fenceFd, EventNotifier::Read));\n>\n> There's no bug in your code, but usage of new() indicates potential\n> memory leaks, so it's best to minimize its usage. I'd write\n>\n> \t\tnotifiers_.insert(std::make_unique<EventNotifier>(fenceFd, EventNotifier::Read));\n>\n> > +\t}\n> > +\n> > +\tif (notifiers_.empty()) {\n> > +\t\tstatus_ = Status::Ready;\n> > +\t\treturn status_;\n> > +\t}\n> > +\n> > +\tfor (const std::unique_ptr<EventNotifier> &notifier : notifiers_)\n> > +\t\tnotifier->activated.connect(this, [this, &notifier] {\n> > +\t\t\t\t\t\t\tnotifierActivated(notifier);\n> > +\t\t\t\t\t        });\n> > +\n> > +\t/* In case a timeout is specified, create a timer and set it up. */\n>\n> Let's add\n>\n> \t * The timer must be created here instead of in the Request constructor,\n> \t * in order to be bound to the pipeline handler thread.\n>\n> > +\tif (timeout != 0ms) {\n> > +\t\ttimer_ = std::make_unique<Timer>();\n> > +\t\ttimer_->timeout.connect(this, &Request::Private::timeout);\n> > +\t\ttimer_->start(timeout);\n> > +\t}\n> > +\n> > +\treturn Status::Pending;\n> >  }\n> > +\n> > +/**\n> > + * \\var Request::Private::prepared\n> > + * \\brief Request preparation completed Signal\n> > + */\n> > +\n> >  /**\n> >   * \\var Request::Private::sequence_\n> >   * \\brief The request sequence number\n> > @@ -191,6 +296,51 @@ void Request::Private::reuse()\n> >   * \\copydoc Request::sequence()\n> >   */\n> >\n> > +void Request::Private::notifierActivated(const std::unique_ptr<EventNotifier> &notifier)\n> > +{\n> > +\tauto it = notifiers_.find(notifier);\n> > +\tASSERT(it != notifiers_.end());\n> > +\n> > +\t/* We need to close the fence if successfully signalled. */\n> > +\tint fd = notifier->fd();\n> > +\tbool found = false;\n> > +\tfor (FrameBuffer *buffer : pending_) {\n> > +\t\tFrameBuffer::Private *bufferData = buffer->_d();\n> > +\n> > +\t\tif (!bufferData->fence())\n> > +\t\t\tcontinue;\n> > +\n> > +\t\tif (bufferData->fence()->fd().fd() != fd)\n> > +\t\t\tcontinue;\n>\n> If you turned notifiers_ into a map indexed by FrameBuffer *, you could\n> pass the FrameBuffer pointer instead of the notifier to the\n> notifierActivated() function, and avoid this loop.\n\nThat's a very nice idea!\n\n>\n> > +\n> > +\t\tbufferData->closeFence();\n> > +\t\tfound = true;\n> > +\t\tbreak;\n> > +\t}\n> > +\tASSERT(found);\n> > +\n> > +\tnotifiers_.erase(it);\n> > +\tif (!notifiers_.empty())\n> > +\t\treturn;\n> > +\n> > +\t/* All fences completed, delete the timer and move to state Ready. */\n> > +\ttimer_.reset();\n> > +\tstatus_ = Status::Ready;\n> > +\tprepared.emit();\n> > +}\n> > +\n> > +void Request::Private::timeout()\n> > +{\n> > +\t/* A timeout can only happen if there are fences not yet signalled. */\n> > +\tASSERT(!notifiers_.empty());\n> > +\tnotifiers_.clear();\n> > +\n> > +\tLOG(Request, Error) << \"Request prepare timeout\";\n>\n> Could you print the cookie to ease debugging ?\n>\n\nIndeed\n\n> > +\n> > +\tstatus_ = Status::Failed;\n> > +\tprepared.emit();\n> > +}\n> > +\n> >  /**\n> >   * \\enum Request::Status\n> >   * Request completion status\n>\n> --\n> Regards,\n>\n> Laurent Pinchart","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 770E0BDB13\n\tfor <parsemail@patchwork.libcamera.org>;\n\tSat, 27 Nov 2021 08:49:37 +0000 (UTC)","from lancelot.ideasonboard.com (localhost [IPv6:::1])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTP id 8656760561;\n\tSat, 27 Nov 2021 09:49:36 +0100 (CET)","from relay11.mail.gandi.net (relay11.mail.gandi.net\n\t[217.70.178.231])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTPS id 8656D60226\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tSat, 27 Nov 2021 09:49:34 +0100 (CET)","(Authenticated sender: jacopo@jmondi.org)\n\tby relay11.mail.gandi.net (Postfix) with ESMTPSA id EDDB4100002;\n\tSat, 27 Nov 2021 08:49:33 +0000 (UTC)"],"Date":"Sat, 27 Nov 2021 09:50:26 +0100","From":"Jacopo Mondi <jacopo@jmondi.org>","To":"Laurent Pinchart <laurent.pinchart@ideasonboard.com>","Message-ID":"<20211127085026.yyfra2x6bm4uw6vx@uno.localdomain>","References":"<20211120111313.106621-1-jacopo@jmondi.org>\n\t<20211120111313.106621-9-jacopo@jmondi.org>\n\t<YZq8iqyS9dalBQ0b@pendragon.ideasonboard.com>","MIME-Version":"1.0","Content-Type":"text/plain; charset=utf-8","Content-Disposition":"inline","In-Reply-To":"<YZq8iqyS9dalBQ0b@pendragon.ideasonboard.com>","Subject":"Re: [libcamera-devel] [PATCH v2 08/12] libcamera: request: Add\n\tRequest::Private::prepare()","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","Errors-To":"libcamera-devel-bounces@lists.libcamera.org","Sender":"\"libcamera-devel\" <libcamera-devel-bounces@lists.libcamera.org>"}}]