Show a patch.

GET /api/1.1/patches/14676/?format=api
HTTP 200 OK
Allow: GET, PUT, PATCH, HEAD, OPTIONS
Content-Type: application/json
Vary: Accept

{
    "id": 14676,
    "url": "https://patchwork.libcamera.org/api/1.1/patches/14676/?format=api",
    "web_url": "https://patchwork.libcamera.org/patch/14676/",
    "project": {
        "id": 1,
        "url": "https://patchwork.libcamera.org/api/1.1/projects/1/?format=api",
        "name": "libcamera",
        "link_name": "libcamera",
        "list_id": "libcamera_core",
        "list_email": "libcamera-devel@lists.libcamera.org",
        "web_url": "",
        "scm_url": "",
        "webscm_url": ""
    },
    "msgid": "<20211120111313.106621-9-jacopo@jmondi.org>",
    "date": "2021-11-20T11:13:09",
    "name": "[libcamera-devel,v2,08/12] libcamera: request: Add Request::Private::prepare()",
    "commit_ref": null,
    "pull_url": null,
    "state": "superseded",
    "archived": false,
    "hash": "6db84a6e7ef9a5d11c9563fb825a2e7f336f6828",
    "submitter": {
        "id": 3,
        "url": "https://patchwork.libcamera.org/api/1.1/people/3/?format=api",
        "name": "Jacopo Mondi",
        "email": "jacopo@jmondi.org"
    },
    "delegate": null,
    "mbox": "https://patchwork.libcamera.org/patch/14676/mbox/",
    "series": [
        {
            "id": 2739,
            "url": "https://patchwork.libcamera.org/api/1.1/series/2739/?format=api",
            "web_url": "https://patchwork.libcamera.org/project/libcamera/list/?series=2739",
            "date": "2021-11-20T11:13:01",
            "name": "libcamera: Add support for Fence",
            "version": 2,
            "mbox": "https://patchwork.libcamera.org/series/2739/mbox/"
        }
    ],
    "comments": "https://patchwork.libcamera.org/api/patches/14676/comments/",
    "check": "pending",
    "checks": "https://patchwork.libcamera.org/api/patches/14676/checks/",
    "tags": {},
    "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 2C35EC324F\n\tfor <parsemail@patchwork.libcamera.org>;\n\tSat, 20 Nov 2021 11:12:31 +0000 (UTC)",
            "from lancelot.ideasonboard.com (localhost [IPv6:::1])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTP id EFE956042B;\n\tSat, 20 Nov 2021 12:12:29 +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 8DDC66039C\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tSat, 20 Nov 2021 12:12:27 +0100 (CET)",
            "(Authenticated sender: jacopo@jmondi.org)\n\tby relay11.mail.gandi.net (Postfix) with ESMTPSA id 1AB69100002;\n\tSat, 20 Nov 2021 11:12:26 +0000 (UTC)"
        ],
        "From": "Jacopo Mondi <jacopo@jmondi.org>",
        "To": "libcamera-devel@lists.libcamera.org",
        "Date": "Sat, 20 Nov 2021 12:13:09 +0100",
        "Message-Id": "<20211120111313.106621-9-jacopo@jmondi.org>",
        "X-Mailer": "git-send-email 2.33.1",
        "In-Reply-To": "<20211120111313.106621-1-jacopo@jmondi.org>",
        "References": "<20211120111313.106621-1-jacopo@jmondi.org>",
        "MIME-Version": "1.0",
        "Content-Transfer-Encoding": "8bit",
        "Subject": "[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>",
        "Errors-To": "libcamera-devel-bounces@lists.libcamera.org",
        "Sender": "\"libcamera-devel\" <libcamera-devel-bounces@lists.libcamera.org>"
    },
    "content": "Add a prepare() function to the Private Request representation.\n\nThe prepare() function is used by the PipelineHandler class to\nprepare a Request to be queued to the hardware.\n\nThe current implementation of prepare() handles the fences associated\nwith the Framebuffers part of a Request. The function starts an event\nnotifier for each of those and notifies the Request as Ready to be queued\nonce all the fences have been signalled.\n\nAn optional timeout allows to interrupt blocked waits and notify the\nRequest as failed.\n\nSigned-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(+)",
    "diff": "diff --git a/include/libcamera/internal/request.h b/include/libcamera/internal/request.h\nindex 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 \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 */\ndiff --git a/src/libcamera/request.cpp b/src/libcamera/request.cpp\nindex 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+ * 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+\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+\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+\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+\tstatus_ = Status::Failed;\n+\tprepared.emit();\n+}\n+\n /**\n  * \\enum Request::Status\n  * Request completion status\n",
    "prefixes": [
        "libcamera-devel",
        "v2",
        "08/12"
    ]
}