Conversation
根因分析: 1. 僵尸广播(zombie broadcast):延迟到达的 ABR HTTP 请求在 session 结束后 触发 change_dynamic_param_for_client(),该函数调用 broadcast_shared.ref() 创建了一个临时的广播上下文,随后 end_broadcast() 中的 control_thread.join() 阻塞了 HTTPS handler 线程,导致 HTTPS 服务器 无法接受新连接,客户端显示主机离线。 2. VDD 模式下初始拓扑错误:apply_config 在 VDD 模式下调用 get_current_topology_metadata() 获取的是 VDD 启用后的当前拓扑 (只有 VDD 设备),忽略了 pre_saved_initial_topology(物理显示器拓扑)。 恢复时 remove_vdd_from_topology() 将初始拓扑清空,物理显示器无法 被重新启用。 修复: - stream.cpp: 在 change_dynamic_param_for_client 中添加 has_ref() 前置 检查,无活跃 session 时不触发僵尸广播 - settings.cpp: VDD 模式下使用 pre_saved_initial_topology 替换 get_current_topology_metadata 返回的 VDD-only 初始拓扑
Summary by CodeRabbitBug Fixes
概述两个模块添加了防御性检查以优化特定场景。VDD模式中的拓扑处理现在有条件地恢复预存的初始拓扑元数据,防止物理显示器配置丢失。流广播代码新增早期退出检查,避免在无活跃会话时进行不必要处理。 变更
代码审查工作量评估🎯 2 (简单) | ⏱️ ~10 分钟 🚥 Pre-merge checks | ✅ 2 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Comment |
There was a problem hiding this comment.
🧹 Nitpick comments (1)
src/stream.cpp (1)
3055-3060: TOCTOU 竞态条件仍存在,虽改善常见场景但未完全消除问题此修复有效改善了主要问题:在明确没有活跃引用时避免触发僵尸广播。然而,
has_ref()和ref()各自独立加锁,存在竞态窗口:线程 A 调用has_ref()看到_count > 0后,线程 B 完全释放引用(_count = 0,调用end_broadcast),随后线程 A 调用ref()时_count == 0,会再次调用_construct()(即start_broadcast),重新触发启停周期。虽然此竞态窗口很小且实际触发概率低(特别是客户端发送动态参数变更时通常有活跃会话),但从线程安全角度讲仍不完美。建议在
shared_t类中添加原子的try_ref()方法,仅当已有活跃引用时才增加计数,这样可完全消除竞态条件。🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/stream.cpp` around lines 3055 - 3060, The current check using broadcast_shared.has_ref() followed by broadcast_shared.ref() has a TOCTOU race where has_ref() and ref() lock separately allowing _count to drop to zero between calls and re-trigger start_broadcast/end_broadcast; add an atomic try_ref() method to shared_t that increments the reference count only if it is already >0 and returns success/failure, then replace the has_ref()+ref() pattern in the stream logic (where broadcast_shared is used) with a single call to try_ref() to ensure no new reference is created when the count reached zero and eliminate the race that recreates the broadcast cycle.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Nitpick comments:
In `@src/stream.cpp`:
- Around line 3055-3060: The current check using broadcast_shared.has_ref()
followed by broadcast_shared.ref() has a TOCTOU race where has_ref() and ref()
lock separately allowing _count to drop to zero between calls and re-trigger
start_broadcast/end_broadcast; add an atomic try_ref() method to shared_t that
increments the reference count only if it is already >0 and returns
success/failure, then replace the has_ref()+ref() pattern in the stream logic
(where broadcast_shared is used) with a single call to try_ref() to ensure no
new reference is created when the count reached zero and eliminate the race that
recreates the broadcast cycle.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: f2d78b1b-3999-4b19-9d0b-f52e7bf91065
📒 Files selected for processing (2)
src/platform/windows/display_device/settings.cppsrc/stream.cpp
📜 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 (2)
src/**/*.{cpp,c,h}
⚙️ CodeRabbit configuration file
src/**/*.{cpp,c,h}: Sunshine 核心 C++ 源码,自托管游戏串流服务器。审查要点:内存安全、 线程安全、RAII 资源管理、安全漏洞。注意预处理宏控制的平台相关代码。
Files:
src/platform/windows/display_device/settings.cppsrc/stream.cpp
src/platform/**
⚙️ CodeRabbit configuration file
src/platform/**: 平台抽象层代码(Windows/Linux/macOS)。确保各平台实现一致, 注意 Windows API 调用的错误处理和资源释放。
Files:
src/platform/windows/display_device/settings.cpp
🔇 Additional comments (1)
src/platform/windows/display_device/settings.cpp (1)
797-805: VDD 初始拓扑覆盖逻辑实现正确。这里在
pre_saved_initial_topology有效且非空时覆盖pair.initial,可避免将 VDD-only 拓扑误持久化为初始拓扑,修复恢复阶段物理显示器无法重新启用的问题。
根因分析:
僵尸广播(zombie broadcast):延迟到达的 ABR HTTP 请求在 session 结束后 触发 change_dynamic_param_for_client(),该函数调用 broadcast_shared.ref() 创建了一个临时的广播上下文,随后 end_broadcast() 中的 control_thread.join() 阻塞了 HTTPS handler 线程,导致 HTTPS 服务器 无法接受新连接,客户端显示主机离线。
VDD 模式下初始拓扑错误:apply_config 在 VDD 模式下调用 get_current_topology_metadata() 获取的是 VDD 启用后的当前拓扑 (只有 VDD 设备),忽略了 pre_saved_initial_topology(物理显示器拓扑)。 恢复时 remove_vdd_from_topology() 将初始拓扑清空,物理显示器无法 被重新启用。
修复: