From patchwork Fri Dec 20 15:08:37 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: =?utf-8?q?Barnab=C3=A1s_P=C5=91cze?= X-Patchwork-Id: 22434 Return-Path: X-Original-To: parsemail@patchwork.libcamera.org Delivered-To: parsemail@patchwork.libcamera.org Received: from lancelot.ideasonboard.com (lancelot.ideasonboard.com [92.243.16.209]) by patchwork.libcamera.org (Postfix) with ESMTPS id C8E8CC3272 for ; Fri, 20 Dec 2024 15:08:44 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id 64145684B2; Fri, 20 Dec 2024 16:08:44 +0100 (CET) Authentication-Results: lancelot.ideasonboard.com; dkim=fail reason="signature verification failed" (2048-bit key; unprotected) header.d=protonmail.com header.i=@protonmail.com header.b="ZoDjLvxh"; dkim-atps=neutral Received: from mail-40134.protonmail.ch (mail-40134.protonmail.ch [185.70.40.134]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id 0294C684AE for ; Fri, 20 Dec 2024 16:08:41 +0100 (CET) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=protonmail.com; s=protonmail3; t=1734707320; x=1734966520; bh=T1ArgVyr9LxnraRalmGQSwNWbt3n6iSqk9UDRW1FMM8=; h=Date:To:From:Subject:Message-ID:In-Reply-To:References: Feedback-ID:From:To:Cc:Date:Subject:Reply-To:Feedback-ID: Message-ID:BIMI-Selector:List-Unsubscribe:List-Unsubscribe-Post; b=ZoDjLvxhG56N2fBRxXgeuDse3QR+gblciFP71dreMHmswTzgR7ECSZcf6kQSJAn2f gGbodGVXWRjl19QRtBsRVrhzWE2VGAXnhV/3D7aW8KdiTwjSFkj7vmKISN3coC2ekH DoJNrcHb/uQVECErp6rIMz04oGMEFKHxS5PJVrVXi4X6GnFcE/1AE3uupkaNENVHP5 n4Fa8j5iz3FK0kCeAoYaWMiDfbHvy7cJKqKJ8MBhnIteFpXYCLK/KbvZoSgOihhjOC 6aqfo8Gd4lRA8QhSUzldmYR7GDyNp5SSmVjk3hIOUCfBhXkft9MQC3HCcfx6xGrdpH R5btGgGe7dCmA== 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?= 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 X-BeenThere: libcamera-devel@lists.libcamera.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: libcamera-devel-bounces@lists.libcamera.org Sender: "libcamera-devel" libevent is only used as a way to notify the main thread from the CameraManager thread when the capture should be terminated. Not only is libevent not really necessary and instead can be replaced with a simple combination of C++ STL parts, the current usage is prone to race conditions. Specifically, the camera's `queueRequest()` might complete the request before the main thread could set the `loop_` member variable and synchronize with the thread. This is a data race, practically resulting in a nullptr or dangling pointer dereference. This can easily be triggered by inserting a sufficiently large timeout before `loop_ = new EventLoop;`: [46:43:40.529620351] [671833] DEBUG Request request.cpp:129 Request(4:C:0/1:0) ../src/apps/lc-compliance/helpers/capture.cpp:140:14: runtime error: member call on null pointer of type 'struct EventLoop' 0 0x632c3e82f81a in CaptureBalanced::requestComplete(libcamera::Request*) ../src/apps/lc-compliance/helpers/capture.cpp:140 Signed-off-by: Barnabás Pőcze Reviewed-by: Jacopo Mondi --- README.rst | 2 +- src/apps/lc-compliance/helpers/capture.cpp | 23 ++++++------ src/apps/lc-compliance/helpers/capture.h | 42 +++++++++++++++++++--- src/apps/lc-compliance/meson.build | 7 ++-- src/apps/meson.build | 5 --- 5 files changed, 53 insertions(+), 26 deletions(-) diff --git a/README.rst b/README.rst index 4068c6cc8..f1749be97 100644 --- a/README.rst +++ b/README.rst @@ -97,7 +97,7 @@ for Python bindings: [optional] pybind11-dev for lc-compliance: [optional] - libevent-dev libgtest-dev + libgtest-dev for abi-compat.sh: [optional] abi-compliance-checker diff --git a/src/apps/lc-compliance/helpers/capture.cpp b/src/apps/lc-compliance/helpers/capture.cpp index 91c4d4400..b473e0773 100644 --- a/src/apps/lc-compliance/helpers/capture.cpp +++ b/src/apps/lc-compliance/helpers/capture.cpp @@ -12,7 +12,7 @@ using namespace libcamera; Capture::Capture(std::shared_ptr camera) - : loop_(nullptr), camera_(std::move(camera)), + : camera_(std::move(camera)), allocator_(camera_) { } @@ -52,6 +52,8 @@ void Capture::start() camera_->requestCompleted.connect(this, &Capture::requestComplete); + result_.reset(); + ASSERT_EQ(camera_->start(), 0) << "Failed to start camera"; } @@ -62,6 +64,8 @@ void Capture::stop() camera_->stop(); + result_.reset(); + camera_->requestCompleted.disconnect(this); Stream *stream = config_->at(0).stream(); @@ -108,11 +112,10 @@ void CaptureBalanced::capture(unsigned int numRequests) } /* Run capture session. */ - loop_ = new EventLoop(); - loop_->exec(); + int status = result_.wait(); stop(); - delete loop_; + ASSERT_EQ(status, 0); ASSERT_EQ(captureCount_, captureLimit_); } @@ -132,13 +135,13 @@ void CaptureBalanced::requestComplete(Request *request) captureCount_++; if (captureCount_ >= captureLimit_) { - loop_->exit(0); + result_.set(0); return; } request->reuse(Request::ReuseBuffers); if (queueRequest(request)) - loop_->exit(-EINVAL); + result_.set(-EINVAL); } /* CaptureUnbalanced */ @@ -171,10 +174,8 @@ void CaptureUnbalanced::capture(unsigned int numRequests) } /* Run capture session. */ - loop_ = new EventLoop(); - int status = loop_->exec(); + int status = result_.wait(); stop(); - delete loop_; ASSERT_EQ(status, 0); } @@ -183,7 +184,7 @@ void CaptureUnbalanced::requestComplete(Request *request) { captureCount_++; if (captureCount_ >= captureLimit_) { - loop_->exit(0); + result_.set(0); return; } @@ -192,5 +193,5 @@ void CaptureUnbalanced::requestComplete(Request *request) request->reuse(Request::ReuseBuffers); if (camera_->queueRequest(request)) - loop_->exit(-EINVAL); + result_.set(-EINVAL); } diff --git a/src/apps/lc-compliance/helpers/capture.h b/src/apps/lc-compliance/helpers/capture.h index a4cc3a99e..8582ade8e 100644 --- a/src/apps/lc-compliance/helpers/capture.h +++ b/src/apps/lc-compliance/helpers/capture.h @@ -7,12 +7,13 @@ #pragma once +#include #include +#include +#include #include -#include "../common/event_loop.h" - class Capture { public: @@ -27,12 +28,45 @@ protected: virtual void requestComplete(libcamera::Request *request) = 0; - EventLoop *loop_; - std::shared_ptr camera_; libcamera::FrameBufferAllocator allocator_; std::unique_ptr config_; std::vector> requests_; + + struct + { + private: + std::mutex mutex_; + std::condition_variable cv_; + std::optional value_; + + public: + int wait() + { + std::unique_lock guard(mutex_); + + cv_.wait(guard, [&] { + return value_.has_value(); + }); + + return *value_; + } + + void set(int value) + { + std::unique_lock guard(mutex_); + + if (!value_) + value_ = value; + + cv_.notify_all(); + } + + void reset() + { + value_.reset(); + } + } result_; }; class CaptureBalanced : public Capture diff --git a/src/apps/lc-compliance/meson.build b/src/apps/lc-compliance/meson.build index b1f605f33..0884bc6ca 100644 --- a/src/apps/lc-compliance/meson.build +++ b/src/apps/lc-compliance/meson.build @@ -4,13 +4,11 @@ libgtest = dependency('gtest', version : '>=1.10.0', required : get_option('lc-compliance'), fallback : ['gtest', 'gtest_dep']) -if opt_lc_compliance.disabled() or not libevent.found() or not libgtest.found() - lc_compliance_enabled = false +lc_compliance_enabled = opt_lc_compliance.allowed() and libgtest.found() +if not lc_compliance_enabled subdir_done() endif -lc_compliance_enabled = true - lc_compliance_sources = files([ 'environment.cpp', 'helpers/capture.cpp', @@ -29,7 +27,6 @@ lc_compliance = executable('lc-compliance', lc_compliance_sources, dependencies : [ libatomic, libcamera_public, - libevent, libgtest, ], include_directories : lc_compliance_includes, diff --git a/src/apps/meson.build b/src/apps/meson.build index af632b9a7..252491441 100644 --- a/src/apps/meson.build +++ b/src/apps/meson.build @@ -3,12 +3,7 @@ opt_cam = get_option('cam') opt_lc_compliance = get_option('lc-compliance') -# libevent is needed by cam and lc-compliance. As they are both feature options, -# they can't be combined with simple boolean logic. libevent = dependency('libevent_pthreads', required : opt_cam) -if not libevent.found() - libevent = dependency('libevent_pthreads', required : opt_lc_compliance) -endif libtiff = dependency('libtiff-4', required : false)