{"id":24056,"url":"https://patchwork.libcamera.org/api/1.1/patches/24056/?format=json","web_url":"https://patchwork.libcamera.org/patch/24056/","project":{"id":1,"url":"https://patchwork.libcamera.org/api/1.1/projects/1/?format=json","name":"libcamera","link_name":"libcamera","list_id":"libcamera_core","list_email":"libcamera-devel@lists.libcamera.org","web_url":"","scm_url":"","webscm_url":""},"msgid":"<20250804165226.63825-1-barnabas.pocze@ideasonboard.com>","date":"2025-08-04T16:52:26","name":"[RFC,v1] utils: codegen: ipc: Split proxy types","commit_ref":null,"pull_url":null,"state":"superseded","archived":false,"hash":"1d35f79690c457f99a7bfa9209872476608de278","submitter":{"id":216,"url":"https://patchwork.libcamera.org/api/1.1/people/216/?format=json","name":"Barnabás Pőcze","email":"barnabas.pocze@ideasonboard.com"},"delegate":null,"mbox":"https://patchwork.libcamera.org/patch/24056/mbox/","series":[{"id":5356,"url":"https://patchwork.libcamera.org/api/1.1/series/5356/?format=json","web_url":"https://patchwork.libcamera.org/project/libcamera/list/?series=5356","date":"2025-08-04T16:52:26","name":"[RFC,v1] utils: codegen: ipc: Split proxy types","version":1,"mbox":"https://patchwork.libcamera.org/series/5356/mbox/"}],"comments":"https://patchwork.libcamera.org/api/patches/24056/comments/","check":"pending","checks":"https://patchwork.libcamera.org/api/patches/24056/checks/","tags":{},"headers":{"Return-Path":"<libcamera-devel-bounces@lists.libcamera.org>","X-Original-To":"parsemail@patchwork.libcamera.org","Delivered-To":"parsemail@patchwork.libcamera.org","Received":["from lancelot.ideasonboard.com (lancelot.ideasonboard.com\n\t[92.243.16.209])\n\tby patchwork.libcamera.org (Postfix) with ESMTPS id 3A8DCBE086\n\tfor <parsemail@patchwork.libcamera.org>;\n\tMon,  4 Aug 2025 16:52:31 +0000 (UTC)","from lancelot.ideasonboard.com (localhost [IPv6:::1])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTP id D84C869220;\n\tMon,  4 Aug 2025 18:52:30 +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 DDFE769217\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tMon,  4 Aug 2025 18:52:28 +0200 (CEST)","from pb-laptop.local (185.182.215.213.nat.pool.zt.hu\n\t[185.182.215.213])\n\tby perceval.ideasonboard.com (Postfix) with ESMTPSA id B4BABC6D;\n\tMon,  4 Aug 2025 18:51:41 +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=\"DJcMx/QJ\"; dkim-atps=neutral","DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com;\n\ts=mail; t=1754326301;\n\tbh=sbUHwJ4Js68DkKRnjeyalheTvQoaHJ60FTiPfEWksYo=;\n\th=From:To:Subject:Date:From;\n\tb=DJcMx/QJ4vzVRRbWxWz4KP7JEwQyp5FrdRjIAhEmtWyBY8W/iZfMHQzPyOZYBcBGX\n\tMMiHEq9AqaEphHb5CKBlRj5ej9n6XCdn+nSqIvElONOBrOmegI4Tf/QCTjqr/LMgij\n\tmeykkChbBbSRwMFuxlydZat7i9kSH/0JnpaC5Dvc=","From":"=?utf-8?q?Barnab=C3=A1s_P=C5=91cze?= <barnabas.pocze@ideasonboard.com>","To":"libcamera-devel@lists.libcamera.org,\n\tPaul Elder <paul.elder@ideasonboard.com>","Subject":"[RFC PATCH v1] utils: codegen: ipc: Split proxy types","Date":"Mon,  4 Aug 2025 18:52:26 +0200","Message-ID":"<20250804165226.63825-1-barnabas.pocze@ideasonboard.com>","X-Mailer":"git-send-email 2.50.1","MIME-Version":"1.0","Content-Type":"text/plain; charset=UTF-8","Content-Transfer-Encoding":"8bit","X-BeenThere":"libcamera-devel@lists.libcamera.org","X-Mailman-Version":"2.1.29","Precedence":"list","List-Id":"<libcamera-devel.lists.libcamera.org>","List-Unsubscribe":"<https://lists.libcamera.org/options/libcamera-devel>,\n\t<mailto:libcamera-devel-request@lists.libcamera.org?subject=unsubscribe>","List-Archive":"<https://lists.libcamera.org/pipermail/libcamera-devel/>","List-Post":"<mailto:libcamera-devel@lists.libcamera.org>","List-Help":"<mailto:libcamera-devel-request@lists.libcamera.org?subject=help>","List-Subscribe":"<https://lists.libcamera.org/listinfo/libcamera-devel>,\n\t<mailto:libcamera-devel-request@lists.libcamera.org?subject=subscribe>","Errors-To":"libcamera-devel-bounces@lists.libcamera.org","Sender":"\"libcamera-devel\" <libcamera-devel-bounces@lists.libcamera.org>"},"content":"Even though there is an abstract class to represent the interface of an IPA,\nthe threaded and IPC versions are still multiplexed using the same type,\nwhich uses a boolean to actually dispatch to the right function.\n\nInstead of doing that, split the classes into a \"local\" and \"remote\" variant,\nand make `IPAManager` choose accordingly.\n\nSigned-off-by: Barnabás Pőcze <barnabas.pocze@ideasonboard.com>\n---\n include/libcamera/internal/ipa_manager.h      |   8 +-\n .../module_ipa_proxy.cpp.tmpl                 | 149 +++++++++---------\n .../module_ipa_proxy.h.tmpl                   |  56 +++++--\n 3 files changed, 118 insertions(+), 95 deletions(-)","diff":"diff --git a/include/libcamera/internal/ipa_manager.h b/include/libcamera/internal/ipa_manager.h\nindex a0d448cf9..847a69066 100644\n--- a/include/libcamera/internal/ipa_manager.h\n+++ b/include/libcamera/internal/ipa_manager.h\n@@ -42,7 +42,13 @@ public:\n \t\tif (!m)\n \t\t\treturn nullptr;\n \n-\t\tstd::unique_ptr<T> proxy = std::make_unique<T>(m, !self->isSignatureValid(m));\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::Local>(m);\n+\t\t\telse\n+\t\t\t\treturn std::make_unique<typename T::Remote>(m);\n+\t\t} ();\n+\n \t\tif (!proxy->isValid()) {\n \t\t\tLOG(IPAManager, Error) << \"Failed to load proxy\";\n \t\t\treturn nullptr;\ndiff --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\nindex beb646e2d..8792b3b9e 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)\n-\t: IPAProxy(ipam), isolate_(isolate),\n-\t  controlSerializer_(ControlSerializer::Role::Proxy), seq_(0)\n+{{proxy_name}}Local::{{proxy_name}}Local(IPAModule *ipam)\n+\t: {{proxy_name}}(ipam)\n {\n \tLOG(IPAProxy, Debug)\n \t\t<< \"initializing {{module_name}} proxy: 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}}Local::{{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}}Local::~{{proxy_name}}Local() = default;\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 + \"Local\", 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-{{proxy_funcs.func_sig(proxy_name, method, \"IPC\")}}\n+{% for method in interface_event.methods %}\n+{{proxy_funcs.func_sig(proxy_name + \"Local\", 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}}Remote::{{proxy_name}}Remote(IPAModule *ipam)\n+\t: {{proxy_name}}(ipam),\n+\t  controlSerializer_(ControlSerializer::Role::Proxy), seq_(0)\n+{\n+\tLOG(IPAProxy, Debug)\n+\t\t<< \"initializing {{module_name}} proxy: 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+\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}}Remote::recvMessage);\n+\n+\tipc_ = std::move(ipc);\n+\tvalid_ = true;\n+}\n+\n+{{proxy_name}}Remote::~{{proxy_name}}Remote()\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 + \"Remote\", 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}}Remote::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}}Remote::{{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)\ndiff --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\nindex a0312a7c1..0c4ef985b 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}}Local;\n+class {{proxy_name}}Remote;\n+\n class {{proxy_name}} : public IPAProxy, public {{interface_name}}, public Object\n {\n public:\n-\t{{proxy_name}}(IPAModule *ipam, bool isolate);\n-\t~{{proxy_name}}();\n+\tusing Local = {{proxy_name}}Local;\n+\tusing Remote = {{proxy_name}}Remote;\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}}Local : public {{proxy_name}}\n+{\n+public:\n+\t{{proxy_name}}Local(IPAModule *ipam);\n+\t~{{proxy_name}}Local();\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 + \"Local\", 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 + \"Local\", 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 + \"Local\", 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 + \"Local\", 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}}Remote : public {{proxy_name}}\n+{\n+public:\n+\t{{proxy_name}}Remote(IPAModule *ipam);\n+\t~{{proxy_name}}Remote();\n+\n+{% for method in interface_main.methods %}\n+{{proxy_funcs.func_sig(proxy_name + \"Local\", 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","prefixes":["RFC","v1"]}