{"id":25285,"url":"https://patchwork.libcamera.org/api/1.1/patches/25285/?format=json","web_url":"https://patchwork.libcamera.org/patch/25285/","project":{"id":1,"url":"https://patchwork.libcamera.org/api/1.1/projects/1/?format=json","name":"libcamera","link_name":"libcamera","list_id":"libcamera_core","list_email":"libcamera-devel@lists.libcamera.org","web_url":"","scm_url":"","webscm_url":""},"msgid":"<20251202122730.1579065-1-barnabas.pocze@ideasonboard.com>","date":"2025-12-02T12:27:30","name":"[RFC,v1] libcamera: request: Add \"queued\" flag","commit_ref":null,"pull_url":null,"state":"new","archived":false,"hash":"06735b00d2c72fcdbb282171d9a0a755ae076aac","submitter":{"id":216,"url":"https://patchwork.libcamera.org/api/1.1/people/216/?format=json","name":"Barnabás Pőcze","email":"barnabas.pocze@ideasonboard.com"},"delegate":null,"mbox":"https://patchwork.libcamera.org/patch/25285/mbox/","series":[{"id":5629,"url":"https://patchwork.libcamera.org/api/1.1/series/5629/?format=json","web_url":"https://patchwork.libcamera.org/project/libcamera/list/?series=5629","date":"2025-12-02T12:27:30","name":"[RFC,v1] libcamera: request: Add \"queued\" flag","version":1,"mbox":"https://patchwork.libcamera.org/series/5629/mbox/"}],"comments":"https://patchwork.libcamera.org/api/patches/25285/comments/","check":"pending","checks":"https://patchwork.libcamera.org/api/patches/25285/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 65BA8BD80A\n\tfor <parsemail@patchwork.libcamera.org>;\n\tTue,  2 Dec 2025 12:27:34 +0000 (UTC)","from lancelot.ideasonboard.com (localhost [IPv6:::1])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTP id 11E0660C79;\n\tTue,  2 Dec 2025 13:27:34 +0100 (CET)","from perceval.ideasonboard.com (perceval.ideasonboard.com\n\t[213.167.242.64])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTPS id D753560A7B\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tTue,  2 Dec 2025 13:27:32 +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 0DB6A9FC\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tTue,  2 Dec 2025 13:25:19 +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=\"ILYEo7+0\"; dkim-atps=neutral","DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com;\n\ts=mail; t=1764678319;\n\tbh=zC3PVKtt1NxdCi49ocXaJCxicxDzz+eF5jucTCG5hXA=;\n\th=From:To:Subject:Date:From;\n\tb=ILYEo7+0wOVX2CXuMRCGczxUI9Rb5gZc9doPBUVgD8Zcy8a0Rgaqjxrbp1eU8T7ix\n\t0ylrXJgN/sr71a1Cfirpm55pWqTd6aZ7vw6YZJ5LgrP0EK4CmBVhEtSXDPxnL6FU/E\n\tnRrGbZJuwk4B2Ss5K6PTx2a3iOFPO/q49qiCAaS0=","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: Add \"queued\" flag","Date":"Tue,  2 Dec 2025 13:27:30 +0100","Message-ID":"<20251202122730.1579065-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":"Add an atomic flag which is used to check if the request has been queued\nusing `Camera::queueRequest()`. This is used to diagnose misuses\nof requests, especially double queueing.\n\nLink: https://gitlab.freedesktop.org/camera/libcamera/-/issues/197\nSigned-off-by: Barnabás Pőcze <barnabas.pocze@ideasonboard.com>\n---\nThis is another approach to detect misuses of Request objects.\n\nhttps://patchwork.libcamera.org/patch/25280/\n---\n include/libcamera/internal/request.h | 11 +++++++++++\n src/libcamera/camera.cpp             |  8 +++++++-\n src/libcamera/pipeline_handler.cpp   |  3 +++\n src/libcamera/request.cpp            | 22 ++++++++++++++++++++++\n 4 files changed, 43 insertions(+), 1 deletion(-)\n\n--\n2.52.0","diff":"diff --git a/include/libcamera/internal/request.h b/include/libcamera/internal/request.h\nindex 78cb99f360..0e6216d6b5 100644\n--- a/include/libcamera/internal/request.h\n+++ b/include/libcamera/internal/request.h\n@@ -7,6 +7,7 @@\n\n #pragma once\n\n+#include <atomic>\n #include <chrono>\n #include <map>\n #include <memory>\n@@ -35,6 +36,15 @@ public:\n\n \tCamera *camera() const { return camera_; }\n \tbool hasPendingBuffers() const;\n+\tbool hasBeenQueued() const { return queued_.load(std::memory_order_relaxed); }\n+\n+\tbool tryQueue()\n+\t{\n+\t\tbool expected = false;\n+\t\treturn queued_.compare_exchange_strong(expected, true, std::memory_order_relaxed);\n+\t}\n+\n+\tvoid unQueue() { queued_.store(false, std::memory_order_relaxed); }\n\n \tbool completeBuffer(FrameBuffer *buffer);\n \tvoid complete();\n@@ -57,6 +67,7 @@ private:\n \tbool cancelled_;\n \tuint32_t sequence_ = 0;\n \tbool prepared_ = false;\n+\tstd::atomic_bool queued_ = false;\n\n \tstd::unordered_set<FrameBuffer *> pending_;\n \tstd::map<FrameBuffer *, EventNotifier> notifiers_;\ndiff --git a/src/libcamera/camera.cpp b/src/libcamera/camera.cpp\nindex 2e1e146a25..8fa45376d3 100644\n--- a/src/libcamera/camera.cpp\n+++ b/src/libcamera/camera.cpp\n@@ -1340,11 +1340,15 @@ int Camera::queueRequest(Request *request)\n \t\treturn -EXDEV;\n \t}\n\n-\tif (request->status() != Request::RequestPending) {\n+\tif (request->status() != Request::RequestPending || !request->_d()->tryQueue()) {\n \t\tLOG(Camera, Error) << request->toString() << \" is not valid\";\n \t\treturn -EINVAL;\n \t}\n\n+\tutils::scope_exit queuedFlagGuard([&] {\n+\t\trequest->_d()->unQueue();\n+\t});\n+\n \t/*\n \t * The camera state may change until the end of the function. No locking\n \t * is however needed as PipelineHandler::queueRequest() will handle\n@@ -1371,6 +1375,8 @@ int Camera::queueRequest(Request *request)\n \td->pipe_->invokeMethod(&PipelineHandler::queueRequest,\n \t\t\t       ConnectionTypeQueued, request);\n\n+\tqueuedFlagGuard.release();\n+\n \treturn 0;\n }\n\ndiff --git a/src/libcamera/pipeline_handler.cpp b/src/libcamera/pipeline_handler.cpp\nindex 5c469e5bad..fade0b040d 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->_d()->hasBeenQueued());\n+\n \tCamera *camera = request->_d()->camera();\n \tCamera::Private *data = camera->_d();\n \tdata->waitingRequests_.push(request);\n@@ -591,6 +593,7 @@ void PipelineHandler::completeRequest(Request *request)\n\n \twhile (!data->queuedRequests_.empty()) {\n \t\tRequest *req = data->queuedRequests_.front();\n+\t\tASSERT(req->_d()->hasBeenQueued());\n \t\tif (req->status() == Request::RequestPending)\n \t\t\tbreak;\n\ndiff --git a/src/libcamera/request.cpp b/src/libcamera/request.cpp\nindex 60565f5984..6df73ba13a 100644\n--- a/src/libcamera/request.cpp\n+++ b/src/libcamera/request.cpp\n@@ -82,6 +82,23 @@ bool Request::Private::hasPendingBuffers() const\n \treturn !pending_.empty();\n }\n\n+/**\n+ * \\fn Request::Private::hasBeenQueued()\n+ * \\brief Check if a the request has the \"queued\" flag set\n+ * \\return True if the request has been marked as queued\n+ */\n+\n+/**\n+ * \\fn Request::Private::tryQueue()\n+ * \\brief Try to set the \"queued\" flag if not already set\n+ * \\return True if it has been set successfully\n+ */\n+\n+/**\n+ * \\fn Request::Private::unQueue()\n+ * \\brief Clear the \"queued\" flag\n+ */\n+\n /**\n  * \\brief Complete a buffer for the request\n  * \\param[in] buffer The buffer that has completed\n@@ -123,6 +140,7 @@ void Request::Private::complete()\n\n \tASSERT(request->status() == RequestPending);\n \tASSERT(!hasPendingBuffers());\n+\tASSERT(hasBeenQueued());\n\n \trequest->status_ = cancelled_ ? RequestCancelled : RequestComplete;\n\n@@ -160,6 +178,7 @@ void Request::Private::cancel()\n\n \tRequest *request = _o<Request>();\n \tASSERT(request->status() == RequestPending);\n+\tASSERT(hasBeenQueued());\n\n \tdoCancelRequest();\n }\n@@ -175,6 +194,7 @@ void Request::Private::reset()\n \tsequence_ = 0;\n \tcancelled_ = false;\n \tprepared_ = false;\n+\tunQueue();\n \tpending_.clear();\n \tnotifiers_.clear();\n \ttimer_.reset();\n@@ -391,6 +411,8 @@ void Request::reuse(ReuseFlag flags)\n {\n \tLIBCAMERA_TRACEPOINT(request_reuse, this);\n\n+\tASSERT(!(status_ == RequestPending && _d()->hasBeenQueued()));\n+\n \t_d()->reset();\n\n \tif (flags & ReuseBuffers) {\n","prefixes":["RFC","v1"]}