已开启
fd_test_1 #874
已开启
xhz-sz创建于 19 天前
9 个文件变更+753-13
@@ -847,6 +847,10 @@ public:
847 847 
848 void DupAllFd();848 void DupAllFd();
849 849 
850+ bool AssignDup(const Want &src);
851+ 
852+ static bool DupCopy(const Want &src, Want &out);
853+ 
850 void SetEntities(const std::vector<std::string> &entities);854 void SetEntities(const std::vector<std::string> &entities);
851 static int32_t Flags_ConvertEts2Native(const int32_t index);855 static int32_t Flags_ConvertEts2Native(const int32_t index);
852 static int32_t Flags_ConvertNative2Ets(const int32_t nativeValue);856 static int32_t Flags_ConvertNative2Ets(const int32_t nativeValue);
@@ -0,0 +1,55 @@
1+/*
2+ * Copyright (c) 2026 Huawei Device Co., Ltd.
3+ * Licensed under the Apache License, Version 2.0 (the "License");
4+ * you may not use this file except in compliance with the License.
xhz-sz
xhz-szxhz-sz7 天前

/*

  • Copyright (c) 2026 Huawei Device Co., Ltd.
  • Licensed under the Apache License, Version 2.0 (the "License");
  • you may not use this file except in compliance with the License.
  • You may obtain a copy of the License at
  • http://www.apache.org/licenses/LICENSE-2.0
    
  • Unless required by applicable law or agreed to in writing, software
  • distributed under the License is distributed on an "AS IS" BASIS,
  • WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  • See the License for the specific language governing permissions and
  • limitations under the License. */ #ifndef OHOS_ABILITY_BASE_WANT_FD_SCOPE_H #define OHOS_ABILITY_BASE_WANT_FD_SCOPE_H

#include "want.h"

namespace OHOS { namespace AAFwk {

class WantFdScope final { public: explicit WantFdScope(Want &want) : want_(&want) {} explicit WantFdScope(Want *want) : want_(want) {} ~WantFdScope() { if (want_ != nullptr) { want_->CloseAllFd(); } } WantFdScope(const WantFdScope &) = delete; WantFdScope &operator=(const WantFdScope &) = delete; WantFdScope(WantFdScope &&other) noexcept : want_(other.want_) { other.want_ = nullptr; } WantFdScope &operator=(WantFdScope &&) = delete;

void Release() noexcept
{
    want_ = nullptr;
}

void Strip()
{
    if (want_ != nullptr) {
        want_->RemoveAllFd();
    }
}

private: Want *want_; }; } // namespace AAFwk } // namespace OHOS

#endif // OHOS_ABILITY_BASE_WANT_FD_SCOPE_H

likedislike
5+ * You may obtain a copy of the License at
6+ *
7+ * http://www.apache.org/licenses/LICENSE-2.0
8+ *
9+ * Unless required by applicable law or agreed to in writing, software
10+ * distributed under the License is distributed on an "AS IS" BASIS,
11+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+ * See the License for the specific language governing permissions and
13+ * limitations under the License.
14+ */
15+#ifndef OHOS_ABILITY_BASE_WANT_FD_SCOPE_H
16+#define OHOS_ABILITY_BASE_WANT_FD_SCOPE_H
17+ 
18+#include "want.h"
19+ 
20+namespace OHOS {
21+namespace AAFwk {
22+ 
23+class WantFdScope final {
24+public:
25+ explicit WantFdScope(Want &want) : want_(&want) {}
26+ ~WantFdScope()
27+ {
28+ if (want_ != nullptr) {
29+ want_->CloseAllFd();
30+ }
31+ }
32+ WantFdScope(const WantFdScope &) = delete;
33+ WantFdScope &operator=(const WantFdScope &) = delete;
34+ WantFdScope(WantFdScope &&) = delete;
35+ WantFdScope &operator=(WantFdScope &&) = delete;
36+ 
37+ void Release() noexcept
38+ {
39+ want_ = nullptr;
40+ }
41+ 
42+ void Strip()
43+ {
44+ if (want_ != nullptr) {
45+ want_->RemoveAllFd();
46+ }
47+ }
48+ 
49+private:
50+ Want *want_;
51+};
52+} // namespace AAFwk
53+} // namespace OHOS
54+ 
55+#endif // OHOS_ABILITY_BASE_WANT_FD_SCOPE_H
@@ -114,6 +114,10 @@ public:
114 114 
115 void DupAllFd();115 void DupAllFd();
116 116 
117+ bool AssignDup(const WantParams &src);
118+ 
119+ static bool DupCopy(const WantParams &src, WantParams &out);
120+ 
117 void GetCachedUnsupportedData(std::vector<UnsupportedData> &cachedUnsuppertedData) const;121 void GetCachedUnsupportedData(std::vector<UnsupportedData> &cachedUnsuppertedData) const;
118 122 
119 void SetCachedUnsupportedData(const std::vector<UnsupportedData> &cachedUnsuppertedData);123 void SetCachedUnsupportedData(const std::vector<UnsupportedData> &cachedUnsuppertedData);
@@ -221,6 +225,7 @@ private:
221 bool NewArrayData(IArray *source, sptr<IArray> &dest);225 bool NewArrayData(IArray *source, sptr<IArray> &dest);
222 bool NewParams(const WantParams &source, WantParams &dest);226 bool NewParams(const WantParams &source, WantParams &dest);
223 bool NewFds(const WantParams &source, WantParams &dest);227 bool NewFds(const WantParams &source, WantParams &dest);
228+ bool TryDupAllFd();
224 bool AddWantParamToInterfaceVector(const sptr<WantParams> &value,229 bool AddWantParamToInterfaceVector(const sptr<WantParams> &value,
225 std::vector<sptr<IInterface>> &array) const;230 std::vector<sptr<IInterface>> &array) const;
226 231 
@@ -2316,6 +2316,16 @@ void Want::DupAllFd()
2316 parameters_.DupAllFd();2316 parameters_.DupAllFd();
2317}2317}
2318 2318 
2319+bool Want::AssignDup(const Want &src)
xhz-sz
xhz-szxhz-sz7 天前

bool Want::AssignDup(const Want &src) { operation_ = src.operation_; bool ret = parameters_.AssignDup(src.GetParams()); ABILITYBASE_LOGI("Want::AssignDup result:%{public}d", ret); return ret; }

bool Want::DupCopy(const Want &src, Want &out) { return WantParams::DupCopy(src.GetParams(), out.parameters_); }

likedislike
2320+{
2321+ return parameters_.AssignDup(src.GetParams());
2322+}
2323+ 
2324+bool Want::DupCopy(const Want &src, Want &out)
2325+{
2326+ return WantParams::DupCopy(src.GetParams(), out.parameters_);
2327+}
2328+ 
2319void Want::SetEntities(const std::vector<std::string> &entities)2329void Want::SetEntities(const std::vector<std::string> &entities)
2320{2330{
2321 operation_.SetEntities(entities);2331 operation_.SetEntities(entities);
@@ -1885,22 +1885,63 @@ void WantParams::RemoveAllFd()
1885 fds_.clear();1885 fds_.clear();
1886}1886}
1887 1887 
1888+bool WantParams::TryDupAllFd()
xhz-sz
xhz-szxhz-sz7 天前

bool WantParams::TryDupAllFd() { ABILITYBASE_LOGI("TryDupAllFd called, fd count: %{public}zu", fds_.size()); std::vector<std::pair<std::string, int32_t>> staged; for (const auto &it : fds_) { if (it.second < 0) { continue; } int32_t dupFd = dup(it.second); if (dupFd < 0) { ABILITYBASE_LOGE("dup fd failed, key: %{public}s oldFd:%{public}d", it.first.c_str(), it.second); for (const auto &s : staged) { close(s.second); } return false; } ABILITYBASE_LOGI("dup fd key:%{public}s oldFd:%{public}d newFd:%{public}d", it.first.c_str(), it.second, dupFd); staged.emplace_back(it.first, dupFd); } for (const auto &s : staged) { WantParams wp; wp.SetParam(TYPE_PROPERTY, String::Box(FD)); wp.SetParam(VALUE_PROPERTY, Integer::Box(s.second)); sptrAAFwk::IWantParams pWantParams = AAFwk::WantParamWrapper::Box(wp); params_[s.first] = pWantParams; fds_[s.first] = s.second; } return true; }

void WantParams::DupAllFd() { (void)TryDupAllFd(); }

bool WantParams::AssignDup(const WantParams &src) { if (this == &src) { return TryDupAllFd(); } *this = src; if (!TryDupAllFd()) { RemoveAllFd(); return false; } return true; }

bool WantParams::DupCopy(const WantParams &src, WantParams &out) { if (&out == &src) { return out.TryDupAllFd(); } out = src; if (!out.TryDupAllFd()) { out.RemoveAllFd(); return false; } return true; }

likedislike
1889+{
1890+ std::vector<std::pair<std::string, int32_t>> staged;
1891+ for (const auto &it : fds_) {
1892+ if (it.second < 0) {
1893+ continue;
1894+ }
1895+ int32_t dupFd = dup(it.second);
1896+ if (dupFd < 0) {
1897+ ABILITYBASE_LOGE("dup fd failed, key: %{public}s", it.first.c_str());
1898+ for (const auto &s : staged) {
1899+ close(s.second);
1900+ }
1901+ return false;
1902+ }
1903+ staged.emplace_back(it.first, dupFd);
1904+ }
1905+ for (const auto &s : staged) {
1906+ WantParams wp;
1907+ wp.SetParam(TYPE_PROPERTY, String::Box(FD));
1908+ wp.SetParam(VALUE_PROPERTY, Integer::Box(s.second));
1909+ sptr<AAFwk::IWantParams> pWantParams = AAFwk::WantParamWrapper::Box(wp);
1910+ params_[s.first] = pWantParams;
1911+ fds_[s.first] = s.second;
1912+ }
1913+ return true;
1914+}
1915+ 
1888void WantParams::DupAllFd()1916void WantParams::DupAllFd()
1889{1917{
1890- for (auto it : fds_) {1918+ (void)TryDupAllFd();
1891- if (it.second > 0) {1919+}
1892- int dupFd = dup(it.second);1920+ 
1893- if (dupFd > 0) {1921+bool WantParams::AssignDup(const WantParams &src)
1894- params_.erase(it.first);1922+{
1895- WantParams wp;1923+ if (this == &src) {
1896- wp.SetParam(TYPE_PROPERTY, String::Box(FD));1924+ return TryDupAllFd();
1897- wp.SetParam(VALUE_PROPERTY, Integer::Box(dupFd));
1898- sptr<AAFwk::IWantParams> pWantParams = AAFwk::WantParamWrapper::Box(wp);
1899- SetParam(it.first, pWantParams);
1900- fds_[it.first] = dupFd;
1901- }
1902- }
1903 }1925 }
1926+ *this = src;
1927+ if (!TryDupAllFd()) {
1928+ RemoveAllFd();
1929+ return false;
1930+ }
1931+ return true;
1932+}
1933+ 
1934+bool WantParams::DupCopy(const WantParams &src, WantParams &out)
1935+{
1936+ if (&out == &src) {
1937+ return out.TryDupAllFd();
1938+ }
1939+ out = src;
1940+ if (!out.TryDupAllFd()) {
1941+ out.RemoveAllFd();
1942+ return false;
1943+ }
1944+ return true;
1904}1945}
1905 1946 
1906void WantParams::GetCachedUnsupportedData(std::vector<UnsupportedData> &cachedUnsupportedData) const1947void WantParams::GetCachedUnsupportedData(std::vector<UnsupportedData> &cachedUnsupportedData) const
@@ -244,6 +244,28 @@ ohos_unittest("want_params_test") {
244 ]244 ]
245}245}
246 246 
247+ohos_unittest("want_fd_scope_test") {
248+ module_out_path = want_output_path
249+ sources = [ "want/want_fd_scope_test.cpp" ]
250+ 
251+ configs = [
252+ ":want_private_config",
253+ "${ability_base_path}:want_public_config",
254+ ]
255+ 
256+ deps = [
257+ "${ability_base_path}:base",
258+ "${ability_base_path}:want",
259+ ]
260+ 
261+ external_deps = [
262+ "c_utils:utils",
263+ "googletest:gtest_main",
264+ "hilog:libhilog",
265+ "ipc:ipc_core",
266+ ]
267+}
268+ 
247ohos_unittest("want_params_wrapper_test") {269ohos_unittest("want_params_wrapper_test") {
248 module_out_path = want_output_path270 module_out_path = want_output_path
249 sources = [ "want/want_params_wrapper_test.cpp" ]271 sources = [ "want/want_params_wrapper_test.cpp" ]
@@ -831,6 +853,7 @@ group("unittest") {
831 ":user_object_wrapper_test",853 ":user_object_wrapper_test",
832 ":view_data_test",854 ":view_data_test",
833 ":want_params_test",855 ":want_params_test",
856+ ":want_fd_scope_test",
834 ":want_params_wrapper_test",857 ":want_params_wrapper_test",
835 ":want_params_wrapper_json_test",858 ":want_params_wrapper_json_test",
836 ":want_test",859 ":want_test",
@@ -0,0 +1,183 @@
1+/*
2+ * Copyright (c) 2026 Huawei Device Co., Ltd.
3+ * Licensed under the Apache License, Version 2.0 (the "License");
4+ * you may not use this file except in compliance with the License.
5+ * You may obtain a copy of the License at
6+ *
7+ * http://www.apache.org/licenses/LICENSE-2.0
8+ *
9+ * Unless required by applicable law or agreed to in writing, software
10+ * distributed under the License is distributed on an "AS IS" BASIS,
11+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+ * See the License for the specific language governing permissions and
13+ * limitations under the License.
14+ */
15+ 
16+#include <gtest/gtest.h>
17+#include <fcntl.h>
18+#include <unistd.h>
19+#include <utility>
20+ 
21+#define private public
22+#include "want_params.h"
23+#include "want.h"
24+#undef private
25+ 
26+#include "int_wrapper.h"
27+#include "string_wrapper.h"
28+#include "want_fd_scope.h"
29+#include "want_params_wrapper.h"
30+ 
31+using namespace OHOS;
32+using namespace OHOS::AAFwk;
33+using testing::ext::TestSize;
34+ 
35+namespace OHOS {
36+namespace AAFwk {
37+namespace {
38+void SetFdMarker(WantParams &p, const std::string &key, int fd)
39+{
40+ WantParams wp;
41+ wp.SetParam(TYPE_PROPERTY, String::Box("FD"));
42+ wp.SetParam(VALUE_PROPERTY, Integer::Box(fd));
43+ sptr<IWantParams> marker = WantParamWrapper::Box(wp);
44+ p.SetParam(key, marker);
45+ p.fds_[key] = fd;
46+}
47+ 
48+int MakePipeReadEnd()
49+{
50+ int pipefd[2] = {-1, -1};
51+ if (pipe(pipefd) != 0) {
52+ return -1;
53+ }
54+ close(pipefd[1]);
55+ return pipefd[0];
56+}
57+} // namespace
58+ 
59+class WantFdScopeTest : public testing::Test {
60+public:
61+ static void SetUpTestCase() {};
62+ static void TearDownTestCase() {};
63+ void SetUp() {};
64+ void TearDown() {};
65+};
66+ 
67+HWTEST_F(WantFdScopeTest, TryDupAllFd_ProducesIndependentFd, TestSize.Level1)
68+{
69+ int fdS = MakePipeReadEnd();
70+ ASSERT_GE(fdS, 0);
71+ WantParams p;
72+ SetFdMarker(p, "k", fdS);
73+ EXPECT_TRUE(p.TryDupAllFd());
74+ int fdD = p.fds_["k"];
75+ EXPECT_GE(fdD, 0);
76+ EXPECT_NE(fdD, fdS);
77+ close(fdD);
78+ EXPECT_NE(-1, fcntl(fdS, F_GETFD));
79+ close(fdS);
80+}
81+ 
82+HWTEST_F(WantFdScopeTest, DupAllFd_VoidWrapper, TestSize.Level1)
83+{
84+ int fdS = MakePipeReadEnd();
85+ ASSERT_GE(fdS, 0);
86+ WantParams p;
87+ SetFdMarker(p, "k", fdS);
88+ p.DupAllFd();
89+ int fdD = p.fds_["k"];
90+ EXPECT_GE(fdD, 0);
91+ EXPECT_NE(fdD, fdS);
92+ close(fdD);
93+ close(fdS);
94+}
95+ 
96+HWTEST_F(WantFdScopeTest, AssignDup_SourceUntouchedAfterDstClose, TestSize.Level1)
97+{
98+ int fdS = MakePipeReadEnd();
99+ ASSERT_GE(fdS, 0);
100+ WantParams src;
101+ SetFdMarker(src, "k", fdS);
102+ WantParams dst;
103+ EXPECT_TRUE(dst.AssignDup(src));
104+ int fdD = dst.fds_["k"];
105+ EXPECT_GE(fdD, 0);
106+ EXPECT_NE(fdD, fdS);
107+ EXPECT_EQ(fdS, src.fds_["k"]);
108+ dst.CloseAllFd();
109+ EXPECT_NE(-1, fcntl(fdS, F_GETFD));
110+ close(fdS);
111+}
112+ 
113+HWTEST_F(WantFdScopeTest, AssignDup_Self, TestSize.Level1)
114+{
115+ int fdS = MakePipeReadEnd();
116+ ASSERT_GE(fdS, 0);
117+ WantParams p;
118+ SetFdMarker(p, "k", fdS);
119+ EXPECT_TRUE(p.AssignDup(p));
120+ int fdD = p.fds_["k"];
121+ EXPECT_GE(fdD, 0);
122+ EXPECT_NE(fdD, fdS);
123+ close(fdD);
124+ close(fdS);
125+}
126+ 
127+HWTEST_F(WantFdScopeTest, DupCopy_SourceUntouchedAfterOutClose, TestSize.Level1)
128+{
129+ int fdS = MakePipeReadEnd();
130+ ASSERT_GE(fdS, 0);
131+ WantParams src;
132+ SetFdMarker(src, "k", fdS);
133+ WantParams out;
134+ EXPECT_TRUE(WantParams::DupCopy(src, out));
135+ int fdO = out.fds_["k"];
136+ EXPECT_GE(fdO, 0);
137+ EXPECT_NE(fdO, fdS);
138+ out.CloseAllFd();
139+ EXPECT_NE(-1, fcntl(fdS, F_GETFD));
140+ close(fdS);
141+}
142+ 
143+HWTEST_F(WantFdScopeTest, WantFdScope_ClosesOnDtor, TestSize.Level1)
144+{
145+ int fd = MakePipeReadEnd();
146+ ASSERT_GE(fd, 0);
147+ Want w;
148+ SetFdMarker(w.parameters_, "k", fd);
149+ {
150+ WantFdScope scope(w);
151+ }
152+ EXPECT_EQ(-1, fcntl(fd, F_GETFD));
153+}
154+ 
155+HWTEST_F(WantFdScopeTest, WantFdScope_ReleaseDisarms, TestSize.Level1)
156+{
157+ int fd = MakePipeReadEnd();
158+ ASSERT_GE(fd, 0);
159+ Want w;
160+ SetFdMarker(w.parameters_, "k", fd);
161+ {
162+ WantFdScope scope(w);
163+ scope.Release();
164+ }
165+ EXPECT_NE(-1, fcntl(fd, F_GETFD));
166+ close(fd);
167+}
168+ 
169+HWTEST_F(WantFdScopeTest, WantFdScope_StripDoesNotClose, TestSize.Level1)
170+{
171+ int fd = MakePipeReadEnd();
172+ ASSERT_GE(fd, 0);
173+ Want w;
174+ SetFdMarker(w.parameters_, "k", fd);
175+ {
176+ WantFdScope scope(w);
177+ scope.Strip();
178+ }
179+ EXPECT_NE(-1, fcntl(fd, F_GETFD));
180+ close(fd);
181+}
182+} // namespace AAFwk
183+} // namespace OHOS
@@ -0,0 +1,174 @@
1+# AMS Want FD 调用点 Manifest(步骤 b 产出)
2+ 
3+> 日期:2026-08-29
4+> 范围:`services/abilitymgr/`(AMS,SA 180,foundation 进程)
5+> 扫描口径:`CloseAllFd` / `RemoveAllFd` / `DupAllFd` 全量
6+> 实测:16 `CloseAllFd` + 7 `RemoveAllFd` + 0 `DupAllFd` = 23 点(V11 的 20/18 是全 runtime 口径,AMS 子集为 23)
7+> 用途:为 Scope 方案 P2 试点迁移提供逐点决策依据。**每点迁移须配对应 UT/长跑证据,不做无证据机械删除。**
8+ 
9+---
10+ 
11+## 1. 汇总与分类
12+ 
13+| 类别 | 点数 | 迁移决定 |
14+|---|---|---|
15+| A. stub 同步局部 Want 末尾兜底 Close | 9 | → `WantFdScope` 替代(删手工 Close) |
16+| B. 赋值(关旧 + assign 新) | 1 | → `AssignDup` 替换(删 CloseAllFd+operator=) |
17+| C. 析构中 CloseAllFd | 1 | → 候选删(want_ 迁独立 ownership 后自关) |
18+| D. 业务剥离(路由/持久化/拦截器只取元数据) | 8 | → 保留 `RemoveAllFd`/`strip()`(业务语义,非兜底) |
19+| E. 长期对象业务关闭(session/pending 用完关) | 3 | → 保留显式关;入库改 `AssignDup` |
20+| F. 待确认(同步/生命周期需核实) | 1 | → 分析后归入 A 或 E |
21+| **合计** | **23** | |
22+ 
23+---
24+ 
25+## 2. A 类:stub 同步末尾兜底 Close → `WantFdScope`(删)
26+ 
27+通用模式:`Want/SessionInfo *x = data.ReadParcelable<…>(); …处理…; x->CloseAllFd(); return;`
28+ 
29+| # | file:line | 对象 | 处理后调用 | 决策 |
30+|---|---|---|---|---|
31+| A1 | ability_manager_stub.cpp:1340 | `extensionSessionInfo->want` | TerminateUIExtensionAbility 后 | `WantFdScope` 覆盖返回路径,删 Close |
32+| A2 | ability_manager_stub.cpp:1397 | `extensionSessionInfo->want` | MinimizeUIExtensionAbility 后 | 同上 |
33+| A3 | ability_manager_stub.cpp:2217 | `senderInfo->want` | SendWantSender 后 | 同上 |
34+| A4 | ability_manager_stub.cpp:2234 | `senderInfo->want` | SendLocalWantSender 后 | 同上 |
35+| A5 | ability_manager_stub.cpp:5232 | `want` (sptr 局部) | StartSelfUIAbility 后 | 同上 |
36+| A6 | ability_manager_stub.cpp:5255 | `want` | StartSelfUIAbilityWithStartOptions 后 | 同上 |
37+| A7 | ability_manager_stub.cpp:5276 | `want` | StartSelfUIAbilityWithToken 后 | 同上 |
38+| A8 | ability_manager_stub.cpp:5303 | `want` | StartSelfUIAbilityWithStartOptionsAndToken 后 | 同上 |
39+| A9 | ability_manager_stub.cpp:5327 | `want` | StartSelfUIAbilityWithPidResult 后 | 同上 |
40+ 
41+**注意**:这 9 点的 Want 均为 stub 局部 `sptr`/`unique_ptr`(来自 `ReadParcelable`),IPC 接收侧 Adopt 的 OWNED marker。scope 析构 = stub 局部 Want 析构时关。迁移前须确认:处理函数(如 StartSelfUIAbility)未把 Want 异步逃逸(按值传入则安全;若存成员则该处升级 B/E)。
42+ 
43+---
44+ 
45+## 3. B 类:赋值 → `AssignDup`(替换)
46+ 
47+| # | file:line | 现状代码 | 决策 |
48+|---|---|---|---|
49+| B1 | ability_record.cpp:2115 | `want_.CloseAllFd(); want_ = want;`(SetWant 中关旧再赋新) | → `want_.AssignDup(want)`:清旧 + share-copy + dup 成独立 ownership,一步原子;删手工 CloseAllFd + legacy operator= |
50+ 
51+**理由**`want_` 是 AbilityRecord 成员(长期对象),生命周期独立于入参 `want` → 赋值必然 dup。`AssignDup` 失败时 `want_` 清空(无 FD),调用方显式处理 false。
52+ 
53+---
54+ 
55+## 4. C 类:析构中 CloseAllFd → 候选删
56+ 
57+| # | file:line | 上下文 | 决策 |
58+|---|---|---|---|
59+| C1 | ability_record.cpp:179 | `~AbilityRecord` 析构清理(伴 RemoveDeathRecipient 等) | **候选删**:B1 迁移后 `want_` 持独立 dup'd marker,析构自关;该显式 CloseAllFd 冗余。须先完成 B1 并验证 `want_` 无共享引用后再删。 |
60+ 
61+---
62+ 
63+## 5. D 类:业务剥离 → 保留 `RemoveAllFd`/`strip()`(非兜底)
64+ 
65+通用语义:副本只用于路由元数据/持久化/拦截器查询,FD 不应随副本扩散。剥离 ≠ 释放兜底。
66+ 
67+| # | file:line | 对象 | 上下文 | 决策 |
68+|---|---|---|---|---|
69+| D1 | ui_ability_lifecycle_manager.cpp:314 | `sessionInfo->want` | newWant 分支:已 SetWant 给 abilityRecord,session 副本剥离 | 保留 `RemoveAllFd`(→ `strip()`) |
70+| D2 | ui_ability_lifecycle_manager.cpp:812 | `hookSessionInfo->want` | NotifySCBPendingActivation 后剥离 hook 副本 | 保留 |
71+| D3 | ui_ability_lifecycle_manager.cpp:896 | `sessionInfo->want` | NotifySCBPendingActivation 后剥离 | 保留 |
72+| D4 | ui_ability_lifecycle_manager.cpp:975 | `sessionInfo->want` | BatchNotify 后循环剥离各 session | 保留 |
73+| D5 | ui_ability_lifecycle_manager.cpp:4622 | `sessionInfo->want` | StartSpecifiedRequest cold 通知后剥离 | 保留 |
74+| D6 | ui_ability_lifecycle_manager.cpp:4892 | `sessionInfo->want` | NotifySCB 后剥离 | 保留 |
75+| D7 | ecological_rule_interceptor.cpp:43 | `newWant`(局部副本) | ERMS 查询只需元数据,剥离 FD | 保留 |
76+| D8 | mission_info.cpp:34 | `want`(反序列化后) | MissionInfo::ReadFromParcel:mission 不持久化 FD,剥离 | **保留语义,建议改 `RemoveAllFd`/`strip()`**(现为 CloseAllFd,意图是剥离) |
77+ 
78+> D8 注意:当前用 `CloseAllFd` 实现剥离语义。在 share 模型下 `want = *parcelWant` 后 CloseAllFd 掉 want 引用,parcelWant 析构关 fd——结果正确但语义混用。建议显式改 `RemoveAllFd`(剥离不关,由 parcelWant 持有者负责关),语义更清晰。
79+ 
80+---
81+ 
82+## 6. E 类:长期对象业务关闭 → 保留显式关 + 入库 `AssignDup`
83+ 
84+| # | file:line | 对象 | 上下文 | 决策 |
85+|---|---|---|---|---|
86+| E1 | pending_want_key.cpp:158 | `wantInfo.want`(循环) | `ClearAllWantsInfosFd`:清空所有 pending want 的 FD | 保留(业务批量回收);`allWantsInfos_` 入库时改 `AssignDup` 使各 pending want 持独立 fd |
87+| E2 | ui_ability_lifecycle_manager.cpp:316 | `sessionInfo->want` | else 分支(非 newWant):ability 已用,session want FD 不再需要 | 保留显式关(session 长期对象用完关);session 入库改 `AssignDup` |
88+| E3 | ui_ability_lifecycle_manager.cpp:1145 | `sessionInfo->want` | NotifySCB 后 session want 用完关 | 保留 |
89+ 
90+> E 类不删:这些是「长期对象在业务节点显式回收 FD」,不是「局部 Want 析构兜底」。入库升级 `AssignDup` 后,关闭仍由这些显式点负责(业务决定何时回收,不受 scope 栈期限制)。
91+ 
92+---
93+ 
94+## 7. F 类:待确认
95+ 
96+| # | file:line | 上下文 | 待确认 | 倾向 |
97+|---|---|---|---|---|
98+| F1 | ui_ability_lifecycle_manager.cpp:445 | `sessionInfo.want.CloseAllFd()`(IN_PROCESS_CALL LaunchAbility 之后) | sessionInfo 是否纯局部值?NotifySCB 是否同步?Want 是否异步逃逸? | 若局部+同步 → A 类(scope 删);若长期/异步 → E 类(保留) |
99+ 
100+---
101+ 
102+## 8. 每点 manifest 必填字段(迁移执行时补齐)
103+ 
104+每个迁移点须记录:
105+ 
106+1. 点编号(A1…F1)
107+2. FD 来源(IPC 接收 / 同进程传入 / dup 生成)
108+3. 当前 ownership(OWNED marker / 共享 marker / 裸 int)
109+4. 是否复制 Want(share-copy / 无)
110+5. 是否进入同进程调用(IN_PROCESS_CALL)
111+6. 是否进入 IPC(WriteParcelable)
112+7. 是否跨线程/回调/Promise/任务队列
113+8. 最后使用点
114+9. 当前 Close/Remove 是资源释放还是业务剥离
115+10. 迁移决定(scope / AssignDup / 保留 strip / 候选删)
116+11. 对应测试编号
117+ 
118+---
119+ 
120+## 9. 迁移顺序建议(P2 执行)
121+ 
122+1. **A 类(9 点)**:风险最低、收益最直接(stub 兜底全删)。先做 A1–A4(SessionInfo/SenderInfo),再做 A5–A9(StartSelfUIAbility×5)。每点删 CloseAllFd + 加 `WantFdScope`,配 stub 返回路径 UT。
123+2. **B1**`ability_record.cpp:2115` SetWant → `AssignDup`。配 AssignDup 失败回滚 UT。
124+3. **C1**:B1 完成且 `want_` 独立 ownership 验证后,删 `ability_record.cpp:179` 析构 CloseAllFd。
125+4. **F1**:核实 sessionInfo 生命周期后归入 A 或 E。
126+5. **E1–E3**:入库路径改 `AssignDup`;显式关保留。
127+6. **D1–D8**:保留;D8 语义澄清(CloseAllFd→RemoveAllFd)。
128+7. 全程:FD 长跑 `/proc/<pid>/fd` 回基线 + fdsan 无 double-close。
129+ 
130+---
131+ 
132+## 10. 不迁点(明确保留,不在本轮删)
133+ 
134+- D1–D8:业务剥离,非兜底。
135+- E1–E3:长期对象业务回收。
136+- 待 P3 评估是否推广到 AppMS/UPMS 等其它服务(不在 AMS 试点范围)。
137+ 
138+---
139+ 
140+## 11. P2 执行状态(本轮)
141+ 
142+> 本轮执行环境无 OH sysroot,无法编译/跑单测。仅落地**行为保持**类迁移(A 子集);**语义变更**类(B1/E入库)需 build + `ability_manager_service_*_test` + FD 长跑验证后合入。
143+ 
144+### 已完成(ability_base + ability_runtime)
145+ 
146+ability_base(API 基座,已落):
147+- `WantParams::TryDupAllFd`(bool, all-or-nothing, fd-0 修复) + `DupAllFd`(void 包装)
148+- `WantParams::AssignDup` / `WantParams::DupCopy`(static)
149+- `Want::AssignDup` / `Want::DupCopy`(委派,镜像 CloseAllFd 模式)
150+- `WantFdScope`(RAII,包 `Want&`,析构 CloseAllFd / `Release()` 解武装 / `Strip()`=RemoveAllFd)
151+- `want_fd_scope_test`(8 用例)
152+ 
153+ability_runtime AMS(A5–A9,5 点,行为保持):
154+- `ability_manager_stub.cpp``#include "want_fd_scope.h"`
155+- A5 StartSelfUIAbilityInner / A6 WithStartOptions / A7 WithToken / A8 WithStartOptionsAndToken / A9 WithPidResult:每个 `ReadParcelable<Want>`+null-check 后加 `WantFdScope fdScope(*want);`,删 `want->CloseAllFd();`
156+- 效果:成功路径关 fd 点不变(行为保持);早返回路径(reply write fail / options null)原先漏关 → 现由 scope 析构覆盖(**顺带修泄漏**
157+ 
158+### 待续(需 build + 单测验证)
159+ 
160+| 点 | 现状 | 待办 |
161+|---|---|---|
162+| A1–A4 | 未迁 | 双 Want(SessionInfo/SenderInfo 的 want + 独立 resultWant,后者 `delete` 前未关 = 既有泄漏)需逐点分析结构再迁 |
163+| B1 | 未迁 | `ability_record.cpp:2115` SetWant:`want_.CloseAllFd(); want_=want;``want_.CloseAllFd(); want_.AssignDup(want);`(★保留 CloseAllFd 关旧值;baseline 无 RAII,旧 fd 不显式关即泄漏)。语义变更(want_ 由共享→独立 dup),需 AbilityRecord 单测 |
164+| C1 | 不删(Route 1 修正) | `ability_record.cpp:179` 析构 CloseAllFd **保留**:baseline WantParams 析构不关 fd,C1 是关 want_ 的独立 fd 的唯一点(V11「候选删」基于 WantFdValue 自关,Route 1 不适用) |
165+| D1–D8 | 保留 | 业务剥离(RemoveAllFd/strip),非兜底。D8 mission_info CloseAllFd→RemoveAllFd 语义澄清可选 |
166+| E1–E3 | 未迁入库 | 入库路径改 `Want::AssignDup` 修共享-close 隐患(baseline 下 E 类 CloseAllFd 当前可能正关共享 fd);显式关保留 |
167+| F1 | 待确认 | `ui_ability_lifecycle_manager.cpp:445` 核实 sessionInfo 生命周期后归入 A 或 E |
168+ 
169+### 放行门禁(待续项合入前)
170+ 
171+1. `./build.sh --product-name <product> --build-target ability_runtime` 编译通过
172+2. `ability_manager_service_first_test` 及后续编号单测通过(AGENTS.md 要求)
173+3. FD 长跑 `/proc/<pid>/fd` 回基线 + fdsan 无 double-close
174+4. 每点迁移配对应 UT/manifest 证据(不做无证据机械删除)
@@ -0,0 +1,245 @@
1+# Want FD 生命周期治理 Scope 方案(v1 · Route 1 / baseline 接地)
2+ 
3+> 日期:2026-08-29
4+> 路线:**Route 1** — 不复活 WantFdValue;在 **baseline 模型**(FD 为 `Integer::Box(fd)` marker + `fds_` 整数镜像)上叠加 scope+dup 纪律与三个薄 API。
5+> 试点:`services/abilitymgr/`(AMS);其它存量保持兼容。
6+> 原则:拷贝/赋值取舍**以逐点分析为准**
7+ 
8+---
9+ 
10+## 1. baseline 模型与核心隐患
11+ 
12+baseline(当前仓库状态)的 FD 表示:
13+ 
14+- marker:`params_[key]` = `WantParamWrapper`,内含 `{TYPE:"FD", VALUE: Integer::Box(fd)}`
15+- 镜像:`fds_[key] = fd`(裸 int)。
16+- 复制(copy ctor / `operator=`):**深拷贝**——每个副本得到自己的 `Integer` marker + 自己的 `fds_` 镜像,但 **fd 整数值相同** → 副本**共享同一个内核 fd**
17+- 无引用计数、无 ownership 类型。fd 就是个 int。
18+ 
19+两大隐患(本方案要解):
20+ 
21+1. **手工释放遗漏**:AMS 约 16 `CloseAllFd` + 7 `RemoveAllFd`,漏一处即泄漏/早关。
22+2. **共享整数 double-close**:baseline 深拷贝使副本共享内核 fd。任一副本 `CloseAllFd` 关掉的是**共享的内核 fd** → 其它副本的 fd 整数变 stale → 后续 close/use 即 double-close / use-after-close。
23+ 
24+> 与 V11 的区别:V11 用 WantFdValue+refcount 让副本共享 owner、靠最后引用 close。Route 1 **不引入 refcount**,改用 **dup 产生独立内核 fd** 作为唯一独立性手段。
25+ 
26+---
27+ 
28+## 2. 核心思路
29+ 
30+```
31+ IPC 接收 Want(OwnFd=ReadFileDescriptor 给的独立内核 fd) ──┐
32+ ├─→ WantFdScope(栈) 用完即关(安全:独占 fd)
33+ AssignDup/DupCopy ─→ dup 出独立内核 fd ─→ 持有者自关 ─────┘
34+ (各持有者不同内核 fd → close 各自合法 → 无 double-close)
35+```
36+ 
37+- **dup 是独立性的唯一来源**:baseline 无 refcount,只有 `dup()` 给每个持有者一个**不同的内核 fd**,close 互不影响。
38+- **scope 只守独占 fd 的持有者**:IPC 接收件(内核已给独立 fd)或 assign-dup 后的副本。**共享整数副本禁止 CloseAllFd**(会关到源的内核 fd)——这类副本只能 `RemoveAllFd`(剥离不关)或按引用传。
39+ 
40+---
41+ 
42+## 3. 前置修复:baseline `DupAllFd`
43+ 
44+baseline `DupAllFd` 有缺陷,须先修(本方案依赖一个可用的 all-or-nothing dup):
45+ 
46+- **fd-0 bug**`it.second > 0` 漏掉 fd 0 → 改 `>= 0`(fd 0 合法)。
47+- **非 all-or-nothing**:逐个 dup+就地替换,中途失败只 skip → 改 staged:全 dup 成功才一次性替换,任一失败则关掉已 staged 的 dup fd、`*this` 不变。
48+- **无状态返回**:新增私有 `bool TryDupAllFd()``DupAllFd()` 保留 `void` 公开签名(兼容),内部调 `TryDupAllFd()` 忽略返回。
49+ 
50+```cpp
51+bool WantParams::TryDupAllFd() {
52+ std::vector<std::pair<std::string, int32_t>> staged; // key, dupFd
53+ for (const auto &it : fds_) {
54+ if (it.second < 0) continue; // fd>=0 合法(修 fd-0 bug)
55+ int32_t dupFd = dup(it.second);
56+ if (dupFd < 0) {
57+ for (const auto &s : staged) close(s.second); // 回收已 dup
58+ return false; // *this 不变
59+ }
60+ staged.emplace_back(it.first, dupFd);
61+ }
62+ for (const auto &s : staged) { // 全成功才替换
63+ // 重建 marker:Integer::Box(s.second) + TYPE:"FD"
64+ params_[s.first] = marker;
65+ fds_[s.first] = s.second;
66+ }
67+ return true;
68+}
69+void WantParams::DupAllFd() { (void)TryDupAllFd(); }
70+```
71+ 
72+> baseline `Integer` marker 析构不关 fd,故 staged dup fd 失败时须**显式 close**(不能靠 RAII)。
73+ 
74+---
75+ 
76+## 4. 机制一:`WantFdScope`(同步 RAII,仅守独占 fd)
77+ 
78+```cpp
79+class WantFdScope final {
80+public:
81+ explicit WantFdScope(WantParams &p) : params_(&p) {}
82+ ~WantFdScope() { if (params_) params_->CloseAllFd(); }
83+ WantFdScope(const WantFdScope&) = delete;
84+ WantFdScope& operator=(const WantFdScope&) = delete;
85+ void release() noexcept { params_ = nullptr; } // 所有权已 dup 转交则解除武装
86+ void strip() { if (params_) params_->RemoveAllFd(); } // 共享副本剥离(不关)
87+private:
88+ WantParams* params_;
89+};
90+```
91+ 
92+**安全前提**:scope 的析构 `CloseAllFd` 只能用在**独占内核 fd 的持有者**
93+ 
94+- IPC 接收的 Want(`ReadFileDescriptor` 给的独立 fd)✅
95+- `AssignDup`/`DupCopy` 之后的副本(dup 出的独立 fd)✅
96+ 
97+**禁止****共享整数副本**(baseline 深拷贝来的)用 scope/CloseAllFd——会关到源的内核 fd。这类副本用 `strip()``RemoveAllFd`,剥离不关)或按引用传。
98+ 
99+---
100+ 
101+## 5. 机制二:`AssignDup`(赋值必然 dup)
102+ 
103+```cpp
104+bool WantParams::AssignDup(const WantParams &src) {
105+ if (this == &src) return TryDupAllFd();
106+ *this = src; // baseline 深拷贝:*this 持共享整数 marker(=src 的 fd int)
107+ if (!TryDupAllFd()) {
108+ RemoveAllFd(); // ★失败回滚用 RemoveAllFd(不关!*this 持的是 src 的共享内核 fd)
109+ return false;
110+ }
111+ return true; // *this 持独立 dup'd fd → scope/析构关安全
112+}
113+```
114+ 
115+- 「必然 dup」:AMS 凡赋值 FD-bearing WantParams,必须走 `AssignDup`(review+manifest 强制),不用 legacy `operator=`(仍深拷贝共享,仅供存量兼容)。
116+- **失败回滚关键差异**:baseline 下 `*this``TryDupAllFd` 失败时持的是**与 src 共享的内核 fd 整数**;若 `CloseAllFd` 会关掉 src 的 fd → **必须 `RemoveAllFd`(剥离不关)**,src 的内核 fd 不受影响。`*this` 变无 FD。
117+- 错误通道:`TryDupAllFd` all-or-nothing 返回 bool——dup 走带返回值 API,不走 ctor(化解「copy 无错误返回」)。
118+ 
119+为何赋值「必然」dup:赋值目标是已存在对象(成员/存储值),生命周期独立于源 → 必须独立内核 fd 才能安全自关。
120+ 
121+---
122+ 
123+## 6. 机制三:`DupCopy`(拷贝选择性 dup)
124+ 
125+```cpp
126+bool WantParams::DupCopy(const WantParams &src, WantParams &out) {
127+ if (&out == &src) return out.TryDupAllFd();
128+ out = src; // 深拷贝共享整数
129+ if (!out.TryDupAllFd()) {
130+ out.RemoveAllFd(); // 剥离不关
131+ return false;
132+ }
133+ return true;
134+}
135+```
136+ 
137+拷贝(copy ctor)默认 baseline 深拷贝(共享整数);是否升级 dup **逐点分析**
138+ 
139+| 场景 | 副本与源关系 | 选择 | 关键 |
140+|---|---|---|---|
141+| 同步栈内只读视图 | 源覆盖副本 | **深拷贝 + 不关**(或按引用传) | 共享副本**禁止 CloseAllFd** |
142+| 路由/持久化/拦截器只取元数据 | 立即剥离 | **深拷贝 + `strip()`(RemoveAllFd)** | 剥离不关,源保留 |
143+| 存入长期容器(Mission/AbilityRecord/PendingWant/Session) | 副本寿命≥源 | **`DupCopy`** | 独立 fd,源可先关 |
144+| 异步任务/回调/Promise/跨线程 | 跨栈帧时序 | **`DupCopy`** | scope 管不到,须独立 fd |
145+| IPC 发送(WriteParcelable) | 内核为接收端 dup | **深拷贝(不必 dup)** | 发送端共享整数即可,内核隔离 |
146+| 交给会自行 close 的组件 | 所有权移交 | **`DupCopy`** | 避免对方关到源的 fd |
147+ 
148+> 判据:**生命周期/所有权是否发散**。同步同域可共享(但不可关);跨域/跨时序/要自关 → dup。
149+ 
150+---
151+ 
152+## 7. 异步:dup-to-independent,杜绝 double-close
153+ 
154+```cpp
155+void AmsStub::Handle(Want &want) { // IPC 接收,独占 fd
156+ WantFdScope scope(want.GetParams()); // sync 兜底(安全:独占)
157+ WantParams asyncOwned;
158+ if (!WantParams::DupCopy(want.GetParams(), asyncOwned)) { return; } // 独立 dup'd fd
159+ PostTask([asyncOwned = std::move(asyncOwned)]() mutable {
160+ Use(asyncOwned); // 独立内核 fd
161+ }); // 任务结束 → asyncOwned 析构 → CloseAllFd 关自己的
162+ // scope 关 stub 的;asyncOwned 关自己的;不同内核 fd → 无 double-close
163+}
164+```
165+ 
166+baseline 下这是**唯一**能安全跨异步的途径——共享整数副本跨异步必然 double-close(stub 先关 → 异步 stale)。
167+ 
168+---
169+ 
170+## 8. 与 baseline 的关系(不改 WantFdValue,因不复活)
171+ 
172+- **不改** FD 表示(`Integer::Box(fd)` marker + `fds_` 镜像)。
173+- **不改** legacy `operator=`/copy ctor(深拷贝共享,存量兼容)。
174+- **不改** `ReadFromParcelFD`(仍 `Integer::Box(fd)`,IPC 接收独占 fd)。
175+- **修** `DupAllFd`(fd-0 bug + all-or-nothing + `TryDupAllFd`)——属 baseline bug 修复,非新模型。
176+- **新增**(薄层):`WantFdScope``AssignDup``DupCopy`(基于 baseline `operator=`/`DupAllFd`/`CloseAllFd`/`RemoveAllFd` 组合)。
177+ 
178+即:Route 1 = baseline bug 修复 + 使用纪律 + 三个薄 API,不引入 WantFdValue/refcount。
179+ 
180+---
181+ 
182+## 9. AMS 试点 + 兼容性
183+ 
184+- **试点域**`services/abilitymgr/`。新 API 作为 WantParams 公开方法(加性、ABI 兼容)+ `WantFdScope` 新 header(经 `want_public_config` include 路径对 AMS 可见,pilot 期暂不登记 inner_kits)。
185+- **兼容**:legacy `operator=`/copy(深拷贝)不动;非 AMS、ability_runtime 其它服务、ability_base 既有调用点不动;AMS 逐点迁移,未迁点保留原手工 Close。
186+- **强制面**:AMS 内 FD-bearing 赋值禁用 legacy `operator=`、改用 `AssignDup`(manifest+review 守门)。
187+ 
188+---
189+ 
190+## 10. 与 manifest 衔接(23 点,见 want_fd_ams_manifest.md)
191+ 
192+manifest 枚举对 baseline 有效。Route 1 下的迁移决定:
193+ 
194+| 类别 | Route 1 决定 | 备注 |
195+|---|---|---|
196+| A. stub 末尾兜底 Close(9) | → `WantFdScope`(IPC 接收件独占 fd,安全) | 删手工 Close |
197+| B. 赋值(关旧+assign)(1) | → `AssignDup` | 删 CloseAllFd+operator= |
198+| C. 析构中 CloseAllFd(1) | → 候选删(B 迁移后 want_ 独占 fd,析构自关) | 须先 B |
199+| D. 业务剥离(8) | → 保留 `RemoveAllFd`/`strip()` | baseline RemoveAllFd 不关,安全 |
200+| E. 长期对象业务关闭(3) | → 入库改 `AssignDup` 修共享整数隐患,显式关保留 | **baseline 下 E 类 CloseAllFd 当前可能正在关共享 fd(bug),AssignDup 修复** |
201+| F. 待确认(1) | 核实是否独占 fd 后归入 A 或 D | |
202+ 
203+**前置**:先修 `DupAllFd`(fd-0 + all-or-nothing + `TryDupAllFd`),否则 AssignDup/DupCopy 无可靠基础。
204+ 
205+---
206+ 
207+## 11. 权衡与风险
208+ 
209+| 项 | Route 1 取舍 |
210+|---|---|
211+| 不引入 refcount | 简单,但失去「最后引用 close」兜底;独立性完全靠 dup 纪律 |
212+| dup 放大 fd 数 | selective dup(仅发散点)+ scope 即时回收;赋值 dup 有界(AMS 有限点) |
213+| 共享整数副本禁忌 CloseAllFd | 纪律强约束:共享副本只能 strip 或按引用;scope 只守独占持有者 |
214+| baseline fd-0 bug | 本方案前置修复 |
215+| E 类当前共享-close bug | AssignDup 入库修复 |
216+| dup 失败处理 | all-or-nothing + 失败 RemoveAllFd(不关),调用方显式 false |
217+| NAPI 裸 int | 不变(已知限制) |
218+ 
219+残留风险:
220+ 
221+- 共享整数副本误用 CloseAllFd 仍会 double-close——靠 review+manifest 守门,非类型级强制。
222+- dup 放大瞬时 fd(高并发启 ability)——需长跑 `/proc/<pid>/fd` 监控。
223+- AMS 内强制 `AssignDup` 靠 review,非类型级。
224+ 
225+---
226+ 
227+## 12. 落地阶段
228+ 
229+| 阶段 | 内容 | 放行 |
230+|---|---|---|
231+| P0 | 本设计冻结 + manifest(23 点,Route 1 分类) | 文档+清单 |
232+| P1 | 前置修 `DupAllFd`(fd-0+all-or-nothing+`TryDupAllFd`);落 `AssignDup`/`DupCopy`/`WantFdScope`(WantParams 公开方法 + 新 header) | 编译 + UT(DupAllFd fd-0、all-or-nothing 失败回滚、AssignDup 失败 RemoveAllFd、DupCopy、scope 各返回路径、async 无 double-close) |
233+| P2 | AMS 试点迁移:A→scope、B→AssignDup、E→入库 AssignDup,逐点删手工 Close | AMS UT + FD 长跑回基线 + fdsan 无 double-close |
234+| P3 | 评估推广 AppMS/其它 | 不在本轮 |
235+ 
236+---
237+ 
238+## 13. 决策记录
239+ 
240+- **Route 1 选定**:不复活 WantFdValue;baseline 模型上叠 scope+dup。理由:仓库已回退 baseline,且 dup-as-独立性 + scope-on-独占 纪律足以解遗漏与 double-close,无需 refcount 模型。
241+- **dup 是唯一独立性来源**:baseline 无 refcount,`dup()` 给不同内核 fd 是避免共享 double-close 的唯一手段。
242+- **scope 只守独占 fd**:IPC 接收件 / AssignDup 后副本。共享整数副本禁止 CloseAllFd(用 strip/RemoveAllFd)。
243+- **赋值必然 dup / 拷贝可选 dup**:赋值目标独立生命周期 → dup;拷贝按发散性逐点定。
244+- **失败回滚用 RemoveAllFd 不用 CloseAllFd**:baseline 共享整数下,CloseAllFd 会关到源的内核 fd。
245+- **前置修 DupAllFd**:fd-0 bug + all-or-nothing + TryDupAllFd 是 AssignDup/DupCopy 的可靠基础。