[{"id":36015,"web_url":"https://patchwork.libcamera.org/comment/36015/","msgid":"<20250929071955.GB4646@pendragon.ideasonboard.com>","date":"2025-09-29T07:19:55","subject":"Re: [RFC PATCH v3] utils: codegen: ipc: Split proxy types","submitter":{"id":2,"url":"https://patchwork.libcamera.org/api/people/2/","name":"Laurent Pinchart","email":"laurent.pinchart@ideasonboard.com"},"content":"Hi Barnabás,\n\nOn Mon, Sep 22, 2025 at 12:03:39PM +0200, Barnabás Pőcze wrote:\n> Even though there is an abstract class to represent the interface of an IPA,\n> the threaded and IPC versions are still multiplexed using the same type,\n> which uses a boolean to actually dispatch to the right function.\n> \n> Instead of doing that, split the classes into \"threaded\" and \"isolated\"\n> variants, and make `IPAManager` choose accordingly.\n> \n> Signed-off-by: Barnabás Pőcze <barnabas.pocze@ideasonboard.com>\n> Reviewed-by: Paul Elder <paul.elder@ideasonboard.com>\n> ---\n> changes in v3:\n>   * rebase\n> \n> changes in v2:\n>   * fix typo\n>   * rename local -> threaded, remote -> isolated\n>   * have different log messages in the two variants\n> \n> v2: https://patchwork.libcamera.org/patch/24108/\n> v1: https://patchwork.libcamera.org/patch/24056/\n> ---\n>  include/libcamera/internal/ipa_manager.h      |   9 +-\n>  .../module_ipa_proxy.cpp.tmpl                 | 151 +++++++++---------\n>  .../module_ipa_proxy.h.tmpl                   |  56 +++++--\n>  3 files changed, 120 insertions(+), 96 deletions(-)\n> \n> diff --git a/include/libcamera/internal/ipa_manager.h b/include/libcamera/internal/ipa_manager.h\n> index b0b44c74a..f8ce78016 100644\n> --- a/include/libcamera/internal/ipa_manager.h\n> +++ b/include/libcamera/internal/ipa_manager.h\n> @@ -44,7 +44,14 @@ public:\n>  \t\t\treturn nullptr;\n> \n>  \t\tconst GlobalConfiguration &configuration = cm->_d()->configuration();\n> -\t\tstd::unique_ptr<T> proxy = std::make_unique<T>(m, !self->isSignatureValid(m), configuration);\n> +\n> +\t\tauto proxy = [&]() -> std::unique_ptr<T> {\n> +\t\t\tif (self->isSignatureValid(m))\n> +\t\t\t\treturn std::make_unique<typename T::Threaded>(m, configuration);\n> +\t\t\telse\n> +\t\t\t\treturn std::make_unique<typename T::Isolated>(m, configuration);\n> +\t\t}();\n> +\n>  \t\tif (!proxy->isValid()) {\n>  \t\t\tLOG(IPAManager, Error) << \"Failed to load proxy\";\n>  \t\t\treturn nullptr;\n> diff --git a/utils/codegen/ipc/generators/libcamera_templates/module_ipa_proxy.cpp.tmpl b/utils/codegen/ipc/generators/libcamera_templates/module_ipa_proxy.cpp.tmpl\n> index 18b4ab5e5..9bf2c58d3 100644\n> --- a/utils/codegen/ipc/generators/libcamera_templates/module_ipa_proxy.cpp.tmpl\n> +++ b/utils/codegen/ipc/generators/libcamera_templates/module_ipa_proxy.cpp.tmpl\n> @@ -45,36 +45,13 @@ namespace {{ns}} {\n>  {% endfor %}\n>  {%- endif %}\n> \n> -{{proxy_name}}::{{proxy_name}}(IPAModule *ipam, bool isolate, const GlobalConfiguration &configuration)\n> -\t: IPAProxy(ipam, configuration), isolate_(isolate),\n> -\t  controlSerializer_(ControlSerializer::Role::Proxy), seq_(0)\n> +{{proxy_name}}Threaded::{{proxy_name}}Threaded(IPAModule *ipam, const GlobalConfiguration &configuration)\n> +\t: {{proxy_name}}(ipam, configuration)\n>  {\n>  \tLOG(IPAProxy, Debug)\n> -\t\t<< \"initializing {{module_name}} proxy: loading IPA from \"\n> +\t\t<< \"initializing {{module_name}} proxy in thread: loading IPA from \"\n>  \t\t<< ipam->path();\n> \n> -\tif (isolate_) {\n> -\t\tconst std::string proxyWorkerPath = resolvePath(\"{{module_name}}_ipa_proxy\");\n> -\t\tif (proxyWorkerPath.empty()) {\n> -\t\t\tLOG(IPAProxy, Error)\n> -\t\t\t\t<< \"Failed to get proxy worker path\";\n> -\t\t\treturn;\n> -\t\t}\n> -\n> -\t\tauto ipc = std::make_unique<IPCPipeUnixSocket>(ipam->path().c_str(),\n> -\t\t\t\t\t\t\t       proxyWorkerPath.c_str());\n> -\t\tif (!ipc->isConnected()) {\n> -\t\t\tLOG(IPAProxy, Error) << \"Failed to create IPCPipe\";\n> -\t\t\treturn;\n> -\t\t}\n> -\n> -\t\tipc->recv.connect(this, &{{proxy_name}}::recvMessage);\n> -\n> -\t\tipc_ = std::move(ipc);\n> -\t\tvalid_ = true;\n> -\t\treturn;\n> -\t}\n> -\n>  \tif (!ipam->load())\n>  \t\treturn;\n> \n> @@ -89,59 +66,16 @@ namespace {{ns}} {\n>  \tproxy_.setIPA(ipa_.get());\n> \n>  {% for method in interface_event.methods %}\n> -\tipa_->{{method.mojom_name}}.connect(this, &{{proxy_name}}::{{method.mojom_name}}Thread);\n> +\tipa_->{{method.mojom_name}}.connect(this, &{{proxy_name}}Threaded::{{method.mojom_name}}Handler);\n>  {%- endfor %}\n> \n>  \tvalid_ = true;\n>  }\n> \n> -{{proxy_name}}::~{{proxy_name}}()\n> -{\n> -\tif (ipc_) {\n> -\t\tIPCMessage::Header header =\n> -\t\t\t{ static_cast<uint32_t>({{cmd_enum_name}}::Exit), seq_++ };\n> -\t\tIPCMessage msg(header);\n> -\t\tipc_->sendAsync(msg);\n> -\t}\n> -}\n> -\n> -{% if interface_event.methods|length > 0 %}\n> -void {{proxy_name}}::recvMessage(const IPCMessage &data)\n> -{\n> -\tsize_t dataSize = data.data().size();\n> -\t{{cmd_event_enum_name}} _cmd = static_cast<{{cmd_event_enum_name}}>(data.header().cmd);\n> -\n> -\tswitch (_cmd) {\n> -{%- for method in interface_event.methods %}\n> -\tcase {{cmd_event_enum_name}}::{{method.mojom_name|cap}}: {\n> -\t\t{{method.mojom_name}}IPC(data.data().cbegin(), dataSize, data.fds());\n> -\t\tbreak;\n> -\t}\n> -{%- endfor %}\n> -\tdefault:\n> -\t\tLOG(IPAProxy, Error) << \"Unknown command \" << static_cast<uint32_t>(_cmd);\n> -\t}\n> -}\n> -{%- endif %}\n> +{{proxy_name}}Threaded::~{{proxy_name}}Threaded() = default;\n\nCan't you omit the destructor from the class definition and drop this\nline, to let the compiler generate the default destructor ?\n\n> \n>  {% for method in interface_main.methods %}\n> -{{proxy_funcs.func_sig(proxy_name, method)}}\n> -{\n> -\tif (isolate_)\n> -\t\treturn {{method.mojom_name}}IPC(\n> -{%- for param in method|method_param_names -%}\n> -\t\t{{param}}{{- \", \" if not loop.last}}\n> -{%- endfor -%}\n> -);\n> -\telse\n> -\t\treturn {{method.mojom_name}}Thread(\n> -{%- for param in method|method_param_names -%}\n> -\t\t{{param}}{{- \", \" if not loop.last}}\n> -{%- endfor -%}\n> -);\n> -}\n> -\n> -{{proxy_funcs.func_sig(proxy_name, method, \"Thread\")}}\n> +{{proxy_funcs.func_sig(proxy_name + \"Threaded\", method)}}\n>  {\n>  {%- if method.mojom_name == \"stop\" %}\n>  \t{{proxy_funcs.stop_thread_body()}}\n> @@ -181,8 +115,58 @@ void {{proxy_name}}::recvMessage(const IPCMessage &data)\n>  );\n>  {%- endif %}\n>  }\n> +{% endfor %}\n> +\n> +{% for method in interface_event.methods %}\n> +{{proxy_funcs.func_sig(proxy_name + \"Threaded\", method, \"Handler\")}}\n> +{\n> +\tASSERT(state_ != ProxyStopped);\n> +\t{{method.mojom_name}}.emit({{method.parameters|params_comma_sep}});\n> +}\n> +{% endfor %}\n> +\n> +/* ========================================================================== */\n> +\n> +{{proxy_name}}Isolated::{{proxy_name}}Isolated(IPAModule *ipam, const GlobalConfiguration &configuration)\n> +\t: {{proxy_name}}(ipam, configuration),\n> +\t  controlSerializer_(ControlSerializer::Role::Proxy), seq_(0)\n> +{\n> +\tLOG(IPAProxy, Debug)\n> +\t\t<< \"initializing {{module_name}} proxy in isolation: loading IPA from \"\n> +\t\t<< ipam->path();\n> +\n> +\tconst std::string proxyWorkerPath = resolvePath(\"{{module_name}}_ipa_proxy\");\n> +\tif (proxyWorkerPath.empty()) {\n> +\t\tLOG(IPAProxy, Error)\n> +\t\t\t<< \"Failed to get proxy worker path\";\n\nThis now holds on a single line.\n\nReviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>\n\n> +\t\treturn;\n> +\t}\n> +\n> +\tauto ipc = std::make_unique<IPCPipeUnixSocket>(ipam->path().c_str(),\n> +\t\t\t\t\t\t       proxyWorkerPath.c_str());\n> +\tif (!ipc->isConnected()) {\n> +\t\tLOG(IPAProxy, Error) << \"Failed to create IPCPipe\";\n> +\t\treturn;\n> +\t}\n> +\n> +\tipc->recv.connect(this, &{{proxy_name}}Isolated::recvMessage);\n> +\n> +\tipc_ = std::move(ipc);\n> +\tvalid_ = true;\n> +}\n> \n> -{{proxy_funcs.func_sig(proxy_name, method, \"IPC\")}}\n> +{{proxy_name}}Isolated::~{{proxy_name}}Isolated()\n> +{\n> +\tif (ipc_) {\n> +\t\tIPCMessage::Header header =\n> +\t\t\t{ static_cast<uint32_t>({{cmd_enum_name}}::Exit), seq_++ };\n> +\t\tIPCMessage msg(header);\n> +\t\tipc_->sendAsync(msg);\n> +\t}\n> +}\n> +\n> +{% for method in interface_main.methods %}\n> +{{proxy_funcs.func_sig(proxy_name + \"Isolated\", method)}}\n>  {\n>  {%- if method.mojom_name == \"configure\" %}\n>  \tcontrolSerializer_.reset();\n> @@ -226,14 +210,25 @@ void {{proxy_name}}::recvMessage(const IPCMessage &data)\n> \n>  {% endfor %}\n> \n> -{% for method in interface_event.methods %}\n> -{{proxy_funcs.func_sig(proxy_name, method, \"Thread\")}}\n> +void {{proxy_name}}Isolated::recvMessage(const IPCMessage &data)\n>  {\n> -\tASSERT(state_ != ProxyStopped);\n> -\t{{method.mojom_name}}.emit({{method.parameters|params_comma_sep}});\n> +\tsize_t dataSize = data.data().size();\n> +\t{{cmd_event_enum_name}} _cmd = static_cast<{{cmd_event_enum_name}}>(data.header().cmd);\n> +\n> +\tswitch (_cmd) {\n> +{%- for method in interface_event.methods %}\n> +\tcase {{cmd_event_enum_name}}::{{method.mojom_name|cap}}: {\n> +\t\t{{method.mojom_name}}Handler(data.data().cbegin(), dataSize, data.fds());\n> +\t\tbreak;\n> +\t}\n> +{%- endfor %}\n> +\tdefault:\n> +\t\tLOG(IPAProxy, Error) << \"Unknown command \" << static_cast<uint32_t>(_cmd);\n> +\t}\n>  }\n> \n> -void {{proxy_name}}::{{method.mojom_name}}IPC(\n> +{% for method in interface_event.methods %}\n> +void {{proxy_name}}Isolated::{{method.mojom_name}}Handler(\n>  \t[[maybe_unused]] std::vector<uint8_t>::const_iterator data,\n>  \t[[maybe_unused]] size_t dataSize,\n>  \t[[maybe_unused]] const std::vector<SharedFD> &fds)\n> diff --git a/utils/codegen/ipc/generators/libcamera_templates/module_ipa_proxy.h.tmpl b/utils/codegen/ipc/generators/libcamera_templates/module_ipa_proxy.h.tmpl\n> index 057c3ab03..ef280ca42 100644\n> --- a/utils/codegen/ipc/generators/libcamera_templates/module_ipa_proxy.h.tmpl\n> +++ b/utils/codegen/ipc/generators/libcamera_templates/module_ipa_proxy.h.tmpl\n> @@ -34,29 +34,32 @@ namespace {{ns}} {\n>  {% endfor %}\n>  {%- endif %}\n> \n> +class {{proxy_name}}Threaded;\n> +class {{proxy_name}}Isolated;\n> +\n>  class {{proxy_name}} : public IPAProxy, public {{interface_name}}, public Object\n>  {\n>  public:\n> -\t{{proxy_name}}(IPAModule *ipam, bool isolate, const GlobalConfiguration &configuration);\n> -\t~{{proxy_name}}();\n> +\tusing Threaded = {{proxy_name}}Threaded;\n> +\tusing Isolated = {{proxy_name}}Isolated;\n> \n> -{% for method in interface_main.methods %}\n> -{{proxy_funcs.func_sig(proxy_name, method, \"\", false, true)|indent(8, true)}};\n> -{% endfor %}\n> +protected:\n> +\tusing IPAProxy::IPAProxy;\n> +};\n> \n> -private:\n> -\tvoid recvMessage(const IPCMessage &data);\n> +class {{proxy_name}}Threaded : public {{proxy_name}}\n> +{\n> +public:\n> +\t{{proxy_name}}Threaded(IPAModule *ipam, const GlobalConfiguration &configuration);\n> +\t~{{proxy_name}}Threaded();\n> \n>  {% for method in interface_main.methods %}\n> -{{proxy_funcs.func_sig(proxy_name, method, \"Thread\", false)|indent(8, true)}};\n> -{{proxy_funcs.func_sig(proxy_name, method, \"IPC\", false)|indent(8, true)}};\n> +{{proxy_funcs.func_sig(proxy_name + \"Threaded\", method, \"\", false, true)|indent(8, true)}};\n>  {% endfor %}\n> +\n> +private:\n>  {% for method in interface_event.methods %}\n> -{{proxy_funcs.func_sig(proxy_name, method, \"Thread\", false)|indent(8, true)}};\n> -\tvoid {{method.mojom_name}}IPC(\n> -\t\tstd::vector<uint8_t>::const_iterator data,\n> -\t\tsize_t dataSize,\n> -\t\tconst std::vector<SharedFD> &fds);\n> +{{proxy_funcs.func_sig(proxy_name + \"Threaded\", method, \"Handler\", false)|indent(8, true)}};\n>  {% endfor %}\n> \n>  \t/* Helper class to invoke async functions in another thread. */\n> @@ -79,12 +82,12 @@ private:\n>  \t\t}\n>  {% for method in interface_main.methods %}\n>  {%- if method|is_async %}\n> -\t\t{{proxy_funcs.func_sig(proxy_name, method, \"\", false)|indent(16)}}\n> +\t\t{{proxy_funcs.func_sig(proxy_name + \"Threaded\", method, \"\", false)|indent(16)}}\n>  \t\t{\n>  \t\t\tipa_->{{method.mojom_name}}({{method.parameters|params_comma_sep}});\n>  \t\t}\n>  {%- elif method.mojom_name == \"start\" %}\n> -\t\t{{proxy_funcs.func_sig(proxy_name, method, \"\", false)|indent(16)}}\n> +\t\t{{proxy_funcs.func_sig(proxy_name + \"Threaded\", method, \"\", false)|indent(16)}}\n>  \t\t{\n>  {%- if method|method_return_value != \"void\" %}\n>  \t\t\treturn ipa_->{{method.mojom_name}}({{method.parameters|params_comma_sep}});\n> @@ -104,8 +107,27 @@ private:\n>  \tThread thread_;\n>  \tThreadProxy proxy_;\n>  \tstd::unique_ptr<{{interface_name}}> ipa_;\n> +};\n> \n> -\tconst bool isolate_;\n> +class {{proxy_name}}Isolated : public {{proxy_name}}\n> +{\n> +public:\n> +\t{{proxy_name}}Isolated(IPAModule *ipam, const GlobalConfiguration &configuration);\n> +\t~{{proxy_name}}Isolated();\n> +\n> +{% for method in interface_main.methods %}\n> +{{proxy_funcs.func_sig(proxy_name + \"Isolated\", method, \"\", false, true)|indent(8, true)}};\n> +{% endfor %}\n> +\n> +private:\n> +\tvoid recvMessage(const IPCMessage &data);\n> +\n> +{% for method in interface_event.methods %}\n> +\tvoid {{method.mojom_name}}Handler(\n> +\t\tstd::vector<uint8_t>::const_iterator data,\n> +\t\tsize_t dataSize,\n> +\t\tconst std::vector<SharedFD> &fds);\n> +{% endfor %}\n> \n>  \tstd::unique_ptr<IPCPipeUnixSocket> ipc_;\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 1653FC324C\n\tfor <parsemail@patchwork.libcamera.org>;\n\tMon, 29 Sep 2025 07:20:03 +0000 (UTC)","from lancelot.ideasonboard.com (localhost [IPv6:::1])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTP id 63F396B5F3;\n\tMon, 29 Sep 2025 09:20:02 +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 598FC6B58E\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tMon, 29 Sep 2025 09:19:59 +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 5FC221121; \n\tMon, 29 Sep 2025 09:18:31 +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=\"r/InvtgL\"; dkim-atps=neutral","DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com;\n\ts=mail; t=1759130311;\n\tbh=U9a6KuNsxT1F8DdPDFnbs5dO8/Tz3fDhEssp61x60es=;\n\th=Date:From:To:Cc:Subject:References:In-Reply-To:From;\n\tb=r/InvtgLe9cozMokx6ZbmwDp938cjcxFhomxJ3k13ShPr5hgd0jzDKOeJzKu9MRWs\n\tOjla4NkSZyZNFU5GKrKwtPPmiVZb1E2POyQOd04vTpppSXGxD2R9XQKL0o6A+JjC9d\n\tYC+kW751OYdt8HKQ45tD+cMzrQZpSLqVEcUSixCo=","Date":"Mon, 29 Sep 2025 10:19:55 +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,\n\tPaul Elder <paul.elder@ideasonboard.com>","Subject":"Re: [RFC PATCH v3] utils: codegen: ipc: Split proxy types","Message-ID":"<20250929071955.GB4646@pendragon.ideasonboard.com>","References":"<20250922100339.1455688-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":"<20250922100339.1455688-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":36016,"web_url":"https://patchwork.libcamera.org/comment/36016/","msgid":"<052e95b0-684a-49c6-864a-253e05dad7cf@ideasonboard.com>","date":"2025-09-29T07:54:49","subject":"Re: [RFC PATCH v3] utils: codegen: ipc: Split proxy types","submitter":{"id":216,"url":"https://patchwork.libcamera.org/api/people/216/","name":"Barnabás Pőcze","email":"barnabas.pocze@ideasonboard.com"},"content":"Hi\n\n\n2025. 09. 29. 9:19 keltezéssel, Laurent Pinchart írta:\n> Hi Barnabás,\n> \n> On Mon, Sep 22, 2025 at 12:03:39PM +0200, Barnabás Pőcze wrote:\n>> Even though there is an abstract class to represent the interface of an IPA,\n>> the threaded and IPC versions are still multiplexed using the same type,\n>> which uses a boolean to actually dispatch to the right function.\n>>\n>> Instead of doing that, split the classes into \"threaded\" and \"isolated\"\n>> variants, and make `IPAManager` choose accordingly.\n>>\n>> Signed-off-by: Barnabás Pőcze <barnabas.pocze@ideasonboard.com>\n>> Reviewed-by: Paul Elder <paul.elder@ideasonboard.com>\n>> ---\n>> changes in v3:\n>>    * rebase\n>>\n>> changes in v2:\n>>    * fix typo\n>>    * rename local -> threaded, remote -> isolated\n>>    * have different log messages in the two variants\n>>\n>> v2: https://patchwork.libcamera.org/patch/24108/\n>> v1: https://patchwork.libcamera.org/patch/24056/\n>> ---\n>>   include/libcamera/internal/ipa_manager.h      |   9 +-\n>>   .../module_ipa_proxy.cpp.tmpl                 | 151 +++++++++---------\n>>   .../module_ipa_proxy.h.tmpl                   |  56 +++++--\n>>   3 files changed, 120 insertions(+), 96 deletions(-)\n>>\n>> diff --git a/include/libcamera/internal/ipa_manager.h b/include/libcamera/internal/ipa_manager.h\n>> index b0b44c74a..f8ce78016 100644\n>> --- a/include/libcamera/internal/ipa_manager.h\n>> +++ b/include/libcamera/internal/ipa_manager.h\n>> @@ -44,7 +44,14 @@ public:\n>>   \t\t\treturn nullptr;\n>>\n>>   \t\tconst GlobalConfiguration &configuration = cm->_d()->configuration();\n>> -\t\tstd::unique_ptr<T> proxy = std::make_unique<T>(m, !self->isSignatureValid(m), configuration);\n>> +\n>> +\t\tauto proxy = [&]() -> std::unique_ptr<T> {\n>> +\t\t\tif (self->isSignatureValid(m))\n>> +\t\t\t\treturn std::make_unique<typename T::Threaded>(m, configuration);\n>> +\t\t\telse\n>> +\t\t\t\treturn std::make_unique<typename T::Isolated>(m, configuration);\n>> +\t\t}();\n>> +\n>>   \t\tif (!proxy->isValid()) {\n>>   \t\t\tLOG(IPAManager, Error) << \"Failed to load proxy\";\n>>   \t\t\treturn nullptr;\n>> diff --git a/utils/codegen/ipc/generators/libcamera_templates/module_ipa_proxy.cpp.tmpl b/utils/codegen/ipc/generators/libcamera_templates/module_ipa_proxy.cpp.tmpl\n>> index 18b4ab5e5..9bf2c58d3 100644\n>> --- a/utils/codegen/ipc/generators/libcamera_templates/module_ipa_proxy.cpp.tmpl\n>> +++ b/utils/codegen/ipc/generators/libcamera_templates/module_ipa_proxy.cpp.tmpl\n>> @@ -45,36 +45,13 @@ namespace {{ns}} {\n>>   {% endfor %}\n>>   {%- endif %}\n>>\n>> -{{proxy_name}}::{{proxy_name}}(IPAModule *ipam, bool isolate, const GlobalConfiguration &configuration)\n>> -\t: IPAProxy(ipam, configuration), isolate_(isolate),\n>> -\t  controlSerializer_(ControlSerializer::Role::Proxy), seq_(0)\n>> +{{proxy_name}}Threaded::{{proxy_name}}Threaded(IPAModule *ipam, const GlobalConfiguration &configuration)\n>> +\t: {{proxy_name}}(ipam, configuration)\n>>   {\n>>   \tLOG(IPAProxy, Debug)\n>> -\t\t<< \"initializing {{module_name}} proxy: loading IPA from \"\n>> +\t\t<< \"initializing {{module_name}} proxy in thread: loading IPA from \"\n>>   \t\t<< ipam->path();\n>>\n>> -\tif (isolate_) {\n>> -\t\tconst std::string proxyWorkerPath = resolvePath(\"{{module_name}}_ipa_proxy\");\n>> -\t\tif (proxyWorkerPath.empty()) {\n>> -\t\t\tLOG(IPAProxy, Error)\n>> -\t\t\t\t<< \"Failed to get proxy worker path\";\n>> -\t\t\treturn;\n>> -\t\t}\n>> -\n>> -\t\tauto ipc = std::make_unique<IPCPipeUnixSocket>(ipam->path().c_str(),\n>> -\t\t\t\t\t\t\t       proxyWorkerPath.c_str());\n>> -\t\tif (!ipc->isConnected()) {\n>> -\t\t\tLOG(IPAProxy, Error) << \"Failed to create IPCPipe\";\n>> -\t\t\treturn;\n>> -\t\t}\n>> -\n>> -\t\tipc->recv.connect(this, &{{proxy_name}}::recvMessage);\n>> -\n>> -\t\tipc_ = std::move(ipc);\n>> -\t\tvalid_ = true;\n>> -\t\treturn;\n>> -\t}\n>> -\n>>   \tif (!ipam->load())\n>>   \t\treturn;\n>>\n>> @@ -89,59 +66,16 @@ namespace {{ns}} {\n>>   \tproxy_.setIPA(ipa_.get());\n>>\n>>   {% for method in interface_event.methods %}\n>> -\tipa_->{{method.mojom_name}}.connect(this, &{{proxy_name}}::{{method.mojom_name}}Thread);\n>> +\tipa_->{{method.mojom_name}}.connect(this, &{{proxy_name}}Threaded::{{method.mojom_name}}Handler);\n>>   {%- endfor %}\n>>\n>>   \tvalid_ = true;\n>>   }\n>>\n>> -{{proxy_name}}::~{{proxy_name}}()\n>> -{\n>> -\tif (ipc_) {\n>> -\t\tIPCMessage::Header header =\n>> -\t\t\t{ static_cast<uint32_t>({{cmd_enum_name}}::Exit), seq_++ };\n>> -\t\tIPCMessage msg(header);\n>> -\t\tipc_->sendAsync(msg);\n>> -\t}\n>> -}\n>> -\n>> -{% if interface_event.methods|length > 0 %}\n>> -void {{proxy_name}}::recvMessage(const IPCMessage &data)\n>> -{\n>> -\tsize_t dataSize = data.data().size();\n>> -\t{{cmd_event_enum_name}} _cmd = static_cast<{{cmd_event_enum_name}}>(data.header().cmd);\n>> -\n>> -\tswitch (_cmd) {\n>> -{%- for method in interface_event.methods %}\n>> -\tcase {{cmd_event_enum_name}}::{{method.mojom_name|cap}}: {\n>> -\t\t{{method.mojom_name}}IPC(data.data().cbegin(), dataSize, data.fds());\n>> -\t\tbreak;\n>> -\t}\n>> -{%- endfor %}\n>> -\tdefault:\n>> -\t\tLOG(IPAProxy, Error) << \"Unknown command \" << static_cast<uint32_t>(_cmd);\n>> -\t}\n>> -}\n>> -{%- endif %}\n>> +{{proxy_name}}Threaded::~{{proxy_name}}Threaded() = default;\n> \n> Can't you omit the destructor from the class definition and drop this\n> line, to let the compiler generate the default destructor ?\n\nYes, it could be omitted. The only difference is that it would be an inline\ndestructor, generated in the translation unit(s) where it is used. Should I\nchange it?\n\n\n> \n>>\n>>   {% for method in interface_main.methods %}\n>> -{{proxy_funcs.func_sig(proxy_name, method)}}\n>> -{\n>> -\tif (isolate_)\n>> -\t\treturn {{method.mojom_name}}IPC(\n>> -{%- for param in method|method_param_names -%}\n>> -\t\t{{param}}{{- \", \" if not loop.last}}\n>> -{%- endfor -%}\n>> -);\n>> -\telse\n>> -\t\treturn {{method.mojom_name}}Thread(\n>> -{%- for param in method|method_param_names -%}\n>> -\t\t{{param}}{{- \", \" if not loop.last}}\n>> -{%- endfor -%}\n>> -);\n>> -}\n>> -\n>> -{{proxy_funcs.func_sig(proxy_name, method, \"Thread\")}}\n>> +{{proxy_funcs.func_sig(proxy_name + \"Threaded\", method)}}\n>>   {\n>>   {%- if method.mojom_name == \"stop\" %}\n>>   \t{{proxy_funcs.stop_thread_body()}}\n>> @@ -181,8 +115,58 @@ void {{proxy_name}}::recvMessage(const IPCMessage &data)\n>>   );\n>>   {%- endif %}\n>>   }\n>> +{% endfor %}\n>> +\n>> +{% for method in interface_event.methods %}\n>> +{{proxy_funcs.func_sig(proxy_name + \"Threaded\", method, \"Handler\")}}\n>> +{\n>> +\tASSERT(state_ != ProxyStopped);\n>> +\t{{method.mojom_name}}.emit({{method.parameters|params_comma_sep}});\n>> +}\n>> +{% endfor %}\n>> +\n>> +/* ========================================================================== */\n>> +\n>> +{{proxy_name}}Isolated::{{proxy_name}}Isolated(IPAModule *ipam, const GlobalConfiguration &configuration)\n>> +\t: {{proxy_name}}(ipam, configuration),\n>> +\t  controlSerializer_(ControlSerializer::Role::Proxy), seq_(0)\n>> +{\n>> +\tLOG(IPAProxy, Debug)\n>> +\t\t<< \"initializing {{module_name}} proxy in isolation: loading IPA from \"\n>> +\t\t<< ipam->path();\n>> +\n>> +\tconst std::string proxyWorkerPath = resolvePath(\"{{module_name}}_ipa_proxy\");\n>> +\tif (proxyWorkerPath.empty()) {\n>> +\t\tLOG(IPAProxy, Error)\n>> +\t\t\t<< \"Failed to get proxy worker path\";\n> \n> This now holds on a single line.\n\nFixed.\n\n\nRegards,\nBarnabás Pőcze\n\n\n> \n> Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>\n> \n>> +\t\treturn;\n>> +\t}\n>> +\n>> +\tauto ipc = std::make_unique<IPCPipeUnixSocket>(ipam->path().c_str(),\n>> +\t\t\t\t\t\t       proxyWorkerPath.c_str());\n>> +\tif (!ipc->isConnected()) {\n>> +\t\tLOG(IPAProxy, Error) << \"Failed to create IPCPipe\";\n>> +\t\treturn;\n>> +\t}\n>> +\n>> +\tipc->recv.connect(this, &{{proxy_name}}Isolated::recvMessage);\n>> +\n>> +\tipc_ = std::move(ipc);\n>> +\tvalid_ = true;\n>> +}\n>>\n>> -{{proxy_funcs.func_sig(proxy_name, method, \"IPC\")}}\n>> +{{proxy_name}}Isolated::~{{proxy_name}}Isolated()\n>> +{\n>> +\tif (ipc_) {\n>> +\t\tIPCMessage::Header header =\n>> +\t\t\t{ static_cast<uint32_t>({{cmd_enum_name}}::Exit), seq_++ };\n>> +\t\tIPCMessage msg(header);\n>> +\t\tipc_->sendAsync(msg);\n>> +\t}\n>> +}\n>> +\n>> +{% for method in interface_main.methods %}\n>> +{{proxy_funcs.func_sig(proxy_name + \"Isolated\", method)}}\n>>   {\n>>   {%- if method.mojom_name == \"configure\" %}\n>>   \tcontrolSerializer_.reset();\n>> @@ -226,14 +210,25 @@ void {{proxy_name}}::recvMessage(const IPCMessage &data)\n>>\n>>   {% endfor %}\n>>\n>> -{% for method in interface_event.methods %}\n>> -{{proxy_funcs.func_sig(proxy_name, method, \"Thread\")}}\n>> +void {{proxy_name}}Isolated::recvMessage(const IPCMessage &data)\n>>   {\n>> -\tASSERT(state_ != ProxyStopped);\n>> -\t{{method.mojom_name}}.emit({{method.parameters|params_comma_sep}});\n>> +\tsize_t dataSize = data.data().size();\n>> +\t{{cmd_event_enum_name}} _cmd = static_cast<{{cmd_event_enum_name}}>(data.header().cmd);\n>> +\n>> +\tswitch (_cmd) {\n>> +{%- for method in interface_event.methods %}\n>> +\tcase {{cmd_event_enum_name}}::{{method.mojom_name|cap}}: {\n>> +\t\t{{method.mojom_name}}Handler(data.data().cbegin(), dataSize, data.fds());\n>> +\t\tbreak;\n>> +\t}\n>> +{%- endfor %}\n>> +\tdefault:\n>> +\t\tLOG(IPAProxy, Error) << \"Unknown command \" << static_cast<uint32_t>(_cmd);\n>> +\t}\n>>   }\n>>\n>> -void {{proxy_name}}::{{method.mojom_name}}IPC(\n>> +{% for method in interface_event.methods %}\n>> +void {{proxy_name}}Isolated::{{method.mojom_name}}Handler(\n>>   \t[[maybe_unused]] std::vector<uint8_t>::const_iterator data,\n>>   \t[[maybe_unused]] size_t dataSize,\n>>   \t[[maybe_unused]] const std::vector<SharedFD> &fds)\n>> diff --git a/utils/codegen/ipc/generators/libcamera_templates/module_ipa_proxy.h.tmpl b/utils/codegen/ipc/generators/libcamera_templates/module_ipa_proxy.h.tmpl\n>> index 057c3ab03..ef280ca42 100644\n>> --- a/utils/codegen/ipc/generators/libcamera_templates/module_ipa_proxy.h.tmpl\n>> +++ b/utils/codegen/ipc/generators/libcamera_templates/module_ipa_proxy.h.tmpl\n>> @@ -34,29 +34,32 @@ namespace {{ns}} {\n>>   {% endfor %}\n>>   {%- endif %}\n>>\n>> +class {{proxy_name}}Threaded;\n>> +class {{proxy_name}}Isolated;\n>> +\n>>   class {{proxy_name}} : public IPAProxy, public {{interface_name}}, public Object\n>>   {\n>>   public:\n>> -\t{{proxy_name}}(IPAModule *ipam, bool isolate, const GlobalConfiguration &configuration);\n>> -\t~{{proxy_name}}();\n>> +\tusing Threaded = {{proxy_name}}Threaded;\n>> +\tusing Isolated = {{proxy_name}}Isolated;\n>>\n>> -{% for method in interface_main.methods %}\n>> -{{proxy_funcs.func_sig(proxy_name, method, \"\", false, true)|indent(8, true)}};\n>> -{% endfor %}\n>> +protected:\n>> +\tusing IPAProxy::IPAProxy;\n>> +};\n>>\n>> -private:\n>> -\tvoid recvMessage(const IPCMessage &data);\n>> +class {{proxy_name}}Threaded : public {{proxy_name}}\n>> +{\n>> +public:\n>> +\t{{proxy_name}}Threaded(IPAModule *ipam, const GlobalConfiguration &configuration);\n>> +\t~{{proxy_name}}Threaded();\n>>\n>>   {% for method in interface_main.methods %}\n>> -{{proxy_funcs.func_sig(proxy_name, method, \"Thread\", false)|indent(8, true)}};\n>> -{{proxy_funcs.func_sig(proxy_name, method, \"IPC\", false)|indent(8, true)}};\n>> +{{proxy_funcs.func_sig(proxy_name + \"Threaded\", method, \"\", false, true)|indent(8, true)}};\n>>   {% endfor %}\n>> +\n>> +private:\n>>   {% for method in interface_event.methods %}\n>> -{{proxy_funcs.func_sig(proxy_name, method, \"Thread\", false)|indent(8, true)}};\n>> -\tvoid {{method.mojom_name}}IPC(\n>> -\t\tstd::vector<uint8_t>::const_iterator data,\n>> -\t\tsize_t dataSize,\n>> -\t\tconst std::vector<SharedFD> &fds);\n>> +{{proxy_funcs.func_sig(proxy_name + \"Threaded\", method, \"Handler\", false)|indent(8, true)}};\n>>   {% endfor %}\n>>\n>>   \t/* Helper class to invoke async functions in another thread. */\n>> @@ -79,12 +82,12 @@ private:\n>>   \t\t}\n>>   {% for method in interface_main.methods %}\n>>   {%- if method|is_async %}\n>> -\t\t{{proxy_funcs.func_sig(proxy_name, method, \"\", false)|indent(16)}}\n>> +\t\t{{proxy_funcs.func_sig(proxy_name + \"Threaded\", method, \"\", false)|indent(16)}}\n>>   \t\t{\n>>   \t\t\tipa_->{{method.mojom_name}}({{method.parameters|params_comma_sep}});\n>>   \t\t}\n>>   {%- elif method.mojom_name == \"start\" %}\n>> -\t\t{{proxy_funcs.func_sig(proxy_name, method, \"\", false)|indent(16)}}\n>> +\t\t{{proxy_funcs.func_sig(proxy_name + \"Threaded\", method, \"\", false)|indent(16)}}\n>>   \t\t{\n>>   {%- if method|method_return_value != \"void\" %}\n>>   \t\t\treturn ipa_->{{method.mojom_name}}({{method.parameters|params_comma_sep}});\n>> @@ -104,8 +107,27 @@ private:\n>>   \tThread thread_;\n>>   \tThreadProxy proxy_;\n>>   \tstd::unique_ptr<{{interface_name}}> ipa_;\n>> +};\n>>\n>> -\tconst bool isolate_;\n>> +class {{proxy_name}}Isolated : public {{proxy_name}}\n>> +{\n>> +public:\n>> +\t{{proxy_name}}Isolated(IPAModule *ipam, const GlobalConfiguration &configuration);\n>> +\t~{{proxy_name}}Isolated();\n>> +\n>> +{% for method in interface_main.methods %}\n>> +{{proxy_funcs.func_sig(proxy_name + \"Isolated\", method, \"\", false, true)|indent(8, true)}};\n>> +{% endfor %}\n>> +\n>> +private:\n>> +\tvoid recvMessage(const IPCMessage &data);\n>> +\n>> +{% for method in interface_event.methods %}\n>> +\tvoid {{method.mojom_name}}Handler(\n>> +\t\tstd::vector<uint8_t>::const_iterator data,\n>> +\t\tsize_t dataSize,\n>> +\t\tconst std::vector<SharedFD> &fds);\n>> +{% endfor %}\n>>\n>>   \tstd::unique_ptr<IPCPipeUnixSocket> ipc_;\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 10673C324C\n\tfor <parsemail@patchwork.libcamera.org>;\n\tMon, 29 Sep 2025 07:54:55 +0000 (UTC)","from lancelot.ideasonboard.com (localhost [IPv6:::1])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTP id 33C7B6B5F3;\n\tMon, 29 Sep 2025 09:54:54 +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 4C9EB6B58E\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tMon, 29 Sep 2025 09:54:53 +0200 (CEST)","from [192.168.33.13] (185.221.142.146.nat.pool.zt.hu\n\t[185.221.142.146])\n\tby perceval.ideasonboard.com (Postfix) with ESMTPSA id 8F30F1121;\n\tMon, 29 Sep 2025 09:53:25 +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=\"VCRwKrY6\"; dkim-atps=neutral","DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com;\n\ts=mail; t=1759132405;\n\tbh=gfnbJ0E80Efmb5MoD1fmiNSjINpaMIB1HrEUTfeelII=;\n\th=Date:Subject:To:Cc:References:From:In-Reply-To:From;\n\tb=VCRwKrY6fZqVF44scQqOobU8rIj0lvEleRo/arwOoHafxfy69YQdq6SDbJv0fEopS\n\tPBrDNWrhILLeDIzEZhquwy51wIJ31rXqzQkk4rzJCUcbXUlevR06FYSrkPM3V6bhGY\n\tVRSoVSJUTKdoElQ7LS1jZ5gPTWxu+RF9N31dyVuY=","Message-ID":"<052e95b0-684a-49c6-864a-253e05dad7cf@ideasonboard.com>","Date":"Mon, 29 Sep 2025 09:54:49 +0200","MIME-Version":"1.0","User-Agent":"Mozilla Thunderbird","Subject":"Re: [RFC PATCH v3] utils: codegen: ipc: Split proxy types","To":"Laurent Pinchart <laurent.pinchart@ideasonboard.com>","Cc":"libcamera-devel@lists.libcamera.org,\n\tPaul Elder <paul.elder@ideasonboard.com>","References":"<20250922100339.1455688-1-barnabas.pocze@ideasonboard.com>\n\t<20250929071955.GB4646@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":"<20250929071955.GB4646@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":36031,"web_url":"https://patchwork.libcamera.org/comment/36031/","msgid":"<20250929112348.GC4646@pendragon.ideasonboard.com>","date":"2025-09-29T11:23:48","subject":"Re: [RFC PATCH v3] utils: codegen: ipc: Split proxy types","submitter":{"id":2,"url":"https://patchwork.libcamera.org/api/people/2/","name":"Laurent Pinchart","email":"laurent.pinchart@ideasonboard.com"},"content":"On Mon, Sep 29, 2025 at 09:54:49AM +0200, Barnabás Pőcze wrote:\n> 2025. 09. 29. 9:19 keltezéssel, Laurent Pinchart írta:\n> > On Mon, Sep 22, 2025 at 12:03:39PM +0200, Barnabás Pőcze wrote:\n> >> Even though there is an abstract class to represent the interface of an IPA,\n> >> the threaded and IPC versions are still multiplexed using the same type,\n> >> which uses a boolean to actually dispatch to the right function.\n> >>\n> >> Instead of doing that, split the classes into \"threaded\" and \"isolated\"\n> >> variants, and make `IPAManager` choose accordingly.\n> >>\n> >> Signed-off-by: Barnabás Pőcze <barnabas.pocze@ideasonboard.com>\n> >> Reviewed-by: Paul Elder <paul.elder@ideasonboard.com>\n> >> ---\n> >> changes in v3:\n> >>    * rebase\n> >>\n> >> changes in v2:\n> >>    * fix typo\n> >>    * rename local -> threaded, remote -> isolated\n> >>    * have different log messages in the two variants\n> >>\n> >> v2: https://patchwork.libcamera.org/patch/24108/\n> >> v1: https://patchwork.libcamera.org/patch/24056/\n> >> ---\n> >>   include/libcamera/internal/ipa_manager.h      |   9 +-\n> >>   .../module_ipa_proxy.cpp.tmpl                 | 151 +++++++++---------\n> >>   .../module_ipa_proxy.h.tmpl                   |  56 +++++--\n> >>   3 files changed, 120 insertions(+), 96 deletions(-)\n> >>\n> >> diff --git a/include/libcamera/internal/ipa_manager.h b/include/libcamera/internal/ipa_manager.h\n> >> index b0b44c74a..f8ce78016 100644\n> >> --- a/include/libcamera/internal/ipa_manager.h\n> >> +++ b/include/libcamera/internal/ipa_manager.h\n> >> @@ -44,7 +44,14 @@ public:\n> >>   \t\t\treturn nullptr;\n> >>\n> >>   \t\tconst GlobalConfiguration &configuration = cm->_d()->configuration();\n> >> -\t\tstd::unique_ptr<T> proxy = std::make_unique<T>(m, !self->isSignatureValid(m), configuration);\n> >> +\n> >> +\t\tauto proxy = [&]() -> std::unique_ptr<T> {\n> >> +\t\t\tif (self->isSignatureValid(m))\n> >> +\t\t\t\treturn std::make_unique<typename T::Threaded>(m, configuration);\n> >> +\t\t\telse\n> >> +\t\t\t\treturn std::make_unique<typename T::Isolated>(m, configuration);\n> >> +\t\t}();\n> >> +\n> >>   \t\tif (!proxy->isValid()) {\n> >>   \t\t\tLOG(IPAManager, Error) << \"Failed to load proxy\";\n> >>   \t\t\treturn nullptr;\n> >> diff --git a/utils/codegen/ipc/generators/libcamera_templates/module_ipa_proxy.cpp.tmpl b/utils/codegen/ipc/generators/libcamera_templates/module_ipa_proxy.cpp.tmpl\n> >> index 18b4ab5e5..9bf2c58d3 100644\n> >> --- a/utils/codegen/ipc/generators/libcamera_templates/module_ipa_proxy.cpp.tmpl\n> >> +++ b/utils/codegen/ipc/generators/libcamera_templates/module_ipa_proxy.cpp.tmpl\n> >> @@ -45,36 +45,13 @@ namespace {{ns}} {\n> >>   {% endfor %}\n> >>   {%- endif %}\n> >>\n> >> -{{proxy_name}}::{{proxy_name}}(IPAModule *ipam, bool isolate, const GlobalConfiguration &configuration)\n> >> -\t: IPAProxy(ipam, configuration), isolate_(isolate),\n> >> -\t  controlSerializer_(ControlSerializer::Role::Proxy), seq_(0)\n> >> +{{proxy_name}}Threaded::{{proxy_name}}Threaded(IPAModule *ipam, const GlobalConfiguration &configuration)\n> >> +\t: {{proxy_name}}(ipam, configuration)\n> >>   {\n> >>   \tLOG(IPAProxy, Debug)\n> >> -\t\t<< \"initializing {{module_name}} proxy: loading IPA from \"\n> >> +\t\t<< \"initializing {{module_name}} proxy in thread: loading IPA from \"\n> >>   \t\t<< ipam->path();\n> >>\n> >> -\tif (isolate_) {\n> >> -\t\tconst std::string proxyWorkerPath = resolvePath(\"{{module_name}}_ipa_proxy\");\n> >> -\t\tif (proxyWorkerPath.empty()) {\n> >> -\t\t\tLOG(IPAProxy, Error)\n> >> -\t\t\t\t<< \"Failed to get proxy worker path\";\n> >> -\t\t\treturn;\n> >> -\t\t}\n> >> -\n> >> -\t\tauto ipc = std::make_unique<IPCPipeUnixSocket>(ipam->path().c_str(),\n> >> -\t\t\t\t\t\t\t       proxyWorkerPath.c_str());\n> >> -\t\tif (!ipc->isConnected()) {\n> >> -\t\t\tLOG(IPAProxy, Error) << \"Failed to create IPCPipe\";\n> >> -\t\t\treturn;\n> >> -\t\t}\n> >> -\n> >> -\t\tipc->recv.connect(this, &{{proxy_name}}::recvMessage);\n> >> -\n> >> -\t\tipc_ = std::move(ipc);\n> >> -\t\tvalid_ = true;\n> >> -\t\treturn;\n> >> -\t}\n> >> -\n> >>   \tif (!ipam->load())\n> >>   \t\treturn;\n> >>\n> >> @@ -89,59 +66,16 @@ namespace {{ns}} {\n> >>   \tproxy_.setIPA(ipa_.get());\n> >>\n> >>   {% for method in interface_event.methods %}\n> >> -\tipa_->{{method.mojom_name}}.connect(this, &{{proxy_name}}::{{method.mojom_name}}Thread);\n> >> +\tipa_->{{method.mojom_name}}.connect(this, &{{proxy_name}}Threaded::{{method.mojom_name}}Handler);\n> >>   {%- endfor %}\n> >>\n> >>   \tvalid_ = true;\n> >>   }\n> >>\n> >> -{{proxy_name}}::~{{proxy_name}}()\n> >> -{\n> >> -\tif (ipc_) {\n> >> -\t\tIPCMessage::Header header =\n> >> -\t\t\t{ static_cast<uint32_t>({{cmd_enum_name}}::Exit), seq_++ };\n> >> -\t\tIPCMessage msg(header);\n> >> -\t\tipc_->sendAsync(msg);\n> >> -\t}\n> >> -}\n> >> -\n> >> -{% if interface_event.methods|length > 0 %}\n> >> -void {{proxy_name}}::recvMessage(const IPCMessage &data)\n> >> -{\n> >> -\tsize_t dataSize = data.data().size();\n> >> -\t{{cmd_event_enum_name}} _cmd = static_cast<{{cmd_event_enum_name}}>(data.header().cmd);\n> >> -\n> >> -\tswitch (_cmd) {\n> >> -{%- for method in interface_event.methods %}\n> >> -\tcase {{cmd_event_enum_name}}::{{method.mojom_name|cap}}: {\n> >> -\t\t{{method.mojom_name}}IPC(data.data().cbegin(), dataSize, data.fds());\n> >> -\t\tbreak;\n> >> -\t}\n> >> -{%- endfor %}\n> >> -\tdefault:\n> >> -\t\tLOG(IPAProxy, Error) << \"Unknown command \" << static_cast<uint32_t>(_cmd);\n> >> -\t}\n> >> -}\n> >> -{%- endif %}\n> >> +{{proxy_name}}Threaded::~{{proxy_name}}Threaded() = default;\n> > \n> > Can't you omit the destructor from the class definition and drop this\n> > line, to let the compiler generate the default destructor ?\n> \n> Yes, it could be omitted. The only difference is that it would be an inline\n> destructor, generated in the translation unit(s) where it is used. Should I\n> change it?\n\nAh right. You can keep it here if you think it's better. Up to you.\n\n> >>\n> >>   {% for method in interface_main.methods %}\n> >> -{{proxy_funcs.func_sig(proxy_name, method)}}\n> >> -{\n> >> -\tif (isolate_)\n> >> -\t\treturn {{method.mojom_name}}IPC(\n> >> -{%- for param in method|method_param_names -%}\n> >> -\t\t{{param}}{{- \", \" if not loop.last}}\n> >> -{%- endfor -%}\n> >> -);\n> >> -\telse\n> >> -\t\treturn {{method.mojom_name}}Thread(\n> >> -{%- for param in method|method_param_names -%}\n> >> -\t\t{{param}}{{- \", \" if not loop.last}}\n> >> -{%- endfor -%}\n> >> -);\n> >> -}\n> >> -\n> >> -{{proxy_funcs.func_sig(proxy_name, method, \"Thread\")}}\n> >> +{{proxy_funcs.func_sig(proxy_name + \"Threaded\", method)}}\n> >>   {\n> >>   {%- if method.mojom_name == \"stop\" %}\n> >>   \t{{proxy_funcs.stop_thread_body()}}\n> >> @@ -181,8 +115,58 @@ void {{proxy_name}}::recvMessage(const IPCMessage &data)\n> >>   );\n> >>   {%- endif %}\n> >>   }\n> >> +{% endfor %}\n> >> +\n> >> +{% for method in interface_event.methods %}\n> >> +{{proxy_funcs.func_sig(proxy_name + \"Threaded\", method, \"Handler\")}}\n> >> +{\n> >> +\tASSERT(state_ != ProxyStopped);\n> >> +\t{{method.mojom_name}}.emit({{method.parameters|params_comma_sep}});\n> >> +}\n> >> +{% endfor %}\n> >> +\n> >> +/* ========================================================================== */\n> >> +\n> >> +{{proxy_name}}Isolated::{{proxy_name}}Isolated(IPAModule *ipam, const GlobalConfiguration &configuration)\n> >> +\t: {{proxy_name}}(ipam, configuration),\n> >> +\t  controlSerializer_(ControlSerializer::Role::Proxy), seq_(0)\n> >> +{\n> >> +\tLOG(IPAProxy, Debug)\n> >> +\t\t<< \"initializing {{module_name}} proxy in isolation: loading IPA from \"\n> >> +\t\t<< ipam->path();\n> >> +\n> >> +\tconst std::string proxyWorkerPath = resolvePath(\"{{module_name}}_ipa_proxy\");\n> >> +\tif (proxyWorkerPath.empty()) {\n> >> +\t\tLOG(IPAProxy, Error)\n> >> +\t\t\t<< \"Failed to get proxy worker path\";\n> > \n> > This now holds on a single line.\n> \n> Fixed.\n> \n> > Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>\n> > \n> >> +\t\treturn;\n> >> +\t}\n> >> +\n> >> +\tauto ipc = std::make_unique<IPCPipeUnixSocket>(ipam->path().c_str(),\n> >> +\t\t\t\t\t\t       proxyWorkerPath.c_str());\n> >> +\tif (!ipc->isConnected()) {\n> >> +\t\tLOG(IPAProxy, Error) << \"Failed to create IPCPipe\";\n> >> +\t\treturn;\n> >> +\t}\n> >> +\n> >> +\tipc->recv.connect(this, &{{proxy_name}}Isolated::recvMessage);\n> >> +\n> >> +\tipc_ = std::move(ipc);\n> >> +\tvalid_ = true;\n> >> +}\n> >>\n> >> -{{proxy_funcs.func_sig(proxy_name, method, \"IPC\")}}\n> >> +{{proxy_name}}Isolated::~{{proxy_name}}Isolated()\n> >> +{\n> >> +\tif (ipc_) {\n> >> +\t\tIPCMessage::Header header =\n> >> +\t\t\t{ static_cast<uint32_t>({{cmd_enum_name}}::Exit), seq_++ };\n> >> +\t\tIPCMessage msg(header);\n> >> +\t\tipc_->sendAsync(msg);\n> >> +\t}\n> >> +}\n> >> +\n> >> +{% for method in interface_main.methods %}\n> >> +{{proxy_funcs.func_sig(proxy_name + \"Isolated\", method)}}\n> >>   {\n> >>   {%- if method.mojom_name == \"configure\" %}\n> >>   \tcontrolSerializer_.reset();\n> >> @@ -226,14 +210,25 @@ void {{proxy_name}}::recvMessage(const IPCMessage &data)\n> >>\n> >>   {% endfor %}\n> >>\n> >> -{% for method in interface_event.methods %}\n> >> -{{proxy_funcs.func_sig(proxy_name, method, \"Thread\")}}\n> >> +void {{proxy_name}}Isolated::recvMessage(const IPCMessage &data)\n> >>   {\n> >> -\tASSERT(state_ != ProxyStopped);\n> >> -\t{{method.mojom_name}}.emit({{method.parameters|params_comma_sep}});\n> >> +\tsize_t dataSize = data.data().size();\n> >> +\t{{cmd_event_enum_name}} _cmd = static_cast<{{cmd_event_enum_name}}>(data.header().cmd);\n> >> +\n> >> +\tswitch (_cmd) {\n> >> +{%- for method in interface_event.methods %}\n> >> +\tcase {{cmd_event_enum_name}}::{{method.mojom_name|cap}}: {\n> >> +\t\t{{method.mojom_name}}Handler(data.data().cbegin(), dataSize, data.fds());\n> >> +\t\tbreak;\n> >> +\t}\n> >> +{%- endfor %}\n> >> +\tdefault:\n> >> +\t\tLOG(IPAProxy, Error) << \"Unknown command \" << static_cast<uint32_t>(_cmd);\n> >> +\t}\n> >>   }\n> >>\n> >> -void {{proxy_name}}::{{method.mojom_name}}IPC(\n> >> +{% for method in interface_event.methods %}\n> >> +void {{proxy_name}}Isolated::{{method.mojom_name}}Handler(\n> >>   \t[[maybe_unused]] std::vector<uint8_t>::const_iterator data,\n> >>   \t[[maybe_unused]] size_t dataSize,\n> >>   \t[[maybe_unused]] const std::vector<SharedFD> &fds)\n> >> diff --git a/utils/codegen/ipc/generators/libcamera_templates/module_ipa_proxy.h.tmpl b/utils/codegen/ipc/generators/libcamera_templates/module_ipa_proxy.h.tmpl\n> >> index 057c3ab03..ef280ca42 100644\n> >> --- a/utils/codegen/ipc/generators/libcamera_templates/module_ipa_proxy.h.tmpl\n> >> +++ b/utils/codegen/ipc/generators/libcamera_templates/module_ipa_proxy.h.tmpl\n> >> @@ -34,29 +34,32 @@ namespace {{ns}} {\n> >>   {% endfor %}\n> >>   {%- endif %}\n> >>\n> >> +class {{proxy_name}}Threaded;\n> >> +class {{proxy_name}}Isolated;\n> >> +\n> >>   class {{proxy_name}} : public IPAProxy, public {{interface_name}}, public Object\n> >>   {\n> >>   public:\n> >> -\t{{proxy_name}}(IPAModule *ipam, bool isolate, const GlobalConfiguration &configuration);\n> >> -\t~{{proxy_name}}();\n> >> +\tusing Threaded = {{proxy_name}}Threaded;\n> >> +\tusing Isolated = {{proxy_name}}Isolated;\n> >>\n> >> -{% for method in interface_main.methods %}\n> >> -{{proxy_funcs.func_sig(proxy_name, method, \"\", false, true)|indent(8, true)}};\n> >> -{% endfor %}\n> >> +protected:\n> >> +\tusing IPAProxy::IPAProxy;\n> >> +};\n> >>\n> >> -private:\n> >> -\tvoid recvMessage(const IPCMessage &data);\n> >> +class {{proxy_name}}Threaded : public {{proxy_name}}\n> >> +{\n> >> +public:\n> >> +\t{{proxy_name}}Threaded(IPAModule *ipam, const GlobalConfiguration &configuration);\n> >> +\t~{{proxy_name}}Threaded();\n> >>\n> >>   {% for method in interface_main.methods %}\n> >> -{{proxy_funcs.func_sig(proxy_name, method, \"Thread\", false)|indent(8, true)}};\n> >> -{{proxy_funcs.func_sig(proxy_name, method, \"IPC\", false)|indent(8, true)}};\n> >> +{{proxy_funcs.func_sig(proxy_name + \"Threaded\", method, \"\", false, true)|indent(8, true)}};\n> >>   {% endfor %}\n> >> +\n> >> +private:\n> >>   {% for method in interface_event.methods %}\n> >> -{{proxy_funcs.func_sig(proxy_name, method, \"Thread\", false)|indent(8, true)}};\n> >> -\tvoid {{method.mojom_name}}IPC(\n> >> -\t\tstd::vector<uint8_t>::const_iterator data,\n> >> -\t\tsize_t dataSize,\n> >> -\t\tconst std::vector<SharedFD> &fds);\n> >> +{{proxy_funcs.func_sig(proxy_name + \"Threaded\", method, \"Handler\", false)|indent(8, true)}};\n> >>   {% endfor %}\n> >>\n> >>   \t/* Helper class to invoke async functions in another thread. */\n> >> @@ -79,12 +82,12 @@ private:\n> >>   \t\t}\n> >>   {% for method in interface_main.methods %}\n> >>   {%- if method|is_async %}\n> >> -\t\t{{proxy_funcs.func_sig(proxy_name, method, \"\", false)|indent(16)}}\n> >> +\t\t{{proxy_funcs.func_sig(proxy_name + \"Threaded\", method, \"\", false)|indent(16)}}\n> >>   \t\t{\n> >>   \t\t\tipa_->{{method.mojom_name}}({{method.parameters|params_comma_sep}});\n> >>   \t\t}\n> >>   {%- elif method.mojom_name == \"start\" %}\n> >> -\t\t{{proxy_funcs.func_sig(proxy_name, method, \"\", false)|indent(16)}}\n> >> +\t\t{{proxy_funcs.func_sig(proxy_name + \"Threaded\", method, \"\", false)|indent(16)}}\n> >>   \t\t{\n> >>   {%- if method|method_return_value != \"void\" %}\n> >>   \t\t\treturn ipa_->{{method.mojom_name}}({{method.parameters|params_comma_sep}});\n> >> @@ -104,8 +107,27 @@ private:\n> >>   \tThread thread_;\n> >>   \tThreadProxy proxy_;\n> >>   \tstd::unique_ptr<{{interface_name}}> ipa_;\n> >> +};\n> >>\n> >> -\tconst bool isolate_;\n> >> +class {{proxy_name}}Isolated : public {{proxy_name}}\n> >> +{\n> >> +public:\n> >> +\t{{proxy_name}}Isolated(IPAModule *ipam, const GlobalConfiguration &configuration);\n> >> +\t~{{proxy_name}}Isolated();\n> >> +\n> >> +{% for method in interface_main.methods %}\n> >> +{{proxy_funcs.func_sig(proxy_name + \"Isolated\", method, \"\", false, true)|indent(8, true)}};\n> >> +{% endfor %}\n> >> +\n> >> +private:\n> >> +\tvoid recvMessage(const IPCMessage &data);\n> >> +\n> >> +{% for method in interface_event.methods %}\n> >> +\tvoid {{method.mojom_name}}Handler(\n> >> +\t\tstd::vector<uint8_t>::const_iterator data,\n> >> +\t\tsize_t dataSize,\n> >> +\t\tconst std::vector<SharedFD> &fds);\n> >> +{% endfor %}\n> >>\n> >>   \tstd::unique_ptr<IPCPipeUnixSocket> ipc_;\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 CCD1CC328C\n\tfor <parsemail@patchwork.libcamera.org>;\n\tMon, 29 Sep 2025 11:23:54 +0000 (UTC)","from lancelot.ideasonboard.com (localhost [IPv6:::1])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTP id CA6746B5F3;\n\tMon, 29 Sep 2025 13:23:53 +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 12B4E6B599\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tMon, 29 Sep 2025 13:23:52 +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 547DC346;\n\tMon, 29 Sep 2025 13:22:24 +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=\"Yra145Pq\"; dkim-atps=neutral","DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com;\n\ts=mail; t=1759144944;\n\tbh=kulOzSO5etQEXzw/iIz9F0zSadRpnyLJ7H+ngy9FwNg=;\n\th=Date:From:To:Cc:Subject:References:In-Reply-To:From;\n\tb=Yra145PqVSkMu7nC8gn6FtzPAOaqES4UDj3pyDgQoYAcY8FkFofuZdRkHScoYQBDN\n\tBD8ds7bcYiNUL92CSD3dygJlmXBFjzPW69MSC/dV1spHmCpJr6OEJIOlWuAonWLeMt\n\tiLZEwQFqDr1G9uQV/5UOjlCcYUljwF4RC23QISWY=","Date":"Mon, 29 Sep 2025 14:23:48 +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,\n\tPaul Elder <paul.elder@ideasonboard.com>","Subject":"Re: [RFC PATCH v3] utils: codegen: ipc: Split proxy types","Message-ID":"<20250929112348.GC4646@pendragon.ideasonboard.com>","References":"<20250922100339.1455688-1-barnabas.pocze@ideasonboard.com>\n\t<20250929071955.GB4646@pendragon.ideasonboard.com>\n\t<052e95b0-684a-49c6-864a-253e05dad7cf@ideasonboard.com>","MIME-Version":"1.0","Content-Type":"text/plain; charset=utf-8","Content-Disposition":"inline","Content-Transfer-Encoding":"8bit","In-Reply-To":"<052e95b0-684a-49c6-864a-253e05dad7cf@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>"}}]