已合并
【c73cebc75900df125584ff0dbae1ffc1】fix(unidev): 修复 hwproxy 与 hwdiscovery 启动竞态 #298
lishengxiao创建于 7月18日
【c73cebc75900df125584ff0dbae1ffc1】fix(unidev): 修复 hwproxy 与 hwdiscovery 启动竞态 #298
已合并
lishengxiao创建于 7月18日
共 12 个文件变更+176-26
@@ -1,5 +1,8 @@
1# Changelog1# Changelog
2All notable changes to this project will be documented in this file.2All notable changes to this project will be documented in this file.
3+## [1.2.73] - 2026-07-18
4+- 修复 unidev 启动竞态:hwproxy 先于 hwdiscovery 启动并发布就绪状态,应用和 hwproxy 就绪后再执行 CSR 全量发现,避免拓扑对象错误注册到 devmon 服务
5+ 
3## [1.2.72] - 2026-07-176## [1.2.72] - 2026-07-17
4- 修复组件编译报错7- 修复组件编译报错
5 8 
@@ -32,9 +32,9 @@
32 {32 {
33 "api_version": "v1",33 "api_version": "v1",
34 "kind": "Service",34 "kind": "Service",
35- "type": "discovery_service",35+ "type": "hwproxy_service",
36 "meta": {36 "meta": {
37- "name": "bmc.kepler.hwdiscovery"37+ "name": "bmc.kepler.hwproxy"
38 },38 },
39 "enabled": true,39 "enabled": true,
40 "properties": {40 "properties": {
@@ -48,9 +48,9 @@
48 {48 {
49 "api_version": "v1",49 "api_version": "v1",
50 "kind": "Service",50 "kind": "Service",
51- "type": "hwproxy_service",51+ "type": "discovery_service",
52 "meta": {52 "meta": {
53- "name": "bmc.kepler.hwproxy"53+ "name": "bmc.kepler.hwdiscovery"
54 },54 },
55 "enabled": true,55 "enabled": true,
56 "properties": {56 "properties": {
@@ -0,0 +1,22 @@
1+/*
2+ * Copyright (c) 2026 Huawei Technologies Co., Ltd.
3+ * openUBMC is licensed under Mulan PSL v2.
4+ * You can use this software according to the terms and conditions of the Mulan PSL v2.
5+ * You may obtain a copy of Mulan PSL v2 at:
6+ * http://license.coscl.org.cn/MulanPSL2
7+ * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
8+ * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
9+ * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
10+ * See the Mulan PSL v2 for more details.
11+ */
12+ 
13+#pragma once
14+ 
15+namespace devmon::service_readiness {
16+ 
17+void set_application_ready(bool ready);
18+bool is_application_ready();
19+void set_hwproxy_ready(bool ready);
20+bool is_hwproxy_ready();
21+ 
22+} // namespace devmon::service_readiness
@@ -13,6 +13,7 @@ common_sources = [
13 'src/csr_types.cpp',13 'src/csr_types.cpp',
14 'src/object_group.cpp',14 'src/object_group.cpp',
15 'src/object_group_interface.cpp',15 'src/object_group_interface.cpp',
16+ 'src/service_readiness.cpp',
16 'src/device_driver.cpp',17 'src/device_driver.cpp',
17 'src/device/manager.cpp',18 'src/device/manager.cpp',
18 'src/device/csr.cpp',19 'src/device/csr.cpp',
@@ -0,0 +1,45 @@
1+/*
2+ * Copyright (c) 2026 Huawei Technologies Co., Ltd.
3+ * openUBMC is licensed under Mulan PSL v2.
4+ * You can use this software according to the terms and conditions of the Mulan PSL v2.
5+ * You may obtain a copy of Mulan PSL v2 at:
6+ * http://license.coscl.org.cn/MulanPSL2
7+ * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
8+ * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
9+ * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
10+ * See the Mulan PSL v2 for more details.
11+ */
12+ 
13+#include <devmon-common/service_readiness.h>
14+ 
15+#include <atomic>
16+ 
17+namespace devmon::service_readiness {
18+namespace {
19+ 
20+std::atomic<bool> g_application_ready{false};
21+std::atomic<bool> g_hwproxy_ready{false};
22+ 
23+} // namespace
24+ 
25+void set_application_ready(bool ready)
26+{
27+ g_application_ready.store(ready, std::memory_order_release);
28+}
29+ 
30+bool is_application_ready()
31+{
32+ return g_application_ready.load(std::memory_order_acquire);
33+}
34+ 
35+void set_hwproxy_ready(bool ready)
36+{
37+ g_hwproxy_ready.store(ready, std::memory_order_release);
38+}
39+ 
40+bool is_hwproxy_ready()
41+{
42+ return g_hwproxy_ready.load(std::memory_order_acquire);
43+}
44+ 
45+} // namespace devmon::service_readiness
@@ -12,10 +12,9 @@
12#ifndef DEVMON_DISCOVERY_SERVICE_H12#ifndef DEVMON_DISCOVERY_SERVICE_H
13#define DEVMON_DISCOVERY_SERVICE_H13#define DEVMON_DISCOVERY_SERVICE_H
14 14 
15-#include <chrono>15+#include <atomic>
16#include <mc/app/service.h>16#include <mc/app/service.h>
17#include <mc/future.h>17#include <mc/future.h>
18-#include <mc/log.h>
19#include <mc/reflect.h>18#include <mc/reflect.h>
20#include <optional>19#include <optional>
21 20 
@@ -42,19 +41,7 @@ public:
42 explicit discovery_service(mc::string name) : mc::app::service(std::move(name))41 explicit discovery_service(mc::string name) : mc::app::service(std::move(name))
43 {}42 {}
44 43 
45- ~discovery_service() override44+ ~discovery_service() override;
46- {
47- if (m_async_task.has_value()) {
48- try {
49- auto status = m_async_task->wait_for(std::chrono::seconds(30));
50- if (status == mc::future_status::timeout) {
51- wlog("discovery_service async task timed out during destructor");
52- }
53- } catch (const std::exception& e) {
54- wlog("exception waiting for async task during destructor: ${error}", ("error", e.what()));
55- }
56- }
57- }
58 45 
59private:46private:
60 bool on_configure() override;47 bool on_configure() override;
@@ -65,6 +52,7 @@ private:
65 hwdiscovery_root_object_ptr m_root_object;52 hwdiscovery_root_object_ptr m_root_object;
66 kepler_object_group_root_ptr m_object_group_root;53 kepler_object_group_root_ptr m_object_group_root;
67 std::optional<mc::future<void>> m_async_task;54 std::optional<mc::future<void>> m_async_task;
55+ std::atomic<bool> m_stop_requested{false};
68};56};
69 57 
70} // namespace devmon58} // namespace devmon
@@ -9,18 +9,69 @@
9 * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.9 * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
10 * See the Mulan PSL v2 for more details.10 * See the Mulan PSL v2 for more details.
11 */11 */
12+#include <mc/app/application.h>
12#include <mc/filesystem.h>13#include <mc/filesystem.h>
13#include <mc/log.h>14#include <mc/log.h>
14#include <mc/runtime.h>15#include <mc/runtime.h>
15 16 
17+#include <chrono>
18+#include <thread>
19+ 
16#include <devmon-common/app_schema.h>20#include <devmon-common/app_schema.h>
17#include <devmon-common/device_driver.h>21#include <devmon-common/device_driver.h>
22+#include <devmon-common/service_readiness.h>
18#include <hwdiscovery/component/csr_config.h>23#include <hwdiscovery/component/csr_config.h>
19#include <hwdiscovery/discovery/discovery.h>24#include <hwdiscovery/discovery/discovery.h>
20#include <hwdiscovery/discovery/kepler_object_group.h>25#include <hwdiscovery/discovery/kepler_object_group.h>
21#include <hwdiscovery/discovery_service.h>26#include <hwdiscovery/discovery_service.h>
22 27 
23namespace devmon {28namespace devmon {
29+namespace {
30+ 
31+constexpr auto DISCOVERY_DEPENDENCY_RETRY_INTERVAL = std::chrono::milliseconds(50);
32+constexpr auto DISCOVERY_DEPENDENCY_TIMEOUT = std::chrono::seconds(60);
33+constexpr const char* HWPROXY_SERVICE_NAME = "bmc.kepler.hwproxy";
34+ 
35+bool wait_for_discovery_dependencies(mc::app::application* app, const std::atomic<bool>& stop_requested)
36+{
37+ const auto deadline = std::chrono::steady_clock::now() + DISCOVERY_DEPENDENCY_TIMEOUT;
38+ nlog("Waiting for application and hwproxy service readiness before discovery");
39+ 
40+ while (!stop_requested.load(std::memory_order_acquire)) {
41+ if (app != nullptr && service_readiness::is_application_ready() && service_readiness::is_hwproxy_ready() &&
42+ app->get_service(HWPROXY_SERVICE_NAME)) {
43+ nlog("Application and hwproxy service are ready for discovery");
44+ return true;
45+ }
46+ 
47+ if (std::chrono::steady_clock::now() >= deadline) {
48+ elog("Timed out waiting for application and hwproxy service readiness; discovery will not start");
49+ return false;
50+ }
51+ std::this_thread::sleep_for(DISCOVERY_DEPENDENCY_RETRY_INTERVAL);
52+ }
53+ 
54+ ilog("Discovery readiness wait cancelled during service shutdown");
55+ return false;
56+}
57+ 
58+} // namespace
59+ 
60+discovery_service::~discovery_service()
61+{
62+ m_stop_requested.store(true, std::memory_order_release);
63+ if (!m_async_task.has_value()) {
64+ return;
65+ }
66+ 
67+ try {
68+ if (m_async_task->wait_for(std::chrono::seconds(30)) == mc::future_status::timeout) {
69+ wlog("discovery_service async task timed out during destructor");
70+ }
71+ } catch (const std::exception& e) {
72+ wlog("exception waiting for async task during destructor: ${error}", ("error", e.what()));
73+ }
74+}
24 75 
25bool discovery_service::on_configure()76bool discovery_service::on_configure()
26{77{
@@ -33,6 +84,7 @@ bool discovery_service::on_configure()
33 84 
34bool discovery_service::on_start()85bool discovery_service::on_start()
35{86{
87+ m_stop_requested.store(false, std::memory_order_release);
36 m_root_object = hwdiscovery_root_object::create_hwdiscovery_root_object(this);88 m_root_object = hwdiscovery_root_object::create_hwdiscovery_root_object(this);
37 m_object_group_root = kepler_object_group_root::create_object(this);89 m_object_group_root = kepler_object_group_root::create_object(this);
38 ilog("Starting discovery service: ${name}", ("name", name()));90 ilog("Starting discovery service: ${name}", ("name", name()));
@@ -40,9 +92,21 @@ bool discovery_service::on_start()
40 auto task_promise = mc::make_promise<void>();92 auto task_promise = mc::make_promise<void>();
41 m_async_task = task_promise.get_future();93 m_async_task = task_promise.get_future();
42 94 
95+ mc::app::application* app = nullptr;
96+ try {
97+ app = &context().app();
98+ } catch (const std::exception&) {
99+ // Standalone service tests do not inject a service context.
100+ }
101+ 
43 mc::post(102 mc::post(
44- [this, task_promise = std::move(task_promise)]() mutable {103+ [this, app, task_promise = std::move(task_promise)]() mutable {
45 try {104 try {
105+ if (!wait_for_discovery_dependencies(app, m_stop_requested)) {
106+ task_promise.set_value();
107+ return;
108+ }
109+ 
46 ilog("Starting asynchronous CSR config initialization and discovery for service: "110 ilog("Starting asynchronous CSR config initialization and discovery for service: "
47 "${name}",111 "${name}",
48 ("name", name()));112 ("name", name()));
@@ -70,6 +134,7 @@ bool discovery_service::on_start()
70 134 
71bool discovery_service::on_stop()135bool discovery_service::on_stop()
72{136{
137+ m_stop_requested.store(true, std::memory_order_release);
73 if (m_async_task.has_value()) {138 if (m_async_task.has_value()) {
74 ilog("Waiting for asynchronous tasks to complete before stopping service: ${name}", ("name", name()));139 ilog("Waiting for asynchronous tasks to complete before stopping service: ${name}", ("name", name()));
75 140 
@@ -21,6 +21,8 @@
21#include <mc/filesystem.h>21#include <mc/filesystem.h>
22#include <mc/log.h>22#include <mc/log.h>
23 23 
24+#include <devmon-common/service_readiness.h>
25+ 
24#include <sstream>26#include <sstream>
25 27 
26namespace devmon {28namespace devmon {
@@ -119,13 +121,16 @@ hwproxy_service::hwproxy_service(mc::string name) : mc::app::service(std::move(n
119 121 
120bool hwproxy_service::on_start()122bool hwproxy_service::on_start()
121{123{
124+ service_readiness::set_hwproxy_ready(false);
122 m_root_object = hwproxy_root_object::create_hwproxy_root_object(this);125 m_root_object = hwproxy_root_object::create_hwproxy_root_object(this);
126+ service_readiness::set_hwproxy_ready(true);
123 ilog("hwproxy service started");127 ilog("hwproxy service started");
124 return true;128 return true;
125}129}
126 130 
127bool hwproxy_service::on_stop()131bool hwproxy_service::on_stop()
128{132{
133+ service_readiness::set_hwproxy_ready(false);
129 try {134 try {
130 auto& object_table = get_object_table();135 auto& object_table = get_object_table();
131 if (object_table.find_object(mc::engine::by_path::field == "/bmc/hwproxy")) {136 if (object_table.find_object(mc::engine::by_path::field == "/bmc/hwproxy")) {
@@ -12,6 +12,7 @@ hwproxy_test_lib = static_library(
12 hwproxy_dep,12 hwproxy_dep,
13 common_dep,13 common_dep,
14 gtest_dep,14 gtest_dep,
15+ test_utilities_dep,
15 ],16 ],
16 include_directories: [17 include_directories: [
17 include_directories('../include'),18 include_directories('../include'),
@@ -10,15 +10,19 @@
10 * See the Mulan PSL v2 for more details.10 * See the Mulan PSL v2 for more details.
11 */11 */
12 12 
13+#include <devmon-common/service_readiness.h>
13#include <gtest/gtest.h>14#include <gtest/gtest.h>
14#include <hwproxy/hwproxy_root_object.h>15#include <hwproxy/hwproxy_root_object.h>
15#include <hwproxy/hwproxy_service.h>16#include <hwproxy/hwproxy_service.h>
16#include <mc/filesystem.h>17#include <mc/filesystem.h>
18+#include <test_utilities/test_base.h>
17 19 
18#include <unistd.h>20#include <unistd.h>
19 21 
20namespace {22namespace {
21 23 
24+class HwproxyServiceTest : public mc::test::TestWithDbusDaemon {};
25+ 
22// 测试 hwproxy 服务构造26// 测试 hwproxy 服务构造
23TEST(HwproxyService, CanConstruct)27TEST(HwproxyService, CanConstruct)
24{28{
@@ -34,6 +38,19 @@ TEST(HwproxyService, CanInit)
34 EXPECT_TRUE(service.init(args));38 EXPECT_TRUE(service.init(args));
35}39}
36 40 
41+TEST_F(HwproxyServiceTest, PublishesReadinessWhileStarted)
42+{
43+ devmon::service_readiness::set_hwproxy_ready(false);
44+ auto service = mc::make_shared<devmon::hwproxy_service>("bmc.kepler.hwproxy");
45+ ASSERT_TRUE(service->init({}));
46+ 
47+ ASSERT_TRUE(service->start());
48+ EXPECT_TRUE(devmon::service_readiness::is_hwproxy_ready());
49+ 
50+ EXPECT_TRUE(service->stop());
51+ EXPECT_FALSE(devmon::service_readiness::is_hwproxy_ready());
52+}
53+ 
37TEST(HwproxyService, OnDumpCreatesSnapshot)54TEST(HwproxyService, OnDumpCreatesSnapshot)
38{55{
39 devmon::hwproxy_service service("bmc.kepler.hwproxy.dump");56 devmon::hwproxy_service service("bmc.kepler.hwproxy.dump");
@@ -55,7 +72,4 @@ TEST(HwproxyService, OnDumpCreatesSnapshot)
55 mc::filesystem::remove_all(test_dir);72 mc::filesystem::remove_all(test_dir);
56}73}
57 74 
58-// 注意:start() 测试需要 DBus 连接,在 CI 环境中可能失败
59-// 实际的服务启动和根对象创建功能已在 devmon_tests 中通过集成测试验证
60- 
61} // namespace75} // namespace
@@ -2,7 +2,7 @@
2 "name": "devmon",2 "name": "devmon",
3 "type": "application",3 "type": "application",
4 "deployConfig": "",4 "deployConfig": "",
5- "version": "1.2.72",5+ "version": "1.2.73",
6 "license": "",6 "license": "",
7 "description": "",7 "description": "",
8 "options": {8 "options": {
@@ -10,6 +10,7 @@
10 * See the Mulan PSL v2 for more details.10 * See the Mulan PSL v2 for more details.
11 */11 */
12 12 
13+#include <devmon-common/service_readiness.h>
13#include <devmon/devmon.h>14#include <devmon/devmon.h>
14#if DEVMON_ENABLE_UNIDEV15#if DEVMON_ENABLE_UNIDEV
15#include <hwdiscovery/discovery_service.h>16#include <hwdiscovery/discovery_service.h>
@@ -47,10 +48,11 @@ int main(int argc, char** argv)
47 ilog("architecture mode: ${mode}", ("mode", DEVMON_ENABLE_UNIDEV ? "three-layer" : "single"));48 ilog("architecture mode: ${mode}", ("mode", DEVMON_ENABLE_UNIDEV ? "three-layer" : "single"));
48 49 
49 mc::app::application app;50 mc::app::application app;
51+ devmon::service_readiness::set_application_ready(false);
50 app.register_service<devmon::devmon_service>("devmon_service");52 app.register_service<devmon::devmon_service>("devmon_service");
51#if DEVMON_ENABLE_UNIDEV53#if DEVMON_ENABLE_UNIDEV
52- app.register_service<devmon::discovery_service>("discovery_service");
53 app.register_service<devmon::hwproxy_service>("hwproxy_service");54 app.register_service<devmon::hwproxy_service>("hwproxy_service");
55+ app.register_service<devmon::discovery_service>("discovery_service");
54#endif56#endif
55 57 
56 if (!app.initialize(mc::app::app_options{argc, argv})) {58 if (!app.initialize(mc::app::app_options{argc, argv})) {
@@ -61,10 +63,14 @@ int main(int argc, char** argv)
61 elog("start devmon app failed");63 elog("start devmon app failed");
62 return 1;64 return 1;
63 }65 }
66+ devmon::service_readiness::set_application_ready(true);
64 67 
65 ilog("devmon started");68 ilog("devmon started");
66- return app.exec();69+ const int exit_code = app.exec();
70+ devmon::service_readiness::set_application_ready(false);
71+ return exit_code;
67 } catch (const std::exception& e) {72 } catch (const std::exception& e) {
73+ devmon::service_readiness::set_application_ready(false);
68 elog("error: ${error}", ("error", e.what()));74 elog("error: ${error}", ("error", e.what()));
69 return 1;75 return 1;
70 }76 }