Show a patch.

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

{
    "id": 25280,
    "url": "https://patchwork.libcamera.org/api/patches/25280/?format=api",
    "web_url": "https://patchwork.libcamera.org/patch/25280/",
    "project": {
        "id": 1,
        "url": "https://patchwork.libcamera.org/api/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": "<20251201124415.1307419-1-barnabas.pocze@ideasonboard.com>",
    "date": "2025-12-01T12:44:15",
    "name": "[RFC,v1] libcamera: request: Split \"Pending\" state into two",
    "commit_ref": null,
    "pull_url": null,
    "state": "new",
    "archived": false,
    "hash": "fdfb56432a47d458fbd716f5f0cc73b10bcd7671",
    "submitter": {
        "id": 216,
        "url": "https://patchwork.libcamera.org/api/people/216/?format=api",
        "name": "Barnabás Pőcze",
        "email": "barnabas.pocze@ideasonboard.com"
    },
    "delegate": null,
    "mbox": "https://patchwork.libcamera.org/patch/25280/mbox/",
    "series": [
        {
            "id": 5625,
            "url": "https://patchwork.libcamera.org/api/series/5625/?format=api",
            "web_url": "https://patchwork.libcamera.org/project/libcamera/list/?series=5625",
            "date": "2025-12-01T12:44:15",
            "name": "[RFC,v1] libcamera: request: Split \"Pending\" state into two",
            "version": 1,
            "mbox": "https://patchwork.libcamera.org/series/5625/mbox/"
        }
    ],
    "comments": "https://patchwork.libcamera.org/api/patches/25280/comments/",
    "check": "pending",
    "checks": "https://patchwork.libcamera.org/api/patches/25280/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 1EAF7BD80A\n\tfor <parsemail@patchwork.libcamera.org>;\n\tMon,  1 Dec 2025 12:44:23 +0000 (UTC)",
            "from lancelot.ideasonboard.com (localhost [IPv6:::1])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTP id 9BBFA60B2F;\n\tMon,  1 Dec 2025 13:44:21 +0100 (CET)",
            "from perceval.ideasonboard.com (perceval.ideasonboard.com\n\t[213.167.242.64])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTPS id C873260A7B\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tMon,  1 Dec 2025 13:44:19 +0100 (CET)",
            "from pb-laptop.local (185.182.214.104.nat.pool.zt.hu\n\t[185.182.214.104])\n\tby perceval.ideasonboard.com (Postfix) with ESMTPSA id 81EA46DF\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tMon,  1 Dec 2025 13:42:06 +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=\"DOIWPsUF\"; dkim-atps=neutral",
        "DKIM-Signature": "v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com;\n\ts=mail; t=1764592926;\n\tbh=7bwcqolPjJ5nnSMO+zbTArRNQYteYTPMAbrR1S5DVCc=;\n\th=From:To:Subject:Date:From;\n\tb=DOIWPsUFK3pg3OEdE5z17Gl3hOKn0JwsHb6cjAszLUmbGPaYJwUIZ2gVHsT4Lzx5P\n\tRVDILGeir9S52fuLnoHDv6Rpvv5ApKBgS/hsJ8tJXcGbdu/H+I2vl4XisgOYhI07aW\n\t4HgRtGF6zcfbwzEalHzNhbYyDSjOvPd9XAjL4ajM=",
        "From": "=?utf-8?q?Barnab=C3=A1s_P=C5=91cze?= <barnabas.pocze@ideasonboard.com>",
        "To": "libcamera-devel@lists.libcamera.org",
        "Subject": "[RFC PATCH v1] libcamera: request: Split \"Pending\" state into two",
        "Date": "Mon,  1 Dec 2025 13:44:15 +0100",
        "Message-ID": "<20251201124415.1307419-1-barnabas.pocze@ideasonboard.com>",
        "X-Mailer": "git-send-email 2.52.0",
        "MIME-Version": "1.0",
        "Content-Type": "text/plain; charset=UTF-8",
        "Content-Transfer-Encoding": "8bit",
        "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": "The \"Pending\" state at the moment is used for two very distinct scenarios:\nwhen the application alone is managing the request, and when the request\nis being processed. This duality makes it harder to track the true state of\na request and consequently it is harder to diagnose misuses.\n\nSo split the \"Pending\" state into the \"Idle\" and \"InProgress\" states to\nmake it explicit when a camera is managing the request. This improves\nthe detection of request double queueing, but since the status flag is\nnot updated atomically, it cannot detect all cases.\n\nThe name \"Pending\" is not kept because any application depending on it\nhas to be adjusted, so it is better to break the API as well. This is also\nan ABI break.\n\nLink: https://gitlab.freedesktop.org/camera/libcamera/-/issues/197\nSigned-off-by: Barnabás Pőcze <barnabas.pocze@ideasonboard.com>\n---\n .../internal/tracepoints/request_enums.tp     |  7 ++--\n include/libcamera/request.h                   |  3 +-\n src/libcamera/camera.cpp                      |  2 +-\n src/libcamera/pipeline_handler.cpp            |  7 +++-\n src/libcamera/request.cpp                     | 34 ++++++++++++-------\n src/py/libcamera/py_main.cpp                  |  3 +-\n 6 files changed, 36 insertions(+), 20 deletions(-)",
    "diff": "diff --git a/include/libcamera/internal/tracepoints/request_enums.tp b/include/libcamera/internal/tracepoints/request_enums.tp\nindex bcbd1aaa2c..e9a5a78fac 100644\n--- a/include/libcamera/internal/tracepoints/request_enums.tp\n+++ b/include/libcamera/internal/tracepoints/request_enums.tp\n@@ -9,8 +9,9 @@ TRACEPOINT_ENUM(\n \tlibcamera,\n \trequest_status,\n \tTP_ENUM_VALUES(\n-\t\tctf_enum_value(\"RequestPending\", 0)\n-\t\tctf_enum_value(\"RequestComplete\", 1)\n-\t\tctf_enum_value(\"RequestCancelled\", 2)\n+\t\tctf_enum_value(\"RequestIdle\", 0)\n+\t\tctf_enum_value(\"RequestInProgress\", 1)\n+\t\tctf_enum_value(\"RequestComplete\", 2)\n+\t\tctf_enum_value(\"RequestCancelled\", 3)\n \t)\n )\ndiff --git a/include/libcamera/request.h b/include/libcamera/request.h\nindex 0c5939f7b3..1e7a5ca807 100644\n--- a/include/libcamera/request.h\n+++ b/include/libcamera/request.h\n@@ -32,7 +32,8 @@ class Request : public Extensible\n \n public:\n \tenum Status {\n-\t\tRequestPending,\n+\t\tRequestIdle,\n+\t\tRequestInProgress,\n \t\tRequestComplete,\n \t\tRequestCancelled,\n \t};\ndiff --git a/src/libcamera/camera.cpp b/src/libcamera/camera.cpp\nindex 2e1e146a25..b6273f6c29 100644\n--- a/src/libcamera/camera.cpp\n+++ b/src/libcamera/camera.cpp\n@@ -1340,7 +1340,7 @@ int Camera::queueRequest(Request *request)\n \t\treturn -EXDEV;\n \t}\n \n-\tif (request->status() != Request::RequestPending) {\n+\tif (request->status() != Request::RequestIdle) {\n \t\tLOG(Camera, Error) << request->toString() << \" is not valid\";\n \t\treturn -EINVAL;\n \t}\ndiff --git a/src/libcamera/pipeline_handler.cpp b/src/libcamera/pipeline_handler.cpp\nindex 5c469e5bad..417b90245a 100644\n--- a/src/libcamera/pipeline_handler.cpp\n+++ b/src/libcamera/pipeline_handler.cpp\n@@ -470,6 +470,8 @@ void PipelineHandler::queueRequest(Request *request)\n {\n \tLIBCAMERA_TRACEPOINT(request_queue, request);\n \n+\tASSERT(request->status() == Request::RequestIdle);\n+\n \tCamera *camera = request->_d()->camera();\n \tCamera::Private *data = camera->_d();\n \tdata->waitingRequests_.push(request);\n@@ -514,6 +516,7 @@ void PipelineHandler::doQueueRequests(Camera *camera)\n \t\t\tbreak;\n \n \t\tRequest *request = data->waitingRequests_.front();\n+\t\tASSERT(request->status() == Request::RequestInProgress);\n \t\tif (!request->_d()->prepared_)\n \t\t\tbreak;\n \n@@ -591,9 +594,11 @@ void PipelineHandler::completeRequest(Request *request)\n \n \twhile (!data->queuedRequests_.empty()) {\n \t\tRequest *req = data->queuedRequests_.front();\n-\t\tif (req->status() == Request::RequestPending)\n+\t\tif (req->status() == Request::RequestInProgress)\n \t\t\tbreak;\n \n+\t\tASSERT(req->status() == Request::RequestCancelled ||\n+\t\t       req->status() == Request::RequestComplete);\n \t\tASSERT(!req->hasPendingBuffers());\n \t\tdata->queuedRequests_.pop_front();\n \t\tcamera->requestComplete(req);\ndiff --git a/src/libcamera/request.cpp b/src/libcamera/request.cpp\nindex 60565f5984..89ad0d7c8d 100644\n--- a/src/libcamera/request.cpp\n+++ b/src/libcamera/request.cpp\n@@ -121,7 +121,7 @@ void Request::Private::complete()\n {\n \tRequest *request = _o<Request>();\n \n-\tASSERT(request->status() == RequestPending);\n+\tASSERT(request->status() == RequestInProgress);\n \tASSERT(!hasPendingBuffers());\n \n \trequest->status_ = cancelled_ ? RequestCancelled : RequestComplete;\n@@ -159,7 +159,7 @@ void Request::Private::cancel()\n \tLIBCAMERA_TRACEPOINT(request_cancel, this);\n \n \tRequest *request = _o<Request>();\n-\tASSERT(request->status() == RequestPending);\n+\tASSERT(request->status() == RequestInProgress);\n \n \tdoCancelRequest();\n }\n@@ -222,6 +222,8 @@ void Request::Private::emitPrepareCompleted()\n  */\n void Request::Private::prepare(std::chrono::milliseconds timeout)\n {\n+\t_o<Request>()->status_ = RequestInProgress;\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@@ -309,8 +311,10 @@ void Request::Private::timeout()\n /**\n  * \\enum Request::Status\n  * Request completion status\n- * \\var Request::RequestPending\n- * The request hasn't completed yet\n+ * \\var Request::RequestIdle\n+ * The request is idle\n+ * \\var Request::RequestInProgress\n+ * The request is being processed\n  * \\var Request::RequestComplete\n  * The request has completed\n  * \\var Request::RequestCancelled\n@@ -354,7 +358,7 @@ void Request::Private::timeout()\n  */\n Request::Request(Camera *camera, uint64_t cookie)\n \t: Extensible(std::make_unique<Private>(camera)),\n-\t  cookie_(cookie), status_(RequestPending)\n+\t  cookie_(cookie), status_(RequestIdle)\n {\n \tcontrols_ = new ControlList(controls::controls,\n \t\t\t\t    camera->_d()->validator());\n@@ -391,6 +395,10 @@ void Request::reuse(ReuseFlag flags)\n {\n \tLIBCAMERA_TRACEPOINT(request_reuse, this);\n \n+\tASSERT(status_ == RequestIdle ||\n+\t       status_ == RequestComplete ||\n+\t       status_ == RequestCancelled);\n+\n \t_d()->reset();\n \n \tif (flags & ReuseBuffers) {\n@@ -403,7 +411,7 @@ void Request::reuse(ReuseFlag flags)\n \t\tbufferMap_.clear();\n \t}\n \n-\tstatus_ = RequestPending;\n+\tstatus_ = RequestIdle;\n \n \tcontrols_->clear();\n \tmetadata_->clear();\n@@ -563,11 +571,11 @@ uint32_t Request::sequence() const\n  * \\fn Request::status()\n  * \\brief Retrieve the request completion status\n  *\n- * The request status indicates whether the request has completed successfully\n- * or with an error. When requests are created and before they complete the\n- * request status is set to RequestPending, and is updated at completion time\n- * to RequestComplete. If a request is cancelled at capture stop before it has\n- * completed, its status is set to RequestCancelled.\n+ * The request status indicates whether the request has completed successfully or with\n+ * an error. When requests are created the request status is set to RequestIdle; when\n+ * they are queued, the status is set to RequestInProgress. Then it is updated at\n+ * completion time to RequestComplete. If a request is cancelled at capture stop before\n+ * it has completed, its status is set to RequestCancelled.\n  *\n  * \\return The request completion status\n  */\n@@ -607,8 +615,8 @@ std::string Request::toString() const\n  */\n std::ostream &operator<<(std::ostream &out, const Request &r)\n {\n-\t/* Pending, Completed, Cancelled(X). */\n-\tstatic const char *statuses = \"PCX\";\n+\t/* Idle, InProgress(P), Completed, Cancelled(X). */\n+\tstatic const char statuses[] = \"IPCX\";\n \n \t/* Example Output: Request(55:P:1/2:6523524) */\n \tout << \"Request(\" << r.sequence() << \":\" << statuses[r.status()] << \":\"\ndiff --git a/src/py/libcamera/py_main.cpp b/src/py/libcamera/py_main.cpp\nindex a983ea75c3..d12f00a6f5 100644\n--- a/src/py/libcamera/py_main.cpp\n+++ b/src/py/libcamera/py_main.cpp\n@@ -486,7 +486,8 @@ PYBIND11_MODULE(_libcamera, m)\n \t\t.def(\"__str__\", &Request::toString);\n \n \tpyRequestStatus\n-\t\t.value(\"Pending\", Request::RequestPending)\n+\t\t.value(\"Idle\", Request::RequestIdle)\n+\t\t.value(\"InProgress\", Request::RequestInProgress)\n \t\t.value(\"Complete\", Request::RequestComplete)\n \t\t.value(\"Cancelled\", Request::RequestCancelled);\n \n",
    "prefixes": [
        "RFC",
        "v1"
    ]
}