Skip to content

refactor(server): 构造路径原子化 —— out-param 一次建立、单一移交点、错误码如实 - #475

Merged
adam-ikari merged 1 commit into
mainfrom
fix/server-construct-atomicity
Oct 13, 2026
Merged

adam-ikari merged 1 commit into
mainfrom
fix/server-construct-atomicity

Conversation

@adam-ikari

Copy link
Copy Markdown
Owner

v2.10 roadmap d5(构造/释放原子化)。对象是 create_simple_server_internal —— builder 构造入口 uvhttp_server_create / uvhttp_serve 的背后实现。

原来的形状

  • out-param *server = NULL 在 5 个分支里各自写一遍
  • 两个失败分支手工置 server->config/router = NULL —— 其中三处其实是无操作(那两项的所有权要到后面才移交),还要靠一条 Do NOT null them here 的注释防止后人"修好"唯一不能置空的分支
  • 一个只写不读的死字段 auto_cleanup

即 brain 记录的那条:正确性靠注释维护。

新形状

  1. out-param 不变式一次建立 —— 函数头部 *server = NULL,所有失败路径天然成立
  2. 单一移交点 —— server/router/config 三个子对象由局部变量持有到唯一 handover 点(builder->* 与 server->* 在同一处赋值)。此前每个失败分支只释放自己命名的对象,任何分支都不需要从 server 手里"取消拥有" —— 这正是那堆手工置 NULL 存在的根本原因
  3. builder 是视图不是所有者 —— 移交之后 uvhttp_server_free 一并释放三者;头文件写明该语义
  4. 错误码如实 —— listen 失败不再一律塌缩成 SERVER_LISTEN:NULL host 是调用方错误(INVALID_PARAM),端口占用是运行时错误(SERVER_LISTEN),两者修复动作不同
  5. 删除死字段 auto_cleanup(只写不读,命中「字段要么权威要么不存在」)

测试(两条不变式均经变异验证)

test_server_atomic_construct +3:

  • out-param 契约:以哨兵值 0x1 初始化 out-param,断言被写成 NULL。第一版用 nullptr 初始化,变异测试当场揭穿它没牙齿——函数压根不写 out-param,测试也绿。变异:删掉头部的 *server = NULL → 该用例红
  • listen 失败清理:占位端口制造 EADDRINUSE(与运行身份无关),断言 SERVER_LISTEN + out-param 为 NULL。变异:把清理改回旧形状(先置 NULL 再 free)→ ASan 抓到 config(56B) + router(120B) 泄漏
  • 视图语义:断言三个句柄在移交点指向同一批对象,并全程走 simple_free(若 builder 也拥有它们,此处即 double free)

验证

ctest 120/120(默认构建 + ASan 全量);cppcheck 零告警;test-validity 门禁 PASS;门禁口径内文件 clang-format 18.1.8 合规。

v2.10 roadmap d5(构造/释放原子化)。create_simple_server_internal(builder
构造入口)原来的形状:

- out-param `*server = NULL` 在 5 个分支里各自写一遍
- 两个失败分支手工置 `server->config/router = NULL`(其中三处是无操作——
  那两项的所有权要到后面才移交),还要靠一条 "Do NOT null them here" 注释
  防止后人"修好"唯一不能置空的分支
- 加上一个只写不读的死字段 `auto_cleanup`

## 新形状

1. **out-param 不变式一次建立**:函数头部 `*server = NULL`,所有失败路径天然
   成立,不再靠每个分支自觉
2. **单一移交点**:server/router/config 三个子对象由局部变量持有到唯一的
   handover 点(builder->server/router/config 与 server->config/router 在
   同一处赋值)。此前每个失败分支只释放自己命名的对象 —— 任何分支都不需要
   从 server 手里"取消拥有",这是那堆手工置 NULL 存在的根本原因
3. **builder 是视图不是所有者**:移交之后 server_free 一并释放三者,builder
   结构体只持句柄。头文件已写明这条语义
4. **错误码如实**:listen 失败不再一律塌缩成 SERVER_LISTEN —— NULL host 是
   调用方错误(INVALID_PARAM),端口占用是运行时错误(SERVER_LISTEN),两者
   的修复动作不同
5. 删除死字段 `auto_cleanup`(只写不读,命中"字段要么权威要么不存在")

## 测试

test_server_atomic_construct 新增 3 个用例,两条不变式均经变异验证:

- **out-param 契约**:用哨兵值 0x1 初始化 out-param 再断言被写成 NULL。
  (第一版用 nullptr 初始化,变异测试当场揭穿它没牙齿——函数压根不写,
  测试也绿。)
  变异:删掉头部的 `*server = NULL` → 该用例红
- **listen 失败清理**:占位端口制造 EADDRINUSE(与运行身份无关),断言返回
  SERVER_LISTEN、out-param 为 NULL。变异:把清理改回旧形状(先置 NULL 再
  free)→ ASan 抓到 config(56B) + router(120B) 泄漏
- **视图语义**:断言三个句柄在移交点指向同一批对象,并走 simple_free 全程
  (若 builder 也拥有它们,这里是 double free)

验证:ctest 120/120(默认构建 + ASan 全量);cppcheck 零告警;test-validity
门禁 PASS;门禁口径内文件 clang-format 18.1.8 合规。
@adam-ikari
adam-ikari merged commit ff65b74 into main Oct 13, 2026
10 checks passed
@adam-ikari
adam-ikari deleted the fix/server-construct-atomicity branch October 13, 2026 01:23
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant