{"id":22434,"url":"https://patchwork.libcamera.org/api/1.1/patches/22434/?format=json","web_url":"https://patchwork.libcamera.org/patch/22434/","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":"<20241220150759.709756-9-pobrn@protonmail.com>","date":"2024-12-20T15:08:37","name":"[RFC,v1,08/12] apps: lc-compliance: Remove libevent dependency","commit_ref":null,"pull_url":null,"state":"superseded","archived":false,"hash":"3d77cbcd2de06852ecfd4f4b536289273ef863d4","submitter":{"id":133,"url":"https://patchwork.libcamera.org/api/1.1/people/133/?format=json","name":"Pőcze Barnabás","email":"pobrn@protonmail.com"},"delegate":null,"mbox":"https://patchwork.libcamera.org/patch/22434/mbox/","series":[{"id":4923,"url":"https://patchwork.libcamera.org/api/1.1/series/4923/?format=json","web_url":"https://patchwork.libcamera.org/project/libcamera/list/?series=4923","date":"2024-12-20T15:08:03","name":"apps: lc-compliance: Multi-stream tests","version":1,"mbox":"https://patchwork.libcamera.org/series/4923/mbox/"}],"comments":"https://patchwork.libcamera.org/api/patches/22434/comments/","check":"pending","checks":"https://patchwork.libcamera.org/api/patches/22434/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 C8E8CC3272\n\tfor <parsemail@patchwork.libcamera.org>;\n\tFri, 20 Dec 2024 15:08:44 +0000 (UTC)","from lancelot.ideasonboard.com (localhost [IPv6:::1])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTP id 64145684B2;\n\tFri, 20 Dec 2024 16:08:44 +0100 (CET)","from mail-40134.protonmail.ch (mail-40134.protonmail.ch\n\t[185.70.40.134])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTPS id 0294C684AE\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tFri, 20 Dec 2024 16:08:41 +0100 (CET)"],"Authentication-Results":"lancelot.ideasonboard.com;\n\tdkim=fail reason=\"signature verification failed\" (2048-bit key;\n\tunprotected) header.d=protonmail.com header.i=@protonmail.com\n\theader.b=\"ZoDjLvxh\"; dkim-atps=neutral","DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/relaxed; d=protonmail.com;\n\ts=protonmail3; t=1734707320; x=1734966520;\n\tbh=T1ArgVyr9LxnraRalmGQSwNWbt3n6iSqk9UDRW1FMM8=;\n\th=Date:To:From:Subject:Message-ID:In-Reply-To:References:\n\tFeedback-ID:From:To:Cc:Date:Subject:Reply-To:Feedback-ID:\n\tMessage-ID:BIMI-Selector:List-Unsubscribe:List-Unsubscribe-Post;\n\tb=ZoDjLvxhG56N2fBRxXgeuDse3QR+gblciFP71dreMHmswTzgR7ECSZcf6kQSJAn2f\n\tgGbodGVXWRjl19QRtBsRVrhzWE2VGAXnhV/3D7aW8KdiTwjSFkj7vmKISN3coC2ekH\n\tDoJNrcHb/uQVECErp6rIMz04oGMEFKHxS5PJVrVXi4X6GnFcE/1AE3uupkaNENVHP5\n\tn4Fa8j5iz3FK0kCeAoYaWMiDfbHvy7cJKqKJ8MBhnIteFpXYCLK/KbvZoSgOihhjOC\n\t6aqfo8Gd4lRA8QhSUzldmYR7GDyNp5SSmVjk3hIOUCfBhXkft9MQC3HCcfx6xGrdpH\n\tR5btGgGe7dCmA==","Date":"Fri, 20 Dec 2024 15:08:37 +0000","To":"libcamera-devel@lists.libcamera.org","From":"=?utf-8?q?Barnab=C3=A1s_P=C5=91cze?= <pobrn@protonmail.com>","Subject":"[RFC PATCH v1 08/12] apps: lc-compliance: Remove libevent dependency","Message-ID":"<20241220150759.709756-9-pobrn@protonmail.com>","In-Reply-To":"<20241220150759.709756-1-pobrn@protonmail.com>","References":"<20241220150759.709756-1-pobrn@protonmail.com>","Feedback-ID":"20568564:user:proton","X-Pm-Message-ID":"5e7dddb2a7e18cb9935e602b516beae085bcff46","MIME-Version":"1.0","Content-Type":"text/plain; charset=utf-8","Content-Transfer-Encoding":"quoted-printable","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":"libevent is only used as a way to notify the main thread from\nthe CameraManager thread when the capture should be terminated.\n\nNot only is libevent not really necessary and instead can be\nreplaced with a simple combination of C++ STL parts, the current\nusage is prone to race conditions.\n\nSpecifically, the camera's `queueRequest()` might complete the\nrequest before the main thread could set the `loop_` member\nvariable and synchronize with the thread. This is a data race,\npractically resulting in a nullptr or dangling pointer dereference.\n\nThis can easily be triggered by inserting a sufficiently large\ntimeout before `loop_ = new EventLoop;`:\n\n  [46:43:40.529620351] [671833] DEBUG Request request.cpp:129 Request(4:C:0/1:0)\n  ../src/apps/lc-compliance/helpers/capture.cpp:140:14: runtime error: member call on null pointer of type 'struct EventLoop'\n    0 0x632c3e82f81a in CaptureBalanced::requestComplete(libcamera::Request*) ../src/apps/lc-compliance/helpers/capture.cpp:140\n\nSigned-off-by: Barnabás Pőcze <pobrn@protonmail.com>\n---\n README.rst                                 |  2 +-\n src/apps/lc-compliance/helpers/capture.cpp | 23 ++++++------\n src/apps/lc-compliance/helpers/capture.h   | 42 +++++++++++++++++++---\n src/apps/lc-compliance/meson.build         |  7 ++--\n src/apps/meson.build                       |  5 ---\n 5 files changed, 53 insertions(+), 26 deletions(-)","diff":"diff --git a/README.rst b/README.rst\nindex 4068c6cc8..f1749be97 100644\n--- a/README.rst\n+++ b/README.rst\n@@ -97,7 +97,7 @@ for Python bindings: [optional]\n         pybind11-dev\n \n for lc-compliance: [optional]\n-        libevent-dev libgtest-dev\n+        libgtest-dev\n \n for abi-compat.sh: [optional]\n         abi-compliance-checker\ndiff --git a/src/apps/lc-compliance/helpers/capture.cpp b/src/apps/lc-compliance/helpers/capture.cpp\nindex 91c4d4400..b473e0773 100644\n--- a/src/apps/lc-compliance/helpers/capture.cpp\n+++ b/src/apps/lc-compliance/helpers/capture.cpp\n@@ -12,7 +12,7 @@\n using namespace libcamera;\n \n Capture::Capture(std::shared_ptr<Camera> camera)\n-\t: loop_(nullptr), camera_(std::move(camera)),\n+\t: camera_(std::move(camera)),\n \t  allocator_(camera_)\n {\n }\n@@ -52,6 +52,8 @@ void Capture::start()\n \n \tcamera_->requestCompleted.connect(this, &Capture::requestComplete);\n \n+\tresult_.reset();\n+\n \tASSERT_EQ(camera_->start(), 0) << \"Failed to start camera\";\n }\n \n@@ -62,6 +64,8 @@ void Capture::stop()\n \n \tcamera_->stop();\n \n+\tresult_.reset();\n+\n \tcamera_->requestCompleted.disconnect(this);\n \n \tStream *stream = config_->at(0).stream();\n@@ -108,11 +112,10 @@ void CaptureBalanced::capture(unsigned int numRequests)\n \t}\n \n \t/* Run capture session. */\n-\tloop_ = new EventLoop();\n-\tloop_->exec();\n+\tint status = result_.wait();\n \tstop();\n-\tdelete loop_;\n \n+\tASSERT_EQ(status, 0);\n \tASSERT_EQ(captureCount_, captureLimit_);\n }\n \n@@ -132,13 +135,13 @@ void CaptureBalanced::requestComplete(Request *request)\n \n \tcaptureCount_++;\n \tif (captureCount_ >= captureLimit_) {\n-\t\tloop_->exit(0);\n+\t\tresult_.set(0);\n \t\treturn;\n \t}\n \n \trequest->reuse(Request::ReuseBuffers);\n \tif (queueRequest(request))\n-\t\tloop_->exit(-EINVAL);\n+\t\tresult_.set(-EINVAL);\n }\n \n /* CaptureUnbalanced */\n@@ -171,10 +174,8 @@ void CaptureUnbalanced::capture(unsigned int numRequests)\n \t}\n \n \t/* Run capture session. */\n-\tloop_ = new EventLoop();\n-\tint status = loop_->exec();\n+\tint status = result_.wait();\n \tstop();\n-\tdelete loop_;\n \n \tASSERT_EQ(status, 0);\n }\n@@ -183,7 +184,7 @@ void CaptureUnbalanced::requestComplete(Request *request)\n {\n \tcaptureCount_++;\n \tif (captureCount_ >= captureLimit_) {\n-\t\tloop_->exit(0);\n+\t\tresult_.set(0);\n \t\treturn;\n \t}\n \n@@ -192,5 +193,5 @@ void CaptureUnbalanced::requestComplete(Request *request)\n \n \trequest->reuse(Request::ReuseBuffers);\n \tif (camera_->queueRequest(request))\n-\t\tloop_->exit(-EINVAL);\n+\t\tresult_.set(-EINVAL);\n }\ndiff --git a/src/apps/lc-compliance/helpers/capture.h b/src/apps/lc-compliance/helpers/capture.h\nindex a4cc3a99e..8582ade8e 100644\n--- a/src/apps/lc-compliance/helpers/capture.h\n+++ b/src/apps/lc-compliance/helpers/capture.h\n@@ -7,12 +7,13 @@\n \n #pragma once\n \n+#include <condition_variable>\n #include <memory>\n+#include <mutex>\n+#include <optional>\n \n #include <libcamera/libcamera.h>\n \n-#include \"../common/event_loop.h\"\n-\n class Capture\n {\n public:\n@@ -27,12 +28,45 @@ protected:\n \n \tvirtual void requestComplete(libcamera::Request *request) = 0;\n \n-\tEventLoop *loop_;\n-\n \tstd::shared_ptr<libcamera::Camera> camera_;\n \tlibcamera::FrameBufferAllocator allocator_;\n \tstd::unique_ptr<libcamera::CameraConfiguration> config_;\n \tstd::vector<std::unique_ptr<libcamera::Request>> requests_;\n+\n+\tstruct\n+\t{\n+\tprivate:\n+\t\tstd::mutex mutex_;\n+\t\tstd::condition_variable cv_;\n+\t\tstd::optional<int> value_;\n+\n+\tpublic:\n+\t\tint wait()\n+\t\t{\n+\t\t\tstd::unique_lock guard(mutex_);\n+\n+\t\t\tcv_.wait(guard, [&] {\n+\t\t\t\treturn value_.has_value();\n+\t\t\t});\n+\n+\t\t\treturn *value_;\n+\t\t}\n+\n+\t\tvoid set(int value)\n+\t\t{\n+\t\t\tstd::unique_lock guard(mutex_);\n+\n+\t\t\tif (!value_)\n+\t\t\t\tvalue_ = value;\n+\n+\t\t\tcv_.notify_all();\n+\t\t}\n+\n+\t\tvoid reset()\n+\t\t{\n+\t\t\tvalue_.reset();\n+\t\t}\n+\t} result_;\n };\n \n class CaptureBalanced : public Capture\ndiff --git a/src/apps/lc-compliance/meson.build b/src/apps/lc-compliance/meson.build\nindex b1f605f33..0884bc6ca 100644\n--- a/src/apps/lc-compliance/meson.build\n+++ b/src/apps/lc-compliance/meson.build\n@@ -4,13 +4,11 @@ libgtest = dependency('gtest', version : '>=1.10.0',\n                       required : get_option('lc-compliance'),\n                       fallback : ['gtest', 'gtest_dep'])\n \n-if opt_lc_compliance.disabled() or not libevent.found() or not libgtest.found()\n-    lc_compliance_enabled = false\n+lc_compliance_enabled = opt_lc_compliance.allowed() and libgtest.found()\n+if not lc_compliance_enabled\n     subdir_done()\n endif\n \n-lc_compliance_enabled = true\n-\n lc_compliance_sources = files([\n     'environment.cpp',\n     'helpers/capture.cpp',\n@@ -29,7 +27,6 @@ lc_compliance  = executable('lc-compliance', lc_compliance_sources,\n                             dependencies : [\n                                 libatomic,\n                                 libcamera_public,\n-                                libevent,\n                                 libgtest,\n                             ],\n                             include_directories : lc_compliance_includes,\ndiff --git a/src/apps/meson.build b/src/apps/meson.build\nindex af632b9a7..252491441 100644\n--- a/src/apps/meson.build\n+++ b/src/apps/meson.build\n@@ -3,12 +3,7 @@\n opt_cam = get_option('cam')\n opt_lc_compliance = get_option('lc-compliance')\n \n-# libevent is needed by cam and lc-compliance. As they are both feature options,\n-# they can't be combined with simple boolean logic.\n libevent = dependency('libevent_pthreads', required : opt_cam)\n-if not libevent.found()\n-    libevent = dependency('libevent_pthreads', required : opt_lc_compliance)\n-endif\n \n libtiff = dependency('libtiff-4', required : false)\n \n","prefixes":["RFC","v1","08/12"]}