[{"id":35456,"web_url":"https://patchwork.libcamera.org/comment/35456/","msgid":"<20250815222143.GN6201@pendragon.ideasonboard.com>","date":"2025-08-15T22:21:43","subject":"Re: [RFC PATCH v1] apps: cam: Use signalfd","submitter":{"id":2,"url":"https://patchwork.libcamera.org/api/people/2/","name":"Laurent Pinchart","email":"laurent.pinchart@ideasonboard.com"},"content":"Hi Barnabás,\n\nThank you for the patch.\n\nOn Fri, Aug 15, 2025 at 04:12:37PM +0200, Barnabás Pőcze wrote:\n> Use a signalfd to detect `SIGINT` instead of registering a signal handler in\n> order to remove the `CamApp` singleton. This is better than using a normal\n> signal handler: a signal can arrive after the `CamApp` object has been destroyed,\n> leading to a use-after-free. Modifying the `CamApp::app_` in the destructor is\n> not a good alternative because that would not be async signal safe (unless it\n> is made atomic).\n\nWhile this is true, it's also not really much of an issue in my opinion,\nor at least a very minor one. Now that the patch exists lets get it\nmerged, but you don't have to hunt for these kind of problems in cam or\nqcam :-)\n\n> Signed-off-by: Barnabás Pőcze <barnabas.pocze@ideasonboard.com>\n> ---\n>  src/apps/cam/main.cpp | 66 +++++++++++++++++++------------------------\n>  1 file changed, 29 insertions(+), 37 deletions(-)\n> \n> diff --git a/src/apps/cam/main.cpp b/src/apps/cam/main.cpp\n> index fa266eca6..cbc85b59f 100644\n> --- a/src/apps/cam/main.cpp\n> +++ b/src/apps/cam/main.cpp\n> @@ -13,6 +13,8 @@\n>  #include <signal.h>\n>  #include <string.h>\n> \n> +#include <sys/signalfd.h>\n\nYou can keep this grouped with the previous headers.\n\n> +\n>  #include <libcamera/libcamera.h>\n>  #include <libcamera/property_ids.h>\n> \n> @@ -27,15 +29,12 @@ using namespace libcamera;\n>  class CamApp\n>  {\n>  public:\n> -\tCamApp();\n> -\n> -\tstatic CamApp *instance();\n> +\tCamApp(EventLoop &loop);\n> \n>  \tint init(int argc, char **argv);\n>  \tvoid cleanup();\n> \n>  \tint exec();\n> -\tvoid quit();\n> \n>  private:\n>  \tvoid cameraAdded(std::shared_ptr<Camera> cam);\n> @@ -46,26 +45,17 @@ private:\n> \n>  \tstatic std::string cameraName(const Camera *camera);\n> \n> -\tstatic CamApp *app_;\n>  \tOptionsParser::Options options_;\n> \n>  \tstd::unique_ptr<CameraManager> cm_;\n> \n>  \tstd::atomic_uint loopUsers_;\n> -\tEventLoop loop_;\n> +\tEventLoop *loop_ = nullptr;\n>  };\n> \n> -CamApp *CamApp::app_ = nullptr;\n> -\n> -CamApp::CamApp()\n> -\t: loopUsers_(0)\n> -{\n> -\tCamApp::app_ = this;\n> -}\n> -\n> -CamApp *CamApp::instance()\n> +CamApp::CamApp(EventLoop &loop)\n> +\t: loopUsers_(0), loop_(&loop)\n>  {\n> -\treturn CamApp::app_;\n>  }\n> \n>  int CamApp::init(int argc, char **argv)\n> @@ -103,11 +93,6 @@ int CamApp::exec()\n>  \treturn ret;\n>  }\n> \n> -void CamApp::quit()\n> -{\n> -\tloop_.exit();\n> -}\n> -\n>  int CamApp::parseOptions(int argc, char *argv[])\n>  {\n>  \tStreamKeyValueParser streamKeyValue;\n> @@ -205,7 +190,7 @@ void CamApp::cameraRemoved(std::shared_ptr<Camera> cam)\n>  void CamApp::captureDone()\n>  {\n>  \tif (--loopUsers_ == 0)\n> -\t\tEventLoop::instance()->exit(0);\n> +\t\tloop_->exit(0);\n>  }\n> \n>  int CamApp::run()\n> @@ -290,7 +275,7 @@ int CamApp::run()\n>  \t}\n> \n>  \tif (loopUsers_)\n> -\t\tloop_.exec();\n> +\t\tloop_->exec();\n> \n>  \t/* 6. Stop capture. */\n>  \tfor (const auto &session : sessions) {\n> @@ -345,29 +330,36 @@ std::string CamApp::cameraName(const Camera *camera)\n>  \treturn name;\n>  }\n> \n> -namespace {\n> -\n> -void signalHandler([[maybe_unused]] int signal)\n> +int main(int argc, char **argv)\n>  {\n> -\tstd::cout << \"Exiting\" << std::endl;\n> -\tCamApp::instance()->quit();\n> -}\n> +\tauto sigfd = [] {\n> +\t\tsigset_t ss;\n> \n> -} /* namespace */\n> +\t\tsigemptyset(&ss);\n> +\t\tsigaddset(&ss, SIGINT);\n> +\t\tsigprocmask(SIG_BLOCK, &ss, nullptr);\n> \n> -int main(int argc, char **argv)\n> -{\n> -\tCamApp app;\n> +\t\treturn UniqueFD(signalfd(-1, &ss, SFD_CLOEXEC | SFD_NONBLOCK));\n> +\t}();\n\nWhy a lambda ?\n\n> +\tif (!sigfd.isValid())\n> +\t\treturn EXIT_FAILURE;\n> +\n> +\tEventLoop loop;\n> +\tCamApp app(loop);\n>  \tint ret;\n> \n> +\tloop.addFdEvent(sigfd.get(), EventLoop::Read, [&] {\n> +\t\tsignalfd_siginfo si;\n> +\t\tstd::ignore = read(sigfd.get(), &si, sizeof(si));\n> +\n> +\t\tstd::cout << \"Exiting\" << std::endl;\n> +\t\tloop.exit(0);\n> +\t});\n\nIt could make things simpler to handle this in the CamApp class, you\nwon't have to change where the EventLoop is instantiated.\n\n> +\n>  \tret = app.init(argc, argv);\n>  \tif (ret)\n>  \t\treturn ret == -EINTR ? 0 : EXIT_FAILURE;\n> \n> -\tstruct sigaction sa = {};\n> -\tsa.sa_handler = &signalHandler;\n> -\tsigaction(SIGINT, &sa, nullptr);\n> -\n>  \tif (app.exec())\n>  \t\treturn EXIT_FAILURE;\n>","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 34FFBBDCC1\n\tfor <parsemail@patchwork.libcamera.org>;\n\tFri, 15 Aug 2025 22:22:08 +0000 (UTC)","from lancelot.ideasonboard.com (localhost [IPv6:::1])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTP id CC63D69259;\n\tSat, 16 Aug 2025 00:22:06 +0200 (CEST)","from perceval.ideasonboard.com (perceval.ideasonboard.com\n\t[213.167.242.64])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTPS id 4FF9F61444\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tSat, 16 Aug 2025 00:22:04 +0200 (CEST)","from pendragon.ideasonboard.com (81-175-209-231.bb.dnainternet.fi\n\t[81.175.209.231])\n\tby perceval.ideasonboard.com (Postfix) with UTF8SMTPSA id E7AF842B;\n\tSat, 16 Aug 2025 00:21:08 +0200 (CEST)"],"Authentication-Results":"lancelot.ideasonboard.com; dkim=pass (1024-bit key;\n\tunprotected) header.d=ideasonboard.com header.i=@ideasonboard.com\n\theader.b=\"KhMKO7Zj\"; dkim-atps=neutral","DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com;\n\ts=mail; t=1755296469;\n\tbh=KhARhEyoL3VOFbp0I56RebfaQsfSXfajlhbh7Kuq4qc=;\n\th=Date:From:To:Cc:Subject:References:In-Reply-To:From;\n\tb=KhMKO7ZjaS+Caszvv6r4GLzdKAPj9Qgxdjz5u8yymE6MPgtFRm/ON7+3QptSwkENK\n\texHpfSXamRN8SIXptxUGOITu00oiJPhEI2eu5eEdRum3fKS5IqHH71tzrRqX46Xp/J\n\tUADYC20KmzDDFWy/2RXtYYEBtrDCYJMMq3ay+VS4=","Date":"Sat, 16 Aug 2025 01:21:43 +0300","From":"Laurent Pinchart <laurent.pinchart@ideasonboard.com>","To":"=?utf-8?q?Barnab=C3=A1s_P=C5=91cze?= <barnabas.pocze@ideasonboard.com>","Cc":"libcamera-devel@lists.libcamera.org","Subject":"Re: [RFC PATCH v1] apps: cam: Use signalfd","Message-ID":"<20250815222143.GN6201@pendragon.ideasonboard.com>","References":"<20250815141237.2235085-1-barnabas.pocze@ideasonboard.com>","MIME-Version":"1.0","Content-Type":"text/plain; charset=utf-8","Content-Disposition":"inline","Content-Transfer-Encoding":"8bit","In-Reply-To":"<20250815141237.2235085-1-barnabas.pocze@ideasonboard.com>","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>"}},{"id":35457,"web_url":"https://patchwork.libcamera.org/comment/35457/","msgid":"<18e78fa9-e9b1-4b1b-bfa9-a19c3585c4f2@ideasonboard.com>","date":"2025-08-15T22:28:49","subject":"Re: [RFC PATCH v1] apps: cam: Use signalfd","submitter":{"id":216,"url":"https://patchwork.libcamera.org/api/people/216/","name":"Barnabás Pőcze","email":"barnabas.pocze@ideasonboard.com"},"content":"Hi\n\n2025. 08. 16. 0:21 keltezéssel, Laurent Pinchart írta:\n> Hi Barnabás,\n> \n> Thank you for the patch.\n> \n> On Fri, Aug 15, 2025 at 04:12:37PM +0200, Barnabás Pőcze wrote:\n>> Use a signalfd to detect `SIGINT` instead of registering a signal handler in\n>> order to remove the `CamApp` singleton. This is better than using a normal\n>> signal handler: a signal can arrive after the `CamApp` object has been destroyed,\n>> leading to a use-after-free. Modifying the `CamApp::app_` in the destructor is\n>> not a good alternative because that would not be async signal safe (unless it\n>> is made atomic).\n> \n> While this is true, it's also not really much of an issue in my opinion,\n> or at least a very minor one. Now that the patch exists lets get it\n> merged, but you don't have to hunt for these kind of problems in cam or\n> qcam :-)\n\nI actually did run into this on multiple occasions. :(\n\n\n> \n>> Signed-off-by: Barnabás Pőcze <barnabas.pocze@ideasonboard.com>\n>> ---\n>>   src/apps/cam/main.cpp | 66 +++++++++++++++++++------------------------\n>>   1 file changed, 29 insertions(+), 37 deletions(-)\n>>\n>> diff --git a/src/apps/cam/main.cpp b/src/apps/cam/main.cpp\n>> index fa266eca6..cbc85b59f 100644\n>> --- a/src/apps/cam/main.cpp\n>> +++ b/src/apps/cam/main.cpp\n>> @@ -13,6 +13,8 @@\n>>   #include <signal.h>\n>>   #include <string.h>\n>>\n>> +#include <sys/signalfd.h>\n> \n> You can keep this grouped with the previous headers.\n> \n>> +\n>>   #include <libcamera/libcamera.h>\n>>   #include <libcamera/property_ids.h>\n>>\n>> @@ -27,15 +29,12 @@ using namespace libcamera;\n>>   class CamApp\n>>   {\n>>   public:\n>> -\tCamApp();\n>> -\n>> -\tstatic CamApp *instance();\n>> +\tCamApp(EventLoop &loop);\n>>\n>>   \tint init(int argc, char **argv);\n>>   \tvoid cleanup();\n>>\n>>   \tint exec();\n>> -\tvoid quit();\n>>\n>>   private:\n>>   \tvoid cameraAdded(std::shared_ptr<Camera> cam);\n>> @@ -46,26 +45,17 @@ private:\n>>\n>>   \tstatic std::string cameraName(const Camera *camera);\n>>\n>> -\tstatic CamApp *app_;\n>>   \tOptionsParser::Options options_;\n>>\n>>   \tstd::unique_ptr<CameraManager> cm_;\n>>\n>>   \tstd::atomic_uint loopUsers_;\n>> -\tEventLoop loop_;\n>> +\tEventLoop *loop_ = nullptr;\n>>   };\n>>\n>> -CamApp *CamApp::app_ = nullptr;\n>> -\n>> -CamApp::CamApp()\n>> -\t: loopUsers_(0)\n>> -{\n>> -\tCamApp::app_ = this;\n>> -}\n>> -\n>> -CamApp *CamApp::instance()\n>> +CamApp::CamApp(EventLoop &loop)\n>> +\t: loopUsers_(0), loop_(&loop)\n>>   {\n>> -\treturn CamApp::app_;\n>>   }\n>>\n>>   int CamApp::init(int argc, char **argv)\n>> @@ -103,11 +93,6 @@ int CamApp::exec()\n>>   \treturn ret;\n>>   }\n>>\n>> -void CamApp::quit()\n>> -{\n>> -\tloop_.exit();\n>> -}\n>> -\n>>   int CamApp::parseOptions(int argc, char *argv[])\n>>   {\n>>   \tStreamKeyValueParser streamKeyValue;\n>> @@ -205,7 +190,7 @@ void CamApp::cameraRemoved(std::shared_ptr<Camera> cam)\n>>   void CamApp::captureDone()\n>>   {\n>>   \tif (--loopUsers_ == 0)\n>> -\t\tEventLoop::instance()->exit(0);\n>> +\t\tloop_->exit(0);\n>>   }\n>>\n>>   int CamApp::run()\n>> @@ -290,7 +275,7 @@ int CamApp::run()\n>>   \t}\n>>\n>>   \tif (loopUsers_)\n>> -\t\tloop_.exec();\n>> +\t\tloop_->exec();\n>>\n>>   \t/* 6. Stop capture. */\n>>   \tfor (const auto &session : sessions) {\n>> @@ -345,29 +330,36 @@ std::string CamApp::cameraName(const Camera *camera)\n>>   \treturn name;\n>>   }\n>>\n>> -namespace {\n>> -\n>> -void signalHandler([[maybe_unused]] int signal)\n>> +int main(int argc, char **argv)\n>>   {\n>> -\tstd::cout << \"Exiting\" << std::endl;\n>> -\tCamApp::instance()->quit();\n>> -}\n>> +\tauto sigfd = [] {\n>> +\t\tsigset_t ss;\n>>\n>> -} /* namespace */\n>> +\t\tsigemptyset(&ss);\n>> +\t\tsigaddset(&ss, SIGINT);\n>> +\t\tsigprocmask(SIG_BLOCK, &ss, nullptr);\n>>\n>> -int main(int argc, char **argv)\n>> -{\n>> -\tCamApp app;\n>> +\t\treturn UniqueFD(signalfd(-1, &ss, SFD_CLOEXEC | SFD_NONBLOCK));\n>> +\t}();\n> \n> Why a lambda ?\n> \n>> +\tif (!sigfd.isValid())\n>> +\t\treturn EXIT_FAILURE;\n>> +\n>> +\tEventLoop loop;\n>> +\tCamApp app(loop);\n>>   \tint ret;\n>>\n>> +\tloop.addFdEvent(sigfd.get(), EventLoop::Read, [&] {\n>> +\t\tsignalfd_siginfo si;\n>> +\t\tstd::ignore = read(sigfd.get(), &si, sizeof(si));\n>> +\n>> +\t\tstd::cout << \"Exiting\" << std::endl;\n>> +\t\tloop.exit(0);\n>> +\t});\n> \n> It could make things simpler to handle this in the CamApp class, you\n> won't have to change where the EventLoop is instantiated.\n> \n>> +\n>>   \tret = app.init(argc, argv);\n>>   \tif (ret)\n>>   \t\treturn ret == -EINTR ? 0 : EXIT_FAILURE;\n>>\n>> -\tstruct sigaction sa = {};\n>> -\tsa.sa_handler = &signalHandler;\n>> -\tsigaction(SIGINT, &sa, nullptr);\n>> -\n>>   \tif (app.exec())\n>>   \t\treturn EXIT_FAILURE;\n>>","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 504BEBEFBE\n\tfor <parsemail@patchwork.libcamera.org>;\n\tFri, 15 Aug 2025 22:28:55 +0000 (UTC)","from lancelot.ideasonboard.com (localhost [IPv6:::1])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTP id 5CF2769259;\n\tSat, 16 Aug 2025 00:28:54 +0200 (CEST)","from perceval.ideasonboard.com (perceval.ideasonboard.com\n\t[213.167.242.64])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTPS id 9626061444\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tSat, 16 Aug 2025 00:28:53 +0200 (CEST)","from [192.168.33.21] (185.221.141.188.nat.pool.zt.hu\n\t[185.221.141.188])\n\tby perceval.ideasonboard.com (Postfix) with ESMTPSA id 2A49E42B;\n\tSat, 16 Aug 2025 00:27:58 +0200 (CEST)"],"Authentication-Results":"lancelot.ideasonboard.com; dkim=pass (1024-bit key;\n\tunprotected) header.d=ideasonboard.com header.i=@ideasonboard.com\n\theader.b=\"ujtRamvo\"; dkim-atps=neutral","DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com;\n\ts=mail; t=1755296878;\n\tbh=Yqt3tZ+vkfLHovciGpFDdabuiw8UK+aYKH6ne+hjSlU=;\n\th=Date:Subject:To:Cc:References:From:In-Reply-To:From;\n\tb=ujtRamvoNAqmffdB8RRFpp+CLiInfOBJDmy4gjUNqTBsN5nT0qFmUV2nXrOllh0as\n\tZuTRk+92yVhtO0xt/oiA7YVblByfv2ASGtRKO34Zu07x5TV8F2n6uK1gH4o5Tp4+Gp\n\tEq4bRRFzDE+tiLGieH7T7NdECFobjcTH+2u40zQM=","Message-ID":"<18e78fa9-e9b1-4b1b-bfa9-a19c3585c4f2@ideasonboard.com>","Date":"Sat, 16 Aug 2025 00:28:49 +0200","MIME-Version":"1.0","User-Agent":"Mozilla Thunderbird","Subject":"Re: [RFC PATCH v1] apps: cam: Use signalfd","To":"Laurent Pinchart <laurent.pinchart@ideasonboard.com>","Cc":"libcamera-devel@lists.libcamera.org","References":"<20250815141237.2235085-1-barnabas.pocze@ideasonboard.com>\n\t<20250815222143.GN6201@pendragon.ideasonboard.com>","From":"=?utf-8?q?Barnab=C3=A1s_P=C5=91cze?= <barnabas.pocze@ideasonboard.com>","Content-Language":"en-US, hu-HU","In-Reply-To":"<20250815222143.GN6201@pendragon.ideasonboard.com>","Content-Type":"text/plain; charset=UTF-8; format=flowed","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>"}},{"id":35458,"web_url":"https://patchwork.libcamera.org/comment/35458/","msgid":"<20250815230225.GO6201@pendragon.ideasonboard.com>","date":"2025-08-15T23:02:25","subject":"Re: [RFC PATCH v1] apps: cam: Use signalfd","submitter":{"id":2,"url":"https://patchwork.libcamera.org/api/people/2/","name":"Laurent Pinchart","email":"laurent.pinchart@ideasonboard.com"},"content":"On Sat, Aug 16, 2025 at 12:28:49AM +0200, Barnabás Pőcze wrote:\n> 2025. 08. 16. 0:21 keltezéssel, Laurent Pinchart írta:\n> > On Fri, Aug 15, 2025 at 04:12:37PM +0200, Barnabás Pőcze wrote:\n> >> Use a signalfd to detect `SIGINT` instead of registering a signal handler in\n> >> order to remove the `CamApp` singleton. This is better than using a normal\n> >> signal handler: a signal can arrive after the `CamApp` object has been destroyed,\n> >> leading to a use-after-free. Modifying the `CamApp::app_` in the destructor is\n> >> not a good alternative because that would not be async signal safe (unless it\n> >> is made atomic).\n> > \n> > While this is true, it's also not really much of an issue in my opinion,\n> > or at least a very minor one. Now that the patch exists lets get it\n> > merged, but you don't have to hunt for these kind of problems in cam or\n> > qcam :-)\n> \n> I actually did run into this on multiple occasions. :(\n\nDo you mean crashes because a signal arrived when the application was\nclosing ? How did that happen ?\n\n> >> Signed-off-by: Barnabás Pőcze <barnabas.pocze@ideasonboard.com>\n> >> ---\n> >>   src/apps/cam/main.cpp | 66 +++++++++++++++++++------------------------\n> >>   1 file changed, 29 insertions(+), 37 deletions(-)\n> >>\n> >> diff --git a/src/apps/cam/main.cpp b/src/apps/cam/main.cpp\n> >> index fa266eca6..cbc85b59f 100644\n> >> --- a/src/apps/cam/main.cpp\n> >> +++ b/src/apps/cam/main.cpp\n> >> @@ -13,6 +13,8 @@\n> >>   #include <signal.h>\n> >>   #include <string.h>\n> >>\n> >> +#include <sys/signalfd.h>\n> > \n> > You can keep this grouped with the previous headers.\n> > \n> >> +\n> >>   #include <libcamera/libcamera.h>\n> >>   #include <libcamera/property_ids.h>\n> >>\n> >> @@ -27,15 +29,12 @@ using namespace libcamera;\n> >>   class CamApp\n> >>   {\n> >>   public:\n> >> -\tCamApp();\n> >> -\n> >> -\tstatic CamApp *instance();\n> >> +\tCamApp(EventLoop &loop);\n> >>\n> >>   \tint init(int argc, char **argv);\n> >>   \tvoid cleanup();\n> >>\n> >>   \tint exec();\n> >> -\tvoid quit();\n> >>\n> >>   private:\n> >>   \tvoid cameraAdded(std::shared_ptr<Camera> cam);\n> >> @@ -46,26 +45,17 @@ private:\n> >>\n> >>   \tstatic std::string cameraName(const Camera *camera);\n> >>\n> >> -\tstatic CamApp *app_;\n> >>   \tOptionsParser::Options options_;\n> >>\n> >>   \tstd::unique_ptr<CameraManager> cm_;\n> >>\n> >>   \tstd::atomic_uint loopUsers_;\n> >> -\tEventLoop loop_;\n> >> +\tEventLoop *loop_ = nullptr;\n> >>   };\n> >>\n> >> -CamApp *CamApp::app_ = nullptr;\n> >> -\n> >> -CamApp::CamApp()\n> >> -\t: loopUsers_(0)\n> >> -{\n> >> -\tCamApp::app_ = this;\n> >> -}\n> >> -\n> >> -CamApp *CamApp::instance()\n> >> +CamApp::CamApp(EventLoop &loop)\n> >> +\t: loopUsers_(0), loop_(&loop)\n> >>   {\n> >> -\treturn CamApp::app_;\n> >>   }\n> >>\n> >>   int CamApp::init(int argc, char **argv)\n> >> @@ -103,11 +93,6 @@ int CamApp::exec()\n> >>   \treturn ret;\n> >>   }\n> >>\n> >> -void CamApp::quit()\n> >> -{\n> >> -\tloop_.exit();\n> >> -}\n> >> -\n> >>   int CamApp::parseOptions(int argc, char *argv[])\n> >>   {\n> >>   \tStreamKeyValueParser streamKeyValue;\n> >> @@ -205,7 +190,7 @@ void CamApp::cameraRemoved(std::shared_ptr<Camera> cam)\n> >>   void CamApp::captureDone()\n> >>   {\n> >>   \tif (--loopUsers_ == 0)\n> >> -\t\tEventLoop::instance()->exit(0);\n> >> +\t\tloop_->exit(0);\n> >>   }\n> >>\n> >>   int CamApp::run()\n> >> @@ -290,7 +275,7 @@ int CamApp::run()\n> >>   \t}\n> >>\n> >>   \tif (loopUsers_)\n> >> -\t\tloop_.exec();\n> >> +\t\tloop_->exec();\n> >>\n> >>   \t/* 6. Stop capture. */\n> >>   \tfor (const auto &session : sessions) {\n> >> @@ -345,29 +330,36 @@ std::string CamApp::cameraName(const Camera *camera)\n> >>   \treturn name;\n> >>   }\n> >>\n> >> -namespace {\n> >> -\n> >> -void signalHandler([[maybe_unused]] int signal)\n> >> +int main(int argc, char **argv)\n> >>   {\n> >> -\tstd::cout << \"Exiting\" << std::endl;\n> >> -\tCamApp::instance()->quit();\n> >> -}\n> >> +\tauto sigfd = [] {\n> >> +\t\tsigset_t ss;\n> >>\n> >> -} /* namespace */\n> >> +\t\tsigemptyset(&ss);\n> >> +\t\tsigaddset(&ss, SIGINT);\n> >> +\t\tsigprocmask(SIG_BLOCK, &ss, nullptr);\n> >>\n> >> -int main(int argc, char **argv)\n> >> -{\n> >> -\tCamApp app;\n> >> +\t\treturn UniqueFD(signalfd(-1, &ss, SFD_CLOEXEC | SFD_NONBLOCK));\n> >> +\t}();\n> > \n> > Why a lambda ?\n> > \n> >> +\tif (!sigfd.isValid())\n> >> +\t\treturn EXIT_FAILURE;\n> >> +\n> >> +\tEventLoop loop;\n> >> +\tCamApp app(loop);\n> >>   \tint ret;\n> >>\n> >> +\tloop.addFdEvent(sigfd.get(), EventLoop::Read, [&] {\n> >> +\t\tsignalfd_siginfo si;\n> >> +\t\tstd::ignore = read(sigfd.get(), &si, sizeof(si));\n> >> +\n> >> +\t\tstd::cout << \"Exiting\" << std::endl;\n> >> +\t\tloop.exit(0);\n> >> +\t});\n> > \n> > It could make things simpler to handle this in the CamApp class, you\n> > won't have to change where the EventLoop is instantiated.\n> > \n> >> +\n> >>   \tret = app.init(argc, argv);\n> >>   \tif (ret)\n> >>   \t\treturn ret == -EINTR ? 0 : EXIT_FAILURE;\n> >>\n> >> -\tstruct sigaction sa = {};\n> >> -\tsa.sa_handler = &signalHandler;\n> >> -\tsigaction(SIGINT, &sa, nullptr);\n> >> -\n> >>   \tif (app.exec())\n> >>   \t\treturn EXIT_FAILURE;\n> >>","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 453C8BDCC1\n\tfor <parsemail@patchwork.libcamera.org>;\n\tFri, 15 Aug 2025 23:02:49 +0000 (UTC)","from lancelot.ideasonboard.com (localhost [IPv6:::1])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTP id 2B2546924E;\n\tSat, 16 Aug 2025 01:02:48 +0200 (CEST)","from perceval.ideasonboard.com (perceval.ideasonboard.com\n\t[213.167.242.64])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTPS id 9D16F61427\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tSat, 16 Aug 2025 01:02:46 +0200 (CEST)","from pendragon.ideasonboard.com (81-175-209-231.bb.dnainternet.fi\n\t[81.175.209.231])\n\tby perceval.ideasonboard.com (Postfix) with UTF8SMTPSA id 21B3A594;\n\tSat, 16 Aug 2025 01:01:51 +0200 (CEST)"],"Authentication-Results":"lancelot.ideasonboard.com; dkim=pass (1024-bit key;\n\tunprotected) header.d=ideasonboard.com header.i=@ideasonboard.com\n\theader.b=\"iZ1pqKA8\"; dkim-atps=neutral","DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com;\n\ts=mail; t=1755298911;\n\tbh=rz7HCkXM4IoJTQU6yywYqSewo2DlrEyGzXmVYZTn0A4=;\n\th=Date:From:To:Cc:Subject:References:In-Reply-To:From;\n\tb=iZ1pqKA8GeS5APruSCuHgDPzx/la4su/HS60iyhXceitsxvKjiMt5ecZ0iuaIEiDC\n\tJxpVdpUmFq4yW9hTEQs0eiWHAgzU8bMaHPlDVj6vTT6OlfmB5pEOsF/z8zcfDoh35P\n\tDRAd7fp/2UYt6iqkSjOFmkbQZWFp6CeTbsq68yYs=","Date":"Sat, 16 Aug 2025 02:02:25 +0300","From":"Laurent Pinchart <laurent.pinchart@ideasonboard.com>","To":"=?utf-8?q?Barnab=C3=A1s_P=C5=91cze?= <barnabas.pocze@ideasonboard.com>","Cc":"libcamera-devel@lists.libcamera.org","Subject":"Re: [RFC PATCH v1] apps: cam: Use signalfd","Message-ID":"<20250815230225.GO6201@pendragon.ideasonboard.com>","References":"<20250815141237.2235085-1-barnabas.pocze@ideasonboard.com>\n\t<20250815222143.GN6201@pendragon.ideasonboard.com>\n\t<18e78fa9-e9b1-4b1b-bfa9-a19c3585c4f2@ideasonboard.com>","MIME-Version":"1.0","Content-Type":"text/plain; charset=utf-8","Content-Disposition":"inline","Content-Transfer-Encoding":"8bit","In-Reply-To":"<18e78fa9-e9b1-4b1b-bfa9-a19c3585c4f2@ideasonboard.com>","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>"}},{"id":35459,"web_url":"https://patchwork.libcamera.org/comment/35459/","msgid":"<a3a93fc9-45af-4ee9-b281-361522fd8a0f@ideasonboard.com>","date":"2025-08-15T23:06:31","subject":"Re: [RFC PATCH v1] apps: cam: Use signalfd","submitter":{"id":216,"url":"https://patchwork.libcamera.org/api/people/216/","name":"Barnabás Pőcze","email":"barnabas.pocze@ideasonboard.com"},"content":"2025. 08. 16. 1:02 keltezéssel, Laurent Pinchart írta:\n> On Sat, Aug 16, 2025 at 12:28:49AM +0200, Barnabás Pőcze wrote:\n>> 2025. 08. 16. 0:21 keltezéssel, Laurent Pinchart írta:\n>>> On Fri, Aug 15, 2025 at 04:12:37PM +0200, Barnabás Pőcze wrote:\n>>>> Use a signalfd to detect `SIGINT` instead of registering a signal handler in\n>>>> order to remove the `CamApp` singleton. This is better than using a normal\n>>>> signal handler: a signal can arrive after the `CamApp` object has been destroyed,\n>>>> leading to a use-after-free. Modifying the `CamApp::app_` in the destructor is\n>>>> not a good alternative because that would not be async signal safe (unless it\n>>>> is made atomic).\n>>>\n>>> While this is true, it's also not really much of an issue in my opinion,\n>>> or at least a very minor one. Now that the patch exists lets get it\n>>> merged, but you don't have to hunt for these kind of problems in cam or\n>>> qcam :-)\n>>\n>> I actually did run into this on multiple occasions. :(\n> \n> Do you mean crashes because a signal arrived when the application was\n> closing ? How did that happen ?\n\nYes. Spamming ctrl+c to stop `cam`. I usually test with asan, and with that it is\neasy to trigger this issue; eventually it became a bit too annoying, hence this change.\n\n\n> \n>>>> Signed-off-by: Barnabás Pőcze <barnabas.pocze@ideasonboard.com>\n>>>> ---\n>>>>    src/apps/cam/main.cpp | 66 +++++++++++++++++++------------------------\n>>>>    1 file changed, 29 insertions(+), 37 deletions(-)\n>>>>\n>>>> diff --git a/src/apps/cam/main.cpp b/src/apps/cam/main.cpp\n>>>> index fa266eca6..cbc85b59f 100644\n>>>> --- a/src/apps/cam/main.cpp\n>>>> +++ b/src/apps/cam/main.cpp\n>>>> @@ -13,6 +13,8 @@\n>>>>    #include <signal.h>\n>>>>    #include <string.h>\n>>>>\n>>>> +#include <sys/signalfd.h>\n>>>\n>>> You can keep this grouped with the previous headers.\n>>>\n>>>> +\n>>>>    #include <libcamera/libcamera.h>\n>>>>    #include <libcamera/property_ids.h>\n>>>>\n>>>> @@ -27,15 +29,12 @@ using namespace libcamera;\n>>>>    class CamApp\n>>>>    {\n>>>>    public:\n>>>> -\tCamApp();\n>>>> -\n>>>> -\tstatic CamApp *instance();\n>>>> +\tCamApp(EventLoop &loop);\n>>>>\n>>>>    \tint init(int argc, char **argv);\n>>>>    \tvoid cleanup();\n>>>>\n>>>>    \tint exec();\n>>>> -\tvoid quit();\n>>>>\n>>>>    private:\n>>>>    \tvoid cameraAdded(std::shared_ptr<Camera> cam);\n>>>> @@ -46,26 +45,17 @@ private:\n>>>>\n>>>>    \tstatic std::string cameraName(const Camera *camera);\n>>>>\n>>>> -\tstatic CamApp *app_;\n>>>>    \tOptionsParser::Options options_;\n>>>>\n>>>>    \tstd::unique_ptr<CameraManager> cm_;\n>>>>\n>>>>    \tstd::atomic_uint loopUsers_;\n>>>> -\tEventLoop loop_;\n>>>> +\tEventLoop *loop_ = nullptr;\n>>>>    };\n>>>>\n>>>> -CamApp *CamApp::app_ = nullptr;\n>>>> -\n>>>> -CamApp::CamApp()\n>>>> -\t: loopUsers_(0)\n>>>> -{\n>>>> -\tCamApp::app_ = this;\n>>>> -}\n>>>> -\n>>>> -CamApp *CamApp::instance()\n>>>> +CamApp::CamApp(EventLoop &loop)\n>>>> +\t: loopUsers_(0), loop_(&loop)\n>>>>    {\n>>>> -\treturn CamApp::app_;\n>>>>    }\n>>>>\n>>>>    int CamApp::init(int argc, char **argv)\n>>>> @@ -103,11 +93,6 @@ int CamApp::exec()\n>>>>    \treturn ret;\n>>>>    }\n>>>>\n>>>> -void CamApp::quit()\n>>>> -{\n>>>> -\tloop_.exit();\n>>>> -}\n>>>> -\n>>>>    int CamApp::parseOptions(int argc, char *argv[])\n>>>>    {\n>>>>    \tStreamKeyValueParser streamKeyValue;\n>>>> @@ -205,7 +190,7 @@ void CamApp::cameraRemoved(std::shared_ptr<Camera> cam)\n>>>>    void CamApp::captureDone()\n>>>>    {\n>>>>    \tif (--loopUsers_ == 0)\n>>>> -\t\tEventLoop::instance()->exit(0);\n>>>> +\t\tloop_->exit(0);\n>>>>    }\n>>>>\n>>>>    int CamApp::run()\n>>>> @@ -290,7 +275,7 @@ int CamApp::run()\n>>>>    \t}\n>>>>\n>>>>    \tif (loopUsers_)\n>>>> -\t\tloop_.exec();\n>>>> +\t\tloop_->exec();\n>>>>\n>>>>    \t/* 6. Stop capture. */\n>>>>    \tfor (const auto &session : sessions) {\n>>>> @@ -345,29 +330,36 @@ std::string CamApp::cameraName(const Camera *camera)\n>>>>    \treturn name;\n>>>>    }\n>>>>\n>>>> -namespace {\n>>>> -\n>>>> -void signalHandler([[maybe_unused]] int signal)\n>>>> +int main(int argc, char **argv)\n>>>>    {\n>>>> -\tstd::cout << \"Exiting\" << std::endl;\n>>>> -\tCamApp::instance()->quit();\n>>>> -}\n>>>> +\tauto sigfd = [] {\n>>>> +\t\tsigset_t ss;\n>>>>\n>>>> -} /* namespace */\n>>>> +\t\tsigemptyset(&ss);\n>>>> +\t\tsigaddset(&ss, SIGINT);\n>>>> +\t\tsigprocmask(SIG_BLOCK, &ss, nullptr);\n>>>>\n>>>> -int main(int argc, char **argv)\n>>>> -{\n>>>> -\tCamApp app;\n>>>> +\t\treturn UniqueFD(signalfd(-1, &ss, SFD_CLOEXEC | SFD_NONBLOCK));\n>>>> +\t}();\n>>>\n>>> Why a lambda ?\n>>>\n>>>> +\tif (!sigfd.isValid())\n>>>> +\t\treturn EXIT_FAILURE;\n>>>> +\n>>>> +\tEventLoop loop;\n>>>> +\tCamApp app(loop);\n>>>>    \tint ret;\n>>>>\n>>>> +\tloop.addFdEvent(sigfd.get(), EventLoop::Read, [&] {\n>>>> +\t\tsignalfd_siginfo si;\n>>>> +\t\tstd::ignore = read(sigfd.get(), &si, sizeof(si));\n>>>> +\n>>>> +\t\tstd::cout << \"Exiting\" << std::endl;\n>>>> +\t\tloop.exit(0);\n>>>> +\t});\n>>>\n>>> It could make things simpler to handle this in the CamApp class, you\n>>> won't have to change where the EventLoop is instantiated.\n>>>\n>>>> +\n>>>>    \tret = app.init(argc, argv);\n>>>>    \tif (ret)\n>>>>    \t\treturn ret == -EINTR ? 0 : EXIT_FAILURE;\n>>>>\n>>>> -\tstruct sigaction sa = {};\n>>>> -\tsa.sa_handler = &signalHandler;\n>>>> -\tsigaction(SIGINT, &sa, nullptr);\n>>>> -\n>>>>    \tif (app.exec())\n>>>>    \t\treturn EXIT_FAILURE;\n>>>>\n>","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 69541BDCC1\n\tfor <parsemail@patchwork.libcamera.org>;\n\tFri, 15 Aug 2025 23:06:37 +0000 (UTC)","from lancelot.ideasonboard.com (localhost [IPv6:::1])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTP id 1F28B6925C;\n\tSat, 16 Aug 2025 01:06:36 +0200 (CEST)","from perceval.ideasonboard.com (perceval.ideasonboard.com\n\t[IPv6:2001:4b98:dc2:55:216:3eff:fef7:d647])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTPS id 1120D61427\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tSat, 16 Aug 2025 01:06:35 +0200 (CEST)","from [192.168.33.21] (185.221.141.188.nat.pool.zt.hu\n\t[185.221.141.188])\n\tby perceval.ideasonboard.com (Postfix) with ESMTPSA id 963A0594;\n\tSat, 16 Aug 2025 01:05:39 +0200 (CEST)"],"Authentication-Results":"lancelot.ideasonboard.com; dkim=pass (1024-bit key;\n\tunprotected) header.d=ideasonboard.com header.i=@ideasonboard.com\n\theader.b=\"OyQYYou5\"; dkim-atps=neutral","DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com;\n\ts=mail; t=1755299139;\n\tbh=kZGKEStOkKeih5UApuMI29WY5LHv7B7XZ5FR0nNtJFI=;\n\th=Date:Subject:To:Cc:References:From:In-Reply-To:From;\n\tb=OyQYYou52fYNFPoDfCwkKb71twag6Gmkrtn/SuDtpVkdQmLtJ06Z1e9k83JsPy1aC\n\tC8Xl2xWHuMWA43JHYGJ5FLIKvcPDU813mX03LlBvOzJJ0PitPe98gVadmgybTlheAd\n\tNLw5XPNcRZ/Uanh9b/Shs/t3iOboUhgMq9e20xyM=","Message-ID":"<a3a93fc9-45af-4ee9-b281-361522fd8a0f@ideasonboard.com>","Date":"Sat, 16 Aug 2025 01:06:31 +0200","MIME-Version":"1.0","User-Agent":"Mozilla Thunderbird","Subject":"Re: [RFC PATCH v1] apps: cam: Use signalfd","To":"Laurent Pinchart <laurent.pinchart@ideasonboard.com>","Cc":"libcamera-devel@lists.libcamera.org","References":"<20250815141237.2235085-1-barnabas.pocze@ideasonboard.com>\n\t<20250815222143.GN6201@pendragon.ideasonboard.com>\n\t<18e78fa9-e9b1-4b1b-bfa9-a19c3585c4f2@ideasonboard.com>\n\t<20250815230225.GO6201@pendragon.ideasonboard.com>","From":"=?utf-8?q?Barnab=C3=A1s_P=C5=91cze?= <barnabas.pocze@ideasonboard.com>","Content-Language":"en-US, hu-HU","In-Reply-To":"<20250815230225.GO6201@pendragon.ideasonboard.com>","Content-Type":"text/plain; charset=UTF-8; format=flowed","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>"}},{"id":35466,"web_url":"https://patchwork.libcamera.org/comment/35466/","msgid":"<7a93d5da-97ab-43ba-b11f-2c08413c098c@ideasonboard.com>","date":"2025-08-18T08:26:09","subject":"Re: [RFC PATCH v1] apps: cam: Use signalfd","submitter":{"id":216,"url":"https://patchwork.libcamera.org/api/people/216/","name":"Barnabás Pőcze","email":"barnabas.pocze@ideasonboard.com"},"content":"2025. 08. 16. 0:21 keltezéssel, Laurent Pinchart írta:\n> Hi Barnabás,\n> \n> Thank you for the patch.\n> \n> On Fri, Aug 15, 2025 at 04:12:37PM +0200, Barnabás Pőcze wrote:\n>> Use a signalfd to detect `SIGINT` instead of registering a signal handler in\n>> order to remove the `CamApp` singleton. This is better than using a normal\n>> signal handler: a signal can arrive after the `CamApp` object has been destroyed,\n>> leading to a use-after-free. Modifying the `CamApp::app_` in the destructor is\n>> not a good alternative because that would not be async signal safe (unless it\n>> is made atomic).\n> \n> While this is true, it's also not really much of an issue in my opinion,\n> or at least a very minor one. Now that the patch exists lets get it\n> merged, but you don't have to hunt for these kind of problems in cam or\n> qcam :-)\n> \n>> Signed-off-by: Barnabás Pőcze <barnabas.pocze@ideasonboard.com>\n>> ---\n>>   src/apps/cam/main.cpp | 66 +++++++++++++++++++------------------------\n>>   1 file changed, 29 insertions(+), 37 deletions(-)\n>>\n>> diff --git a/src/apps/cam/main.cpp b/src/apps/cam/main.cpp\n>> index fa266eca6..cbc85b59f 100644\n>> --- a/src/apps/cam/main.cpp\n>> +++ b/src/apps/cam/main.cpp\n>> @@ -13,6 +13,8 @@\n>>   #include <signal.h>\n>>   #include <string.h>\n>>\n>> +#include <sys/signalfd.h>\n> \n> You can keep this grouped with the previous headers.\n\nOK\n\n\n> \n>> +\n>>   #include <libcamera/libcamera.h>\n>>   #include <libcamera/property_ids.h>\n>>\n>> @@ -27,15 +29,12 @@ using namespace libcamera;\n>>   class CamApp\n>>   {\n>>   public:\n>> -\tCamApp();\n>> -\n>> -\tstatic CamApp *instance();\n>> +\tCamApp(EventLoop &loop);\n>>\n>>   \tint init(int argc, char **argv);\n>>   \tvoid cleanup();\n>>\n>>   \tint exec();\n>> -\tvoid quit();\n>>\n>>   private:\n>>   \tvoid cameraAdded(std::shared_ptr<Camera> cam);\n>> @@ -46,26 +45,17 @@ private:\n>>\n>>   \tstatic std::string cameraName(const Camera *camera);\n>>\n>> -\tstatic CamApp *app_;\n>>   \tOptionsParser::Options options_;\n>>\n>>   \tstd::unique_ptr<CameraManager> cm_;\n>>\n>>   \tstd::atomic_uint loopUsers_;\n>> -\tEventLoop loop_;\n>> +\tEventLoop *loop_ = nullptr;\n>>   };\n>>\n>> -CamApp *CamApp::app_ = nullptr;\n>> -\n>> -CamApp::CamApp()\n>> -\t: loopUsers_(0)\n>> -{\n>> -\tCamApp::app_ = this;\n>> -}\n>> -\n>> -CamApp *CamApp::instance()\n>> +CamApp::CamApp(EventLoop &loop)\n>> +\t: loopUsers_(0), loop_(&loop)\n>>   {\n>> -\treturn CamApp::app_;\n>>   }\n>>\n>>   int CamApp::init(int argc, char **argv)\n>> @@ -103,11 +93,6 @@ int CamApp::exec()\n>>   \treturn ret;\n>>   }\n>>\n>> -void CamApp::quit()\n>> -{\n>> -\tloop_.exit();\n>> -}\n>> -\n>>   int CamApp::parseOptions(int argc, char *argv[])\n>>   {\n>>   \tStreamKeyValueParser streamKeyValue;\n>> @@ -205,7 +190,7 @@ void CamApp::cameraRemoved(std::shared_ptr<Camera> cam)\n>>   void CamApp::captureDone()\n>>   {\n>>   \tif (--loopUsers_ == 0)\n>> -\t\tEventLoop::instance()->exit(0);\n>> +\t\tloop_->exit(0);\n>>   }\n>>\n>>   int CamApp::run()\n>> @@ -290,7 +275,7 @@ int CamApp::run()\n>>   \t}\n>>\n>>   \tif (loopUsers_)\n>> -\t\tloop_.exec();\n>> +\t\tloop_->exec();\n>>\n>>   \t/* 6. Stop capture. */\n>>   \tfor (const auto &session : sessions) {\n>> @@ -345,29 +330,36 @@ std::string CamApp::cameraName(const Camera *camera)\n>>   \treturn name;\n>>   }\n>>\n>> -namespace {\n>> -\n>> -void signalHandler([[maybe_unused]] int signal)\n>> +int main(int argc, char **argv)\n>>   {\n>> -\tstd::cout << \"Exiting\" << std::endl;\n>> -\tCamApp::instance()->quit();\n>> -}\n>> +\tauto sigfd = [] {\n>> +\t\tsigset_t ss;\n>>\n>> -} /* namespace */\n>> +\t\tsigemptyset(&ss);\n>> +\t\tsigaddset(&ss, SIGINT);\n>> +\t\tsigprocmask(SIG_BLOCK, &ss, nullptr);\n>>\n>> -int main(int argc, char **argv)\n>> -{\n>> -\tCamApp app;\n>> +\t\treturn UniqueFD(signalfd(-1, &ss, SFD_CLOEXEC | SFD_NONBLOCK));\n>> +\t}();\n> \n> Why a lambda ?\n\nGenerally I like to use a lambda to separate a piece of \"throw away\" logic that\nI don't deem appropriate for moving into a function, but still want to separate\nfrom the main parts, especially if it has a well-defined result, or it defines variables,\netc.\n\n\n> \n>> +\tif (!sigfd.isValid())\n>> +\t\treturn EXIT_FAILURE;\n>> +\n>> +\tEventLoop loop;\n>> +\tCamApp app(loop);\n>>   \tint ret;\n>>\n>> +\tloop.addFdEvent(sigfd.get(), EventLoop::Read, [&] {\n>> +\t\tsignalfd_siginfo si;\n>> +\t\tstd::ignore = read(sigfd.get(), &si, sizeof(si));\n>> +\n>> +\t\tstd::cout << \"Exiting\" << std::endl;\n>> +\t\tloop.exit(0);\n>> +\t});\n> \n> It could make things simpler to handle this in the CamApp class, you\n> won't have to change where the EventLoop is instantiated.\n\nMy motivation here was that handling signals is kind of a global thing,\nhence it is moved into `main()`. But I suppose it is fewer changes if\nthis is handled in e.g. `CamApp::init()`.\n\n\nRegards,\nBarnabás Pőcze\n\n> \n>> +\n>>   \tret = app.init(argc, argv);\n>>   \tif (ret)\n>>   \t\treturn ret == -EINTR ? 0 : EXIT_FAILURE;\n>>\n>> -\tstruct sigaction sa = {};\n>> -\tsa.sa_handler = &signalHandler;\n>> -\tsigaction(SIGINT, &sa, nullptr);\n>> -\n>>   \tif (app.exec())\n>>   \t\treturn EXIT_FAILURE;\n>>","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 03DBFBDCC1\n\tfor <parsemail@patchwork.libcamera.org>;\n\tMon, 18 Aug 2025 08:26:17 +0000 (UTC)","from lancelot.ideasonboard.com (localhost [IPv6:::1])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTP id CF2256925B;\n\tMon, 18 Aug 2025 10:26:15 +0200 (CEST)","from perceval.ideasonboard.com (perceval.ideasonboard.com\n\t[IPv6:2001:4b98:dc2:55:216:3eff:fef7:d647])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTPS id 0FD136924E\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tMon, 18 Aug 2025 10:26:14 +0200 (CEST)","from [192.168.33.19] (185.221.141.188.nat.pool.zt.hu\n\t[185.221.141.188])\n\tby perceval.ideasonboard.com (Postfix) with ESMTPSA id B88502394;\n\tMon, 18 Aug 2025 10:25:16 +0200 (CEST)"],"Authentication-Results":"lancelot.ideasonboard.com; dkim=pass (1024-bit key;\n\tunprotected) header.d=ideasonboard.com header.i=@ideasonboard.com\n\theader.b=\"Fv9AYGUP\"; dkim-atps=neutral","DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com;\n\ts=mail; t=1755505516;\n\tbh=Kz74+W2XpVNWexsQ0M9WuNzk9gDhQrynZukXkJT/HeE=;\n\th=Date:Subject:To:Cc:References:From:In-Reply-To:From;\n\tb=Fv9AYGUP2S1AbVRue6cs9SqRmFFxEa5KDZMpiHBRSpxjujqgBUeOLbo4j9T364VA+\n\tAicKLO4UzTdZufYQg8L31ohMjwWJ6bESf8rDjH7tuGQ4fWpTjwBwh4bEgvNqGkBUM8\n\tvDTGr6RRWxlBAHHU5JYYHfdtB2YJa94JXBjR1PK4=","Message-ID":"<7a93d5da-97ab-43ba-b11f-2c08413c098c@ideasonboard.com>","Date":"Mon, 18 Aug 2025 10:26:09 +0200","MIME-Version":"1.0","User-Agent":"Mozilla Thunderbird","Subject":"Re: [RFC PATCH v1] apps: cam: Use signalfd","To":"Laurent Pinchart <laurent.pinchart@ideasonboard.com>","Cc":"libcamera-devel@lists.libcamera.org","References":"<20250815141237.2235085-1-barnabas.pocze@ideasonboard.com>\n\t<20250815222143.GN6201@pendragon.ideasonboard.com>","From":"=?utf-8?q?Barnab=C3=A1s_P=C5=91cze?= <barnabas.pocze@ideasonboard.com>","Content-Language":"en-US, hu-HU","In-Reply-To":"<20250815222143.GN6201@pendragon.ideasonboard.com>","Content-Type":"text/plain; charset=UTF-8; format=flowed","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>"}},{"id":35494,"web_url":"https://patchwork.libcamera.org/comment/35494/","msgid":"<20250819015340.GH5862@pendragon.ideasonboard.com>","date":"2025-08-19T01:53:40","subject":"Re: [RFC PATCH v1] apps: cam: Use signalfd","submitter":{"id":2,"url":"https://patchwork.libcamera.org/api/people/2/","name":"Laurent Pinchart","email":"laurent.pinchart@ideasonboard.com"},"content":"On Mon, Aug 18, 2025 at 10:26:09AM +0200, Barnabás Pőcze wrote:\n> 2025. 08. 16. 0:21 keltezéssel, Laurent Pinchart írta:\n> > On Fri, Aug 15, 2025 at 04:12:37PM +0200, Barnabás Pőcze wrote:\n> >> Use a signalfd to detect `SIGINT` instead of registering a signal handler in\n> >> order to remove the `CamApp` singleton. This is better than using a normal\n> >> signal handler: a signal can arrive after the `CamApp` object has been destroyed,\n> >> leading to a use-after-free. Modifying the `CamApp::app_` in the destructor is\n> >> not a good alternative because that would not be async signal safe (unless it\n> >> is made atomic).\n> > \n> > While this is true, it's also not really much of an issue in my opinion,\n> > or at least a very minor one. Now that the patch exists lets get it\n> > merged, but you don't have to hunt for these kind of problems in cam or\n> > qcam :-)\n> > \n> >> Signed-off-by: Barnabás Pőcze <barnabas.pocze@ideasonboard.com>\n> >> ---\n> >>   src/apps/cam/main.cpp | 66 +++++++++++++++++++------------------------\n> >>   1 file changed, 29 insertions(+), 37 deletions(-)\n> >>\n> >> diff --git a/src/apps/cam/main.cpp b/src/apps/cam/main.cpp\n> >> index fa266eca6..cbc85b59f 100644\n> >> --- a/src/apps/cam/main.cpp\n> >> +++ b/src/apps/cam/main.cpp\n> >> @@ -13,6 +13,8 @@\n> >>   #include <signal.h>\n> >>   #include <string.h>\n> >>\n> >> +#include <sys/signalfd.h>\n> > \n> > You can keep this grouped with the previous headers.\n> \n> OK\n> \n> >> +\n> >>   #include <libcamera/libcamera.h>\n> >>   #include <libcamera/property_ids.h>\n> >>\n> >> @@ -27,15 +29,12 @@ using namespace libcamera;\n> >>   class CamApp\n> >>   {\n> >>   public:\n> >> -\tCamApp();\n> >> -\n> >> -\tstatic CamApp *instance();\n> >> +\tCamApp(EventLoop &loop);\n> >>\n> >>   \tint init(int argc, char **argv);\n> >>   \tvoid cleanup();\n> >>\n> >>   \tint exec();\n> >> -\tvoid quit();\n> >>\n> >>   private:\n> >>   \tvoid cameraAdded(std::shared_ptr<Camera> cam);\n> >> @@ -46,26 +45,17 @@ private:\n> >>\n> >>   \tstatic std::string cameraName(const Camera *camera);\n> >>\n> >> -\tstatic CamApp *app_;\n> >>   \tOptionsParser::Options options_;\n> >>\n> >>   \tstd::unique_ptr<CameraManager> cm_;\n> >>\n> >>   \tstd::atomic_uint loopUsers_;\n> >> -\tEventLoop loop_;\n> >> +\tEventLoop *loop_ = nullptr;\n> >>   };\n> >>\n> >> -CamApp *CamApp::app_ = nullptr;\n> >> -\n> >> -CamApp::CamApp()\n> >> -\t: loopUsers_(0)\n> >> -{\n> >> -\tCamApp::app_ = this;\n> >> -}\n> >> -\n> >> -CamApp *CamApp::instance()\n> >> +CamApp::CamApp(EventLoop &loop)\n> >> +\t: loopUsers_(0), loop_(&loop)\n> >>   {\n> >> -\treturn CamApp::app_;\n> >>   }\n> >>\n> >>   int CamApp::init(int argc, char **argv)\n> >> @@ -103,11 +93,6 @@ int CamApp::exec()\n> >>   \treturn ret;\n> >>   }\n> >>\n> >> -void CamApp::quit()\n> >> -{\n> >> -\tloop_.exit();\n> >> -}\n> >> -\n> >>   int CamApp::parseOptions(int argc, char *argv[])\n> >>   {\n> >>   \tStreamKeyValueParser streamKeyValue;\n> >> @@ -205,7 +190,7 @@ void CamApp::cameraRemoved(std::shared_ptr<Camera> cam)\n> >>   void CamApp::captureDone()\n> >>   {\n> >>   \tif (--loopUsers_ == 0)\n> >> -\t\tEventLoop::instance()->exit(0);\n> >> +\t\tloop_->exit(0);\n> >>   }\n> >>\n> >>   int CamApp::run()\n> >> @@ -290,7 +275,7 @@ int CamApp::run()\n> >>   \t}\n> >>\n> >>   \tif (loopUsers_)\n> >> -\t\tloop_.exec();\n> >> +\t\tloop_->exec();\n> >>\n> >>   \t/* 6. Stop capture. */\n> >>   \tfor (const auto &session : sessions) {\n> >> @@ -345,29 +330,36 @@ std::string CamApp::cameraName(const Camera *camera)\n> >>   \treturn name;\n> >>   }\n> >>\n> >> -namespace {\n> >> -\n> >> -void signalHandler([[maybe_unused]] int signal)\n> >> +int main(int argc, char **argv)\n> >>   {\n> >> -\tstd::cout << \"Exiting\" << std::endl;\n> >> -\tCamApp::instance()->quit();\n> >> -}\n> >> +\tauto sigfd = [] {\n> >> +\t\tsigset_t ss;\n> >>\n> >> -} /* namespace */\n> >> +\t\tsigemptyset(&ss);\n> >> +\t\tsigaddset(&ss, SIGINT);\n> >> +\t\tsigprocmask(SIG_BLOCK, &ss, nullptr);\n> >>\n> >> -int main(int argc, char **argv)\n> >> -{\n> >> -\tCamApp app;\n> >> +\t\treturn UniqueFD(signalfd(-1, &ss, SFD_CLOEXEC | SFD_NONBLOCK));\n> >> +\t}();\n> > \n> > Why a lambda ?\n> \n> Generally I like to use a lambda to separate a piece of \"throw away\" logic that\n> I don't deem appropriate for moving into a function, but still want to separate\n> from the main parts, especially if it has a well-defined result, or it defines variables,\n> etc.\n\nI see what you mean, but that kind of clashes with the coding style of\nlibcamera. Do we want to do this more globally ?\n\n> >> +\tif (!sigfd.isValid())\n> >> +\t\treturn EXIT_FAILURE;\n> >> +\n> >> +\tEventLoop loop;\n> >> +\tCamApp app(loop);\n> >>   \tint ret;\n> >>\n> >> +\tloop.addFdEvent(sigfd.get(), EventLoop::Read, [&] {\n> >> +\t\tsignalfd_siginfo si;\n> >> +\t\tstd::ignore = read(sigfd.get(), &si, sizeof(si));\n> >> +\n> >> +\t\tstd::cout << \"Exiting\" << std::endl;\n> >> +\t\tloop.exit(0);\n> >> +\t});\n> > \n> > It could make things simpler to handle this in the CamApp class, you\n> > won't have to change where the EventLoop is instantiated.\n> \n> My motivation here was that handling signals is kind of a global thing,\n\nI thought the same, and then also thought that CamApp is kind of a\nglobal thing too :-) In this specific case, I think we can minimize the\nchanges, and handling the signal in the CamApp class shouldn't be a big\ndeal.\n\n> hence it is moved into `main()`. But I suppose it is fewer changes if\n> this is handled in e.g. `CamApp::init()`.\n> \n> >> +\n> >>   \tret = app.init(argc, argv);\n> >>   \tif (ret)\n> >>   \t\treturn ret == -EINTR ? 0 : EXIT_FAILURE;\n> >>\n> >> -\tstruct sigaction sa = {};\n> >> -\tsa.sa_handler = &signalHandler;\n> >> -\tsigaction(SIGINT, &sa, nullptr);\n> >> -\n> >>   \tif (app.exec())\n> >>   \t\treturn EXIT_FAILURE;\n> >>","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 3F388BDCC1\n\tfor <parsemail@patchwork.libcamera.org>;\n\tTue, 19 Aug 2025 01:54:06 +0000 (UTC)","from lancelot.ideasonboard.com (localhost [IPv6:::1])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTP id 368EB69265;\n\tTue, 19 Aug 2025 03:54:05 +0200 (CEST)","from perceval.ideasonboard.com (perceval.ideasonboard.com\n\t[IPv6:2001:4b98:dc2:55:216:3eff:fef7:d647])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTPS id 88E11613C5\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tTue, 19 Aug 2025 03:54:04 +0200 (CEST)","from pendragon.ideasonboard.com (81-175-209-231.bb.dnainternet.fi\n\t[81.175.209.231])\n\tby perceval.ideasonboard.com (Postfix) with UTF8SMTPSA id AB9CC446;\n\tTue, 19 Aug 2025 03:53:06 +0200 (CEST)"],"Authentication-Results":"lancelot.ideasonboard.com; dkim=pass (1024-bit key;\n\tunprotected) header.d=ideasonboard.com header.i=@ideasonboard.com\n\theader.b=\"RbaP4ubx\"; dkim-atps=neutral","DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com;\n\ts=mail; t=1755568386;\n\tbh=QgG+WJjDBvKN42pEB22K/2BVn/LUOdTtXat3AwXjYGg=;\n\th=Date:From:To:Cc:Subject:References:In-Reply-To:From;\n\tb=RbaP4ubxCHIWhtsmulz/iJZbqPzXhr/79/xZO7J6TjGXLPSqsMewnRCo3rNRjZUvO\n\tPPbWPiIdUYYr6Hw1UdqsbaOOmcNIpWvNOnB1wS/WyfbIIW7nsO3Dyoh5WtRmcX3krP\n\tk4PmdOuBgCtFo8nOhgjQnVqCfCmtGoe0S95l+Muw=","Date":"Tue, 19 Aug 2025 04:53:40 +0300","From":"Laurent Pinchart <laurent.pinchart@ideasonboard.com>","To":"=?utf-8?q?Barnab=C3=A1s_P=C5=91cze?= <barnabas.pocze@ideasonboard.com>","Cc":"libcamera-devel@lists.libcamera.org","Subject":"Re: [RFC PATCH v1] apps: cam: Use signalfd","Message-ID":"<20250819015340.GH5862@pendragon.ideasonboard.com>","References":"<20250815141237.2235085-1-barnabas.pocze@ideasonboard.com>\n\t<20250815222143.GN6201@pendragon.ideasonboard.com>\n\t<7a93d5da-97ab-43ba-b11f-2c08413c098c@ideasonboard.com>","MIME-Version":"1.0","Content-Type":"text/plain; charset=utf-8","Content-Disposition":"inline","Content-Transfer-Encoding":"8bit","In-Reply-To":"<7a93d5da-97ab-43ba-b11f-2c08413c098c@ideasonboard.com>","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>"}},{"id":35498,"web_url":"https://patchwork.libcamera.org/comment/35498/","msgid":"<704354ae-a551-4111-a8a1-80ea277902c0@ideasonboard.com>","date":"2025-08-19T07:29:35","subject":"Re: [RFC PATCH v1] apps: cam: Use signalfd","submitter":{"id":216,"url":"https://patchwork.libcamera.org/api/people/216/","name":"Barnabás Pőcze","email":"barnabas.pocze@ideasonboard.com"},"content":"2025. 08. 19. 3:53 keltezéssel, Laurent Pinchart írta:\n> On Mon, Aug 18, 2025 at 10:26:09AM +0200, Barnabás Pőcze wrote:\n>> 2025. 08. 16. 0:21 keltezéssel, Laurent Pinchart írta:\n>>> On Fri, Aug 15, 2025 at 04:12:37PM +0200, Barnabás Pőcze wrote:\n>>>> Use a signalfd to detect `SIGINT` instead of registering a signal handler in\n>>>> order to remove the `CamApp` singleton. This is better than using a normal\n>>>> signal handler: a signal can arrive after the `CamApp` object has been destroyed,\n>>>> leading to a use-after-free. Modifying the `CamApp::app_` in the destructor is\n>>>> not a good alternative because that would not be async signal safe (unless it\n>>>> is made atomic).\n>>>\n>>> While this is true, it's also not really much of an issue in my opinion,\n>>> or at least a very minor one. Now that the patch exists lets get it\n>>> merged, but you don't have to hunt for these kind of problems in cam or\n>>> qcam :-)\n>>>\n>>>> Signed-off-by: Barnabás Pőcze <barnabas.pocze@ideasonboard.com>\n>>>> ---\n>>>>    src/apps/cam/main.cpp | 66 +++++++++++++++++++------------------------\n>>>>    1 file changed, 29 insertions(+), 37 deletions(-)\n>>>>\n>>>> diff --git a/src/apps/cam/main.cpp b/src/apps/cam/main.cpp\n>>>> index fa266eca6..cbc85b59f 100644\n>>>> --- a/src/apps/cam/main.cpp\n>>>> +++ b/src/apps/cam/main.cpp\n>>>> @@ -13,6 +13,8 @@\n>>>>    #include <signal.h>\n>>>>    #include <string.h>\n>>>>\n>>>> +#include <sys/signalfd.h>\n>>>\n>>> You can keep this grouped with the previous headers.\n>>\n>> OK\n>>\n>>>> +\n>>>>    #include <libcamera/libcamera.h>\n>>>>    #include <libcamera/property_ids.h>\n>>>>\n>>>> @@ -27,15 +29,12 @@ using namespace libcamera;\n>>>>    class CamApp\n>>>>    {\n>>>>    public:\n>>>> -\tCamApp();\n>>>> -\n>>>> -\tstatic CamApp *instance();\n>>>> +\tCamApp(EventLoop &loop);\n>>>>\n>>>>    \tint init(int argc, char **argv);\n>>>>    \tvoid cleanup();\n>>>>\n>>>>    \tint exec();\n>>>> -\tvoid quit();\n>>>>\n>>>>    private:\n>>>>    \tvoid cameraAdded(std::shared_ptr<Camera> cam);\n>>>> @@ -46,26 +45,17 @@ private:\n>>>>\n>>>>    \tstatic std::string cameraName(const Camera *camera);\n>>>>\n>>>> -\tstatic CamApp *app_;\n>>>>    \tOptionsParser::Options options_;\n>>>>\n>>>>    \tstd::unique_ptr<CameraManager> cm_;\n>>>>\n>>>>    \tstd::atomic_uint loopUsers_;\n>>>> -\tEventLoop loop_;\n>>>> +\tEventLoop *loop_ = nullptr;\n>>>>    };\n>>>>\n>>>> -CamApp *CamApp::app_ = nullptr;\n>>>> -\n>>>> -CamApp::CamApp()\n>>>> -\t: loopUsers_(0)\n>>>> -{\n>>>> -\tCamApp::app_ = this;\n>>>> -}\n>>>> -\n>>>> -CamApp *CamApp::instance()\n>>>> +CamApp::CamApp(EventLoop &loop)\n>>>> +\t: loopUsers_(0), loop_(&loop)\n>>>>    {\n>>>> -\treturn CamApp::app_;\n>>>>    }\n>>>>\n>>>>    int CamApp::init(int argc, char **argv)\n>>>> @@ -103,11 +93,6 @@ int CamApp::exec()\n>>>>    \treturn ret;\n>>>>    }\n>>>>\n>>>> -void CamApp::quit()\n>>>> -{\n>>>> -\tloop_.exit();\n>>>> -}\n>>>> -\n>>>>    int CamApp::parseOptions(int argc, char *argv[])\n>>>>    {\n>>>>    \tStreamKeyValueParser streamKeyValue;\n>>>> @@ -205,7 +190,7 @@ void CamApp::cameraRemoved(std::shared_ptr<Camera> cam)\n>>>>    void CamApp::captureDone()\n>>>>    {\n>>>>    \tif (--loopUsers_ == 0)\n>>>> -\t\tEventLoop::instance()->exit(0);\n>>>> +\t\tloop_->exit(0);\n>>>>    }\n>>>>\n>>>>    int CamApp::run()\n>>>> @@ -290,7 +275,7 @@ int CamApp::run()\n>>>>    \t}\n>>>>\n>>>>    \tif (loopUsers_)\n>>>> -\t\tloop_.exec();\n>>>> +\t\tloop_->exec();\n>>>>\n>>>>    \t/* 6. Stop capture. */\n>>>>    \tfor (const auto &session : sessions) {\n>>>> @@ -345,29 +330,36 @@ std::string CamApp::cameraName(const Camera *camera)\n>>>>    \treturn name;\n>>>>    }\n>>>>\n>>>> -namespace {\n>>>> -\n>>>> -void signalHandler([[maybe_unused]] int signal)\n>>>> +int main(int argc, char **argv)\n>>>>    {\n>>>> -\tstd::cout << \"Exiting\" << std::endl;\n>>>> -\tCamApp::instance()->quit();\n>>>> -}\n>>>> +\tauto sigfd = [] {\n>>>> +\t\tsigset_t ss;\n>>>>\n>>>> -} /* namespace */\n>>>> +\t\tsigemptyset(&ss);\n>>>> +\t\tsigaddset(&ss, SIGINT);\n>>>> +\t\tsigprocmask(SIG_BLOCK, &ss, nullptr);\n>>>>\n>>>> -int main(int argc, char **argv)\n>>>> -{\n>>>> -\tCamApp app;\n>>>> +\t\treturn UniqueFD(signalfd(-1, &ss, SFD_CLOEXEC | SFD_NONBLOCK));\n>>>> +\t}();\n>>>\n>>> Why a lambda ?\n>>\n>> Generally I like to use a lambda to separate a piece of \"throw away\" logic that\n>> I don't deem appropriate for moving into a function, but still want to separate\n>> from the main parts, especially if it has a well-defined result, or it defines variables,\n>> etc.\n> \n> I see what you mean, but that kind of clashes with the coding style of\n> libcamera. Do we want to do this more globally ?\n\nHmmm... I did not realize that. In any case I have already moved it into\n`CamApp::init()` without the lambda.\n\n\n> \n>>>> +\tif (!sigfd.isValid())\n>>>> +\t\treturn EXIT_FAILURE;\n>>>> +\n>>>> +\tEventLoop loop;\n>>>> +\tCamApp app(loop);\n>>>>    \tint ret;\n>>>>\n>>>> +\tloop.addFdEvent(sigfd.get(), EventLoop::Read, [&] {\n>>>> +\t\tsignalfd_siginfo si;\n>>>> +\t\tstd::ignore = read(sigfd.get(), &si, sizeof(si));\n>>>> +\n>>>> +\t\tstd::cout << \"Exiting\" << std::endl;\n>>>> +\t\tloop.exit(0);\n>>>> +\t});\n>>>\n>>> It could make things simpler to handle this in the CamApp class, you\n>>> won't have to change where the EventLoop is instantiated.\n>>\n>> My motivation here was that handling signals is kind of a global thing,\n> \n> I thought the same, and then also thought that CamApp is kind of a\n> global thing too :-) In this specific case, I think we can minimize the\n> changes, and handling the signal in the CamApp class shouldn't be a big\n> deal.\n> \n>> hence it is moved into `main()`. But I suppose it is fewer changes if\n>> this is handled in e.g. `CamApp::init()`.\n>>\n>>>> +\n>>>>    \tret = app.init(argc, argv);\n>>>>    \tif (ret)\n>>>>    \t\treturn ret == -EINTR ? 0 : EXIT_FAILURE;\n>>>>\n>>>> -\tstruct sigaction sa = {};\n>>>> -\tsa.sa_handler = &signalHandler;\n>>>> -\tsigaction(SIGINT, &sa, nullptr);\n>>>> -\n>>>>    \tif (app.exec())\n>>>>    \t\treturn EXIT_FAILURE;\n>>>>\n>","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 8E549BDCC1\n\tfor <parsemail@patchwork.libcamera.org>;\n\tTue, 19 Aug 2025 07:29:40 +0000 (UTC)","from lancelot.ideasonboard.com (localhost [IPv6:::1])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTP id 913BA6925F;\n\tTue, 19 Aug 2025 09:29:39 +0200 (CEST)","from perceval.ideasonboard.com (perceval.ideasonboard.com\n\t[213.167.242.64])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTPS id DE11B6923C\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tTue, 19 Aug 2025 09:29:37 +0200 (CEST)","from [192.168.33.20] (185.221.141.188.nat.pool.zt.hu\n\t[185.221.141.188])\n\tby perceval.ideasonboard.com (Postfix) with ESMTPSA id 150E1596;\n\tTue, 19 Aug 2025 09:28:39 +0200 (CEST)"],"Authentication-Results":"lancelot.ideasonboard.com; dkim=pass (1024-bit key;\n\tunprotected) header.d=ideasonboard.com header.i=@ideasonboard.com\n\theader.b=\"QE4KWX8l\"; dkim-atps=neutral","DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com;\n\ts=mail; t=1755588520;\n\tbh=GyCNO3nCJ0bEjw8FYVB5XIx4mny0Cz97cOzoGicsz4Q=;\n\th=Date:Subject:To:Cc:References:From:In-Reply-To:From;\n\tb=QE4KWX8ltt8J5OZtNMNiqfxAUUwt/LcDYZZImflPTjKTyhOlzAW9geir3pQgIKOqJ\n\tMft96RNBcsOPnQJYCuEc1yRRVHOtNFV8QSX6P0Belbb5zy7iSFXvjUXu5IEbM9Le7F\n\tIx7ZHAWVGHgPxSVTZKnRoQA5aVVF22w+UQDum/L0=","Message-ID":"<704354ae-a551-4111-a8a1-80ea277902c0@ideasonboard.com>","Date":"Tue, 19 Aug 2025 09:29:35 +0200","MIME-Version":"1.0","User-Agent":"Mozilla Thunderbird","Subject":"Re: [RFC PATCH v1] apps: cam: Use signalfd","To":"Laurent Pinchart <laurent.pinchart@ideasonboard.com>","Cc":"libcamera-devel@lists.libcamera.org","References":"<20250815141237.2235085-1-barnabas.pocze@ideasonboard.com>\n\t<20250815222143.GN6201@pendragon.ideasonboard.com>\n\t<7a93d5da-97ab-43ba-b11f-2c08413c098c@ideasonboard.com>\n\t<20250819015340.GH5862@pendragon.ideasonboard.com>","From":"=?utf-8?q?Barnab=C3=A1s_P=C5=91cze?= <barnabas.pocze@ideasonboard.com>","Content-Language":"en-US, hu-HU","In-Reply-To":"<20250819015340.GH5862@pendragon.ideasonboard.com>","Content-Type":"text/plain; charset=UTF-8; format=flowed","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>"}}]