[{"id":21669,"web_url":"https://patchwork.libcamera.org/comment/21669/","msgid":"<YbCaGzVAs2hWEvbe@pendragon.ideasonboard.com>","date":"2021-12-08T11:42:19","subject":"Re: [libcamera-devel] [PATCH v4 09/11] 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 Wed, Dec 01, 2021 at 03:29:34PM +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 emits the Request::prepared signal when\n> all fences have been signalled or an optional timeout has expired.\n> \n> The optional timeout allows to interrupt blocked waits and notify the\n> Request as failed so that it can be cancelled.\n> \n> Signed-off-by: Jacopo Mondi <jacopo@jmondi.org>\n> ---\n>  include/libcamera/internal/request.h |  15 +++\n>  src/libcamera/request.cpp            | 132 ++++++++++++++++++++++++++-\n>  2 files changed, 146 insertions(+), 1 deletion(-)\n> \n> diff --git a/include/libcamera/internal/request.h b/include/libcamera/internal/request.h\n> index 1340ffa2a683..740ab21ac7e0 100644\n> --- a/include/libcamera/internal/request.h\n> +++ b/include/libcamera/internal/request.h\n> @@ -7,10 +7,17 @@\n>  #ifndef __LIBCAMERA_INTERNAL_REQUEST_H__\n>  #define __LIBCAMERA_INTERNAL_REQUEST_H__\n>  \n> +#include <chrono>\n> +#include <map>\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> @@ -32,16 +39,24 @@ public:\n>  \tvoid cancel();\n>  \tvoid reuse();\n>  \n> +\tvoid prepare(std::chrono::milliseconds timeout = 0ms);\n> +\tSignal<> prepared;\n> +\n>  private:\n>  \tfriend class PipelineHandler;\n>  \n>  \tvoid doCancelRequest();\n> +\tvoid notifierActivated(FrameBuffer *buffer);\n> +\tvoid timeout();\n>  \n>  \tCamera *camera_;\n>  \tbool cancelled_;\n>  \tuint32_t sequence_ = 0;\n> +\tbool prepared_ = false;\n>  \n>  \tstd::unordered_set<FrameBuffer *> pending_;\n> +\tstd::map<FrameBuffer *, 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 5ddb4b0592b3..59fb02b4a0b7 100644\n> --- a/src/libcamera/request.cpp\n> +++ b/src/libcamera/request.cpp\n> @@ -135,6 +135,8 @@ void Request::Private::doCancelRequest()\n>  \n>  \tcancelled_ = true;\n>  \tpending_.clear();\n> +\tnotifiers_.clear();\n> +\ttimer_.reset();\n>  }\n>  \n>  /**\n> @@ -163,6 +165,134 @@ void Request::Private::reuse()\n>  \tsequence_ = 0;\n>  \tcancelled_ = false;\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 by ensuring it is\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 cancelled.\n> + *\n> + * The function immediately emits the prepared signal if all the prepare\n> + * operations have been completed synchronously. If instead the prepare\n> + * operations require to wait the completion of asynchronous events, such as\n> + * fences notifications or timer expiration, the prepared signal is emitted upon\n> + * the asynchronous event completion.\n> + *\n> + * As we currently only handle fences, the function emits the prepared signal\n> + * immediately if there are no fences to wait on. Otherwise the prepared signal\n> + * is emitted when all fences have been signalled or the optional timeout has\n> + * expired.\n> + *\n> + * If not all the fences have been correctly signalled or the optional timeout\n> + * has expired the Request will be cancelled and the Request::prepared signal\n> + * emitted.\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> +void Request::Private::prepare(std::chrono::milliseconds timeout)\n> +{\n> +\tprepared_ = false;\n\nShould this be moved to Request::Private::reuse() ?\n\n> +\n> +\t/* Create and connect one notifier for each synchronization fence. */\n> +\tfor (FrameBuffer *buffer : pending_) {\n> +\t\tconst Fence *fence = buffer->_d()->fence();\n> +\n\nYou can drop the blank line.\n\n> +\t\tif (!fence)\n> +\t\t\tcontinue;\n> +\n> +\t\tnotifiers_[buffer] = std::make_unique<EventNotifier>(fence->fd().get(),\n> +\t\t\t\t\t\t\t\t     EventNotifier::Read);\n> +\t}\n> +\n> +\tif (notifiers_.empty()) {\n> +\t\tprepared_ = true;\n> +\t\tprepared.emit();\n> +\t\treturn;\n> +\t}\n> +\n> +\tfor (auto &it : notifiers_) {\n> +\t\tFrameBuffer *buffer = it.first;\n> +\t\tstd::unique_ptr<EventNotifier> &notifier = it.second;\n> +\n> +\t\tnotifier->activated.connect(this, [this, buffer] {\n> +\t\t\t\t\t\t\tnotifierActivated(buffer);\n> +\t\t\t\t\t        });\n\nI think this can be moved to the loop above, possibly with something\nlike\n\n\t\tstd::unique_ptr<EventNotifier> notifier =\n\t\t\tstd::make_unique<EventNotifier>(fence->fd().get(),\n\t\t\t\t\t\t\tEventNotifier::Read);\n\t\tnotifier->activated.connect(this, [this, buffer] {\n\t\t\t\t\t\t    notifierActivated(buffer);\n\t\t\t\t\t    });\n\n\t\tnotifiers_[buffer] = std::move(notifier);\n\n\n> +\t}\n> +\n> +\t/*\n> +\t * In case a timeout is specified, create a timer and set it up.\n> +\t *\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> +\t */\n> +\tif (timeout != 0ms) {\n\nMaybe\n\n\tif (timeout) {\n\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> +\n> +/**\n> + * \\var Request::Private::prepared\n> + * \\brief Request preparation completed Signal\n> + *\n> + * The signal is emitted once the request preparation has completed (prepared_\n> + * == true) and is ready for being queued. The Request might complete with\n\ns/for being/to be/\n\n> + * errors in which case it is cancelled.\n> + *\n> + * The intended slot for this signal is the PipelineHandler::doQueueRequests()\n> + * function which queues Request after they have been prepared or cancel them\n> + * if they have failed preparing.\n> + */\n> +\n> +void Request::Private::notifierActivated(FrameBuffer *buffer)\n> +{\n> +\t/* Close the fence if successfully signalled. */\n> +\tASSERT(buffer);\n> +\tbuffer->releaseFence();\n> +\n> +\t/* Remove the entry from the map and check if other fences are pending. */\n> +\tauto it = notifiers_.find(buffer);\n> +\tASSERT(it != notifiers_.end());\n> +\tnotifiers_.erase(it);\n> +\n> +\tRequest *request = _o<Request>();\n> +\tLOG(Request, Debug)\n> +\t\t<< \"Request \" << request->cookie() << \" buffer \" << buffer\n> +\t\t<< \" fence signalled\";\n> +\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> +\tprepared_ = true;\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> +\tRequest *request = _o<Request>();\n> +\tLOG(Request, Debug) << \"Request prepare timeout: \" << request->cookie();\n> +\n> +\tcancel();\n> +\n> +\tprepared_ = true;\n> +\tprepared.emit();\n>  }\n>  \n>  /**\n> @@ -314,7 +444,7 @@ void Request::reuse(ReuseFlag flags)\n>   * responsible for resetting it before associating this buffer with a new\n>   * Request by calling this function again.\n>   *\n> - * \\sa FrameBuffer::resetFence()\n> + * \\sa FrameBuffer::releaseFence()\n\nThis seems to belong to another patch.\n\nReviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>\n\n>   *\n>   * \\return 0 on success or a negative error code otherwise\n>   * \\retval -EEXIST The request already contains a buffer for the stream","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 0C223BF415\n\tfor <parsemail@patchwork.libcamera.org>;\n\tWed,  8 Dec 2021 11:42:51 +0000 (UTC)","from lancelot.ideasonboard.com (localhost [IPv6:::1])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTP id BBEED60889;\n\tWed,  8 Dec 2021 12:42:50 +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 2150260225\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tWed,  8 Dec 2021 12:42:49 +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 B66D98BB;\n\tWed,  8 Dec 2021 12:42:48 +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=\"Nmc1u6FD\"; dkim-atps=neutral","DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com;\n\ts=mail; t=1638963769;\n\tbh=D3tpsli4UK6Dnehe7WRe4bZ0U4wiIChRbOJL5FQHUCE=;\n\th=Date:From:To:Cc:Subject:References:In-Reply-To:From;\n\tb=Nmc1u6FDB8EUJiwi4/owMOzZ1/Wnd1jMGZ5sMR5T0NQNd8NGxTRNOGcpqNT2uKc6S\n\tirfBdCF/vt+Z09EmOmcxorCMLyAEfCwqmB0f+Wd2KGU8f5/uZmuXH5DgBA4HbjLzJ0\n\tr/b7I724tJP3CspWe3nSfc1z1bAR0EuJrZJ+wlVs=","Date":"Wed, 8 Dec 2021 13:42:19 +0200","From":"Laurent Pinchart <laurent.pinchart@ideasonboard.com>","To":"Jacopo Mondi <jacopo@jmondi.org>","Message-ID":"<YbCaGzVAs2hWEvbe@pendragon.ideasonboard.com>","References":"<20211201142936.107405-1-jacopo@jmondi.org>\n\t<20211201142936.107405-10-jacopo@jmondi.org>","MIME-Version":"1.0","Content-Type":"text/plain; charset=utf-8","Content-Disposition":"inline","In-Reply-To":"<20211201142936.107405-10-jacopo@jmondi.org>","Subject":"Re: [libcamera-devel] [PATCH v4 09/11] 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>"}}]