Skip to content

feat: 解析 VDD 驱动管道新响应格式,优化创建等待#587

Open
ShadowLemoon wants to merge 2 commits intomasterfrom
fix/vdd-create-retries
Open

feat: 解析 VDD 驱动管道新响应格式,优化创建等待#587
ShadowLemoon wants to merge 2 commits intomasterfrom
fix/vdd-create-retries

Conversation

@ShadowLemoon
Copy link
Copy Markdown
Collaborator

背景

配合 ZakoVDD 驱动端修改(CREATEMONITOR 命令现在返回 CCD 就绪状态),更新 Sunshine 管道通信层以利用新响应。

修改

vdd_utils.h / vdd_utils.cpp:

  1. execute_pipe_command 新增 read_timeout_ms 参数(默认 0 = 使用 kPipeTimeoutMs),允许调用者指定自定义读取超时
  2. create_vdd_monitor 使用 20 秒读取超时(kCreateMonitorReadTimeoutMs),适配驱动端最多 15 秒的 CCD 等待
  3. 解析新响应格式:
    • OK:驱动确认 CCD 已就绪,后续 wait_for_vdd_device 会立即成功
    • OK_PENDING:创建成功但 CCD 未就绪,走原有等待逻辑
    • FAIL:创建失败,create_vdd_monitor 返回 false,触发 try_recover_vdd_device 重试
    • 空响应/超时:旧版驱动兼容,行为不变

向后兼容

  • 旧版驱动不回复管道响应 → read_timed_out=true → 与之前行为一致
  • 读超时从 3s 增大到 20s 仅影响 CREATEMONITOR 命令,其他命令不受影响

@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Apr 8, 2026

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 18ae4aa8-f51e-43b7-b69f-18398d23507a

📥 Commits

Reviewing files that changed from the base of the PR and between 4dbabbd and c9364cc.

📒 Files selected for processing (1)
  • src/display_device/session.cpp
📜 Recent review details
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: Windows
🧰 Additional context used
📓 Path-based instructions (1)
src/**/*.{cpp,c,h}

⚙️ CodeRabbit configuration file

src/**/*.{cpp,c,h}: Sunshine 核心 C++ 源码,自托管游戏串流服务器。审查要点:内存安全、 线程安全、RAII 资源管理、安全漏洞。注意预处理宏控制的平台相关代码。

Files:

  • src/display_device/session.cpp
🔇 Additional comments (2)
src/display_device/session.cpp (2)

512-522: LGTM!

此更改正确处理了驱动新响应格式。失败时调用 try_recover_vdd_device 进行恢复,恢复仍失败则执行 disable_enable_vdd 重置后返回。

轻微冗余:当初始创建失败但恢复成功时,device_zako 已由 try_recover_vdd_device 填充,后续第 526 行的 wait_for_vdd_device 调用会立即成功。这不影响正确性,仅为小幅效率损失。


634-643: LGTM!

正确检测创建失败并跳过无意义的等待循环。与 prepare_vdd 不同,无头模式下创建失败不进行恢复尝试,这是合理的设计选择——无头自动创建是会话结束时的辅助功能,失败后静默跳过不影响核心流程。


Summary by CodeRabbit

  • 缺陷修复

    • 修复并强化了监视器创建失败时的处理逻辑:失败会记录错误并尝试恢复,避免进入无效重试循环。
    • 改进了驱动响应的识别:明确区分失败、成功与待处理状态以优化错误报告。
  • 改进

    • 增强了管道读取超时控制,提升在不同硬件和旧驱动下的稳定性和可观测性。

Walkthrough

扩展 execute_pipe_command 支持传入读超时参数,create_vdd_monitor 使用新的 20000ms 读超时并增强对驱动响应(FAIL/OK/OK_PENDING)的解析;调用方(session)在创建失败时新增错误处理与恢复路径。

Changes

Cohort / File(s) Summary
管道超时与实现变更
src/display_device/vdd_utils.h, src/display_device/vdd_utils.cpp
execute_pipe_command 签名添加参数 DWORD read_timeout_ms = 0,实现中使用 effective_read_timeout = (read_timeout_ms > 0 ? read_timeout_ms : kPipeTimeoutMs);为 CREATEMONITOR 读取使用 constexpr DWORD kCreateMonitorReadTimeoutMs = 20000
驱动响应解析与行为调整
src/display_device/vdd_utils.cpp
create_vdd_monitor 增加对驱动返回值的解析:遇到 "FAIL" 返回 false 并记录错误,"OK"/"OK_PENDING" 记录相应日志,其他响应保留原有处理并兼容旧驱动超时行为。
调用方错误处理改动
src/display_device/session.cpp
session_t::prepare_vdd 与无头自动创建流程在调用 create_vdd_monitor 后新增失败检测与恢复逻辑(包括调用 try_recover_vdd_devicedisable_enable_vdd),并仅在创建成功时执行原先的短暂停顿或重试行为。

Sequence Diagram(s)

sequenceDiagram
    participant Session as Session (调用者)
    participant VDD as vdd_utils::create_vdd_monitor
    participant Driver as VDD Driver (NamedPipe)

    Session->>VDD: request create_vdd_monitor(pipe, ...)
    VDD->>Driver: write CREATEMONITOR command (over pipe)
    Driver-->>VDD: response ("OK", "OK_PENDING", "FAIL", or other) or timeout
    alt response == "FAIL"
        VDD-->>Session: return false (log error)
        Session->>Session: try_recover_vdd_device() / disable_enable_vdd()
    else response == "OK" or "OK_PENDING"
        VDD-->>Session: return true (log success/pending)
        Session->>Session: continue normal flow (sleep/restore)
    else timeout or other
        VDD-->>Session: handle as legacy/other response (may treat timeout as success)
        Session->>Session: follow legacy handling or additional checks
    end
Loading

预估代码审查工作量

🎯 4 (Complex) | ⏱️ ~45 分钟

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed 标题准确总结了PR的核心改动:解析VDD驱动管道新响应格式并优化创建等待超时,与changeset主要内容相符。
Description check ✅ Passed 描述详细阐述了背景、修改点、新响应格式处理和向后兼容性考虑,与changeset内容充分相关。

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/vdd-create-retries

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (2)
src/display_device/vdd_utils.cpp (2)

359-362: 日志消息中 return 标签含义不清。

此处记录的是 read_timed_out 变量,但日志显示 [return=...],可能造成维护时的困惑。建议改为更直观的标签名:

📝 建议修改
-        BOOST_LOG(info) << "创建虚拟显示器完成,响应: " << response << " [return=" << (read_timed_out ? 1 : 0) << "]";
+        BOOST_LOG(info) << "创建虚拟显示器完成,响应: " << response << " [read_timed_out=" << (read_timed_out ? 1 : 0) << "]";
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/display_device/vdd_utils.cpp` around lines 359 - 362, The log message in
the else branch that prints response uses an ambiguous label "[return=...]"
while actually showing the boolean variable read_timed_out; update the log to
use a clear label (e.g., "[read_timed_out=...]" or "[timed_out=...]") so it
accurately reflects the variable, by modifying the BOOST_LOG(info) statement
that prints response and read_timed_out in vdd_utils.cpp (the branch that treats
old drivers as successful).

45-45: 静态变量 last_used_client_uuid 缺少线程同步。

该静态变量在 create_vdd_monitor 中被读写(行 282、321-323),但没有同步保护。如果存在多线程调用场景,可能导致数据竞争。

如果确认此函数仅在单线程上下文中调用,可忽略此建议。否则建议添加 std::mutex 保护或使用 std::atomic / thread_local

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/display_device/vdd_utils.cpp` at line 45, The static variable
last_used_client_uuid is accessed from create_vdd_monitor and lacks
synchronization; protect it by either making it thread_local (if each thread
should have its own value) or adding a mutex (e.g., declare a static std::mutex
last_used_client_uuid_mtx) and lock_guard it around every read/write of
last_used_client_uuid inside create_vdd_monitor (and any other accessors) to
eliminate data races; ensure both reads (checks) and writes (updates) use the
same synchronization primitive.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/display_device/vdd_utils.cpp`:
- Around line 348-351: create_vdd_monitor now returns false on driver "FAIL" but
two callers in session.cpp (the call before wait_for_vdd_device() and the
headless-host creation call before is_display_on() polling) ignore the return;
update both call sites to check the boolean result from
vdd_utils::create_vdd_monitor(...) and handle failures immediately (e.g., log an
error via BOOST_LOG/error with context, perform any needed cleanup, and
return/propagate failure instead of continuing to rely solely on
wait_for_vdd_device() or is_display_on() polling). Ensure you reference the
existing call sites that invoke create_vdd_monitor, and keep the existing
subsequent polling logic as a fallback only after the create_vdd_monitor success
check.

---

Nitpick comments:
In `@src/display_device/vdd_utils.cpp`:
- Around line 359-362: The log message in the else branch that prints response
uses an ambiguous label "[return=...]" while actually showing the boolean
variable read_timed_out; update the log to use a clear label (e.g.,
"[read_timed_out=...]" or "[timed_out=...]") so it accurately reflects the
variable, by modifying the BOOST_LOG(info) statement that prints response and
read_timed_out in vdd_utils.cpp (the branch that treats old drivers as
successful).
- Line 45: The static variable last_used_client_uuid is accessed from
create_vdd_monitor and lacks synchronization; protect it by either making it
thread_local (if each thread should have its own value) or adding a mutex (e.g.,
declare a static std::mutex last_used_client_uuid_mtx) and lock_guard it around
every read/write of last_used_client_uuid inside create_vdd_monitor (and any
other accessors) to eliminate data races; ensure both reads (checks) and writes
(updates) use the same synchronization primitive.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 031eca75-351d-488c-8d5d-824468a5620b

📥 Commits

Reviewing files that changed from the base of the PR and between 33bc20d and 4dbabbd.

📒 Files selected for processing (2)
  • src/display_device/vdd_utils.cpp
  • src/display_device/vdd_utils.h
📜 Review details
🧰 Additional context used
📓 Path-based instructions (1)
src/**/*.{cpp,c,h}

⚙️ CodeRabbit configuration file

src/**/*.{cpp,c,h}: Sunshine 核心 C++ 源码,自托管游戏串流服务器。审查要点:内存安全、 线程安全、RAII 资源管理、安全漏洞。注意预处理宏控制的平台相关代码。

Files:

  • src/display_device/vdd_utils.h
  • src/display_device/vdd_utils.cpp
🔇 Additional comments (4)
src/display_device/vdd_utils.h (1)

69-70: API 变更设计合理。

新增 read_timeout_ms 参数使用默认值 0,保证了向后兼容性。现有调用方无需修改即可保持原有行为。

src/display_device/vdd_utils.cpp (3)

143-144: LGTM!超时参数实现正确。

新增 read_timeout_ms 参数与头文件声明一致,配合 effective_read_timeout 计算逻辑,允许调用方按需定制读取超时。


326-329: 超时设置合理。

根据 PR 描述,驱动端 CCD 准备最多需要 15 秒,20 秒超时提供了适当的缓冲。仅针对 CREATEMONITOR 命令使用此超时,不影响其他管道命令。


346-362: 响应字符串比较可能因 null 字符而失败(需确认驱动行为)。

responsestd::string(buffer, bytesRead) 构造(第 196-197、206-207 行),该构造函数会取 buffer 中恰好 bytesRead 个字符,包括任何嵌入的 null 字符。如果 ZakoVDD 驱动在响应中发送 null 终止符(如 "OK\0" 计为 3 字节),则 bytesRead=3,response 将包含长度为 3 的字符串,导致第 353、356 行的比较 response == "OK" 失败。

代码结构(ReadFile 预留一个字节、之后手动添加 null)表明设计上预期接收非 null 终止的原始数据。若驱动遵循此协议,当前代码正确;若驱动包含 null 字节在长度计数内,则需要修复。

建议在字符串比较前添加 null 及尾部空白字符的清理,或采用 starts_with() 等更宽松的匹配方式,以增强鲁棒性。

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