1 Commits

Author SHA1 Message Date
psc b37134bb36 修复webserver死锁 2026-08-17 17:46:58 +08:00
3 changed files with 176 additions and 48 deletions
+2
View File
@@ -10,3 +10,5 @@
/output/ /output/
/webapp_gallery/.playwright-cli/ /webapp_gallery/.playwright-cli/
/logs/ /logs/
/deadlock_capture/
/*.zip
+113
View File
@@ -0,0 +1,113 @@
$ErrorActionPreference = "Stop"
$targetExe = "D:\ae\proj\Renderive\cmake-build-vs2022_debug\web_server\Renderive_Web_Server.exe"
$cdb = "D:\ae\ewdk\EWDK_22621_230929-1800\Program Files\Windows Kits\10\Debuggers\x64\cdb.exe"
$outputRoot = "D:\ae\proj\Renderive\deadlock_capture"
if (-not (Test-Path -LiteralPath $cdb))
{
throw "CDB not found: $cdb"
}
if (-not (Test-Path -LiteralPath $targetExe))
{
throw "Target executable not found: $targetExe"
}
$processes = @(
Get-CimInstance Win32_Process |
Where-Object { $_.ExecutablePath -eq $targetExe }
)
if ($processes.Count -eq 0)
{
throw "Renderive_Web_Server.exe is not running: $targetExe"
}
if ($processes.Count -ne 1)
{
$processes |
Select-Object ProcessId, Name, ExecutablePath |
Format-Table -AutoSize
throw "Multiple matching processes found. Stop the extra processes first."
}
$pidValue = [int]$processes[0].ProcessId
$timestamp = Get-Date -Format "yyyyMMdd_HHmmss"
$outputDir = Join-Path $outputRoot $timestamp
$logPath = Join-Path $outputDir "Renderive_deadlock_$timestamp.txt"
$dumpPath = Join-Path $outputDir "Renderive_deadlock_$timestamp.dmp"
$commandPath = Join-Path $outputDir "cdb_commands.txt"
New-Item -ItemType Directory -Force -Path $outputDir | Out-Null
$cdbCommands = @"
.echo ============================================================
.echo Renderive deadlock capture
.echo ============================================================
.echo === PROCESS ===
|
.echo === ALL THREAD STACKS ===
~* kb
.echo === THREAD CPU TIME ===
!runaway 3
.echo === LOCKS ===
!locks
.echo === HANG ANALYSIS ===
!analyze -hang
.echo === FULL MEMORY DUMP ===
.dump /ma $dumpPath
.echo === CAPTURE FINISHED ===
q
"@
Set-Content -LiteralPath $commandPath -Value $cdbCommands -Encoding ASCII
Write-Host ""
Write-Host "Target:"
Write-Host " $targetExe"
Write-Host ""
Write-Host "PID:"
Write-Host " $pidValue"
Write-Host ""
Write-Host "Output:"
Write-Host " $outputDir"
Write-Host ""
Write-Host "Attaching CDB non-invasively..."
Write-Host ""
$cdbArgs = @(
"-pv"
"-p"
$pidValue.ToString()
"-logo"
$logPath
"-cf"
$commandPath
)
& $cdb @cdbArgs
$cdbExitCode = $LASTEXITCODE
Write-Host ""
Write-Host "CDB exit code: $cdbExitCode"
Write-Host ""
if (Test-Path -LiteralPath $logPath)
{
Write-Host "Log:"
Write-Host " $logPath"
}
if (Test-Path -LiteralPath $dumpPath)
{
$dump = Get-Item -LiteralPath $dumpPath
Write-Host ""
Write-Host "Dump:"
Write-Host " $dumpPath"
Write-Host " Size: $([math]::Round($dump.Length / 1MB, 2) ) MB"
}
Write-Host ""
Write-Host "Capture complete."
+61 -48
View File
@@ -4,7 +4,6 @@
#include "common/Gallery_Scene_Interface.h" #include "common/Gallery_Scene_Interface.h"
#include "render_2D/Gallery_Scene2D.h" #include "render_2D/Gallery_Scene2D.h"
#include "render_3D/Gallery_Scene3D.h" #include "render_3D/Gallery_Scene3D.h"
#include <renderive/scheduling/Scheduler.hpp>
#include <trantor/net/EventLoopThread.h> #include <trantor/net/EventLoopThread.h>
#include <nlohmann/json.hpp> #include <nlohmann/json.hpp>
#include <algorithm> #include <algorithm>
@@ -204,24 +203,31 @@ struct Gallery_Plot_Session::Impl : std::enable_shared_from_this<Gallery_Plot_Se
scene->set_client_metrics(performance); scene->set_client_metrics(performance);
return true; return true;
} }
[[nodiscard]] std::unique_lock<std::mutex> acquire_foreground_lock() { [[nodiscard]] std::unique_lock<std::mutex> acquire_scene_lock() {
return std::unique_lock<std::mutex>(mutex); return std::unique_lock<std::mutex>(scene_mutex);
} }
void disarm_automatic_render() { void disarm_automatic_render_locked() {
++automatic_timer_revision; ++automatic_timer_revision;
if (automatic_timer == trantor::InvalidTimerId) if (automatic_timer == trantor::InvalidTimerId)
return; return;
automatic_render_loop()->invalidateTimer(automatic_timer); automatic_render_loop()->invalidateTimer(automatic_timer);
automatic_timer = trantor::InvalidTimerId; automatic_timer = trantor::InvalidTimerId;
} }
void arm_automatic_render_locked() { void disarm_automatic_render() {
std::lock_guard lock(state_mutex);
disarm_automatic_render_locked();
}
void arm_automatic_render() {
if (!automatic_low_latency) if (!automatic_low_latency)
return; return;
disarm_automatic_render(); const bool can_render = scene && scene->can_render_automatically();
if (!scene || !scene->can_render_automatically() || render_task_pending) const auto interval = can_render
? std::chrono::nanoseconds(std::max<std::uint64_t>(1, scene->kernel_refresh_interval_ns()))
: std::chrono::nanoseconds::zero();
std::lock_guard lock(state_mutex);
disarm_automatic_render_locked();
if (!can_render || automatic_render_running)
return; return;
const auto interval = std::chrono::nanoseconds(
std::max<std::uint64_t>(1, scene->kernel_refresh_interval_ns()));
const auto deadline = last_render_started ? *last_render_started + interval : Clock::now(); const auto deadline = last_render_started ? *last_render_started + interval : Clock::now();
const auto delay = std::max(Clock::duration::zero(), deadline - Clock::now()); const auto delay = std::max(Clock::duration::zero(), deadline - Clock::now());
const std::uint64_t revision = automatic_timer_revision; const std::uint64_t revision = automatic_timer_revision;
@@ -235,46 +241,46 @@ struct Gallery_Plot_Session::Impl : std::enable_shared_from_this<Gallery_Plot_Se
void automatic_render_due(std::uint64_t revision) { void automatic_render_due(std::uint64_t revision) {
std::uint64_t generation{}; std::uint64_t generation{};
{ {
std::lock_guard lock(mutex); std::lock_guard lock(state_mutex);
if (revision != automatic_timer_revision) if (revision != automatic_timer_revision)
return; return;
automatic_timer = trantor::InvalidTimerId; automatic_timer = trantor::InvalidTimerId;
++automatic_timer_revision; ++automatic_timer_revision;
if (!automatic_low_latency || !scene || !scene->can_render_automatically() || render_task_pending) if (!automatic_low_latency || automatic_render_running)
return; return;
render_task_pending = true; automatic_render_running = true;
generation = scene_generation; generation = scene_generation;
} }
auto self = shared_from_this(); run_automatic_render(generation);
try {
renderive::scheduling::enqueue_task([self = std::move(self), generation] {
self->run_automatic_render(generation);
});
} catch (...) {
std::lock_guard lock(mutex);
render_task_pending = false;
automatic_render_exception = ::renderive::error::capture(
"arming automatic gallery render", std::current_exception());
disarm_automatic_render();
}
} }
void run_automatic_render(std::uint64_t generation) { void run_automatic_render(std::uint64_t generation) {
const auto started = Clock::now(); const auto started = Clock::now();
try { try {
std::lock_guard lock(mutex); auto scene_lock = acquire_scene_lock();
if (generation == scene_generation && scene && scene->can_render_automatically()) { bool current_generation{};
const auto error = scene->render_latest_frame(); {
if (error == Gallery_Render_Result::none) std::lock_guard lock(state_mutex);
last_render_started = started; current_generation = generation == scene_generation;
} }
render_task_pending = false; if (current_generation && scene && scene->can_render_automatically()) {
arm_automatic_render_locked(); const auto error = scene->render_latest_frame();
if (error == Gallery_Render_Result::none) {
std::lock_guard lock(state_mutex);
if (generation == scene_generation)
last_render_started = started;
}
}
{
std::lock_guard lock(state_mutex);
automatic_render_running = false;
}
arm_automatic_render();
} catch (...) { } catch (...) {
std::lock_guard lock(mutex); std::lock_guard lock(state_mutex);
render_task_pending = false; automatic_render_running = false;
automatic_render_exception = ::renderive::error::capture( automatic_render_exception = ::renderive::error::capture(
"running automatic gallery render", std::current_exception()); "running automatic gallery render", std::current_exception());
disarm_automatic_render(); disarm_automatic_render_locked();
} }
} }
static bool affects_render_schedule(const Web_Event& event) { static bool affects_render_schedule(const Web_Event& event) {
@@ -297,11 +303,12 @@ struct Gallery_Plot_Session::Impl : std::enable_shared_from_this<Gallery_Plot_Se
event); event);
} }
std::unique_ptr<Gallery_Scene_Interface> scene; std::unique_ptr<Gallery_Scene_Interface> scene;
std::mutex mutex; std::mutex scene_mutex;
std::mutex state_mutex;
std::optional<Clock::time_point> last_render_started; std::optional<Clock::time_point> last_render_started;
std::uint64_t scene_generation{}; std::uint64_t scene_generation{};
bool automatic_low_latency{}; bool automatic_low_latency{};
bool render_task_pending{}; bool automatic_render_running{};
std::exception_ptr automatic_render_exception; std::exception_ptr automatic_render_exception;
std::uint64_t session_id{}; std::uint64_t session_id{};
trantor::TimerId automatic_timer{trantor::InvalidTimerId}; trantor::TimerId automatic_timer{trantor::InvalidTimerId};
@@ -317,9 +324,12 @@ struct Gallery_Plot_Session::Impl : std::enable_shared_from_this<Gallery_Plot_Se
Gallery_Protocol::error_json("未知的 gallery case 或 frame_mode", "case") Gallery_Protocol::error_json("未知的 gallery case 或 frame_mode", "case")
}; };
scene = make_gallery_scene(session_id, open->case_id, open->frame_mode, automatic_low_latency); scene = make_gallery_scene(session_id, open->case_id, open->frame_mode, automatic_low_latency);
++scene_generation; {
last_render_started.reset(); std::lock_guard lock(state_mutex);
arm_automatic_render_locked(); ++scene_generation;
last_render_started.reset();
}
arm_automatic_render();
return Web_Response{ return Web_Response{
Web_Response_Type::Json, Web_Response_Type::Json,
Gallery_Protocol::case_json_from_controls(scene->case_id(), scene->controls().dump(), Gallery_Protocol::case_json_from_controls(scene->case_id(), scene->controls().dump(),
@@ -335,7 +345,7 @@ struct Gallery_Plot_Session::Impl : std::enable_shared_from_this<Gallery_Plot_Se
}; };
if (request.kind == Gallery_Request_Kind::Observe) { if (request.kind == Gallery_Request_Kind::Observe) {
if (update_client_metrics(request.message)) if (update_client_metrics(request.message))
arm_automatic_render_locked(); arm_automatic_render();
return Web_Response{ return Web_Response{
Web_Response_Type::Json, Web_Response_Type::Json,
Gallery_Protocol::observer_json( Gallery_Protocol::observer_json(
@@ -344,7 +354,7 @@ struct Gallery_Plot_Session::Impl : std::enable_shared_from_this<Gallery_Plot_Se
} }
if (request.kind == Gallery_Request_Kind::Refresh) { if (request.kind == Gallery_Request_Kind::Refresh) {
if (update_client_metrics(request.message)) if (update_client_metrics(request.message))
arm_automatic_render_locked(); arm_automatic_render();
return Web_Response{ return Web_Response{
Web_Response_Type::Json, Web_Response_Type::Json,
Gallery_Protocol::case_json_from_controls( Gallery_Protocol::case_json_from_controls(
@@ -398,9 +408,12 @@ struct Gallery_Plot_Session::Impl : std::enable_shared_from_this<Gallery_Plot_Se
const std::string id = scene->case_id(); const std::string id = scene->case_id();
const auto mode = scene->frame_mode(); const auto mode = scene->frame_mode();
scene = make_gallery_scene(session_id, id, mode, automatic_low_latency); scene = make_gallery_scene(session_id, id, mode, automatic_low_latency);
++scene_generation; {
last_render_started.reset(); std::lock_guard lock(state_mutex);
arm_automatic_render_locked(); ++scene_generation;
last_render_started.reset();
}
arm_automatic_render();
return Web_Response{ return Web_Response{
Web_Response_Type::Json, Web_Response_Type::Json,
Gallery_Protocol::case_json_from_controls(id, scene->controls().dump(), Gallery_Protocol::case_json_from_controls(id, scene->controls().dump(),
@@ -428,7 +441,7 @@ struct Gallery_Plot_Session::Impl : std::enable_shared_from_this<Gallery_Plot_Se
const auto request_started = std::chrono::steady_clock::now(); const auto request_started = std::chrono::steady_clock::now();
std::optional<std::string> pixels; std::optional<std::string> pixels;
{ {
auto lock = acquire_foreground_lock(); auto lock = acquire_scene_lock();
if (!scene) if (!scene)
return std::nullopt; return std::nullopt;
const auto encode_started = std::chrono::steady_clock::now(); const auto encode_started = std::chrono::steady_clock::now();
@@ -446,7 +459,7 @@ struct Gallery_Plot_Session::Impl : std::enable_shared_from_this<Gallery_Plot_Se
{ {
std::exception_ptr exception; std::exception_ptr exception;
{ {
std::lock_guard lock(mutex); std::lock_guard lock(state_mutex);
exception = std::exchange(automatic_render_exception, {}); exception = std::exchange(automatic_render_exception, {});
} }
if (exception) if (exception)
@@ -458,7 +471,7 @@ struct Gallery_Plot_Session::Impl : std::enable_shared_from_this<Gallery_Plot_Se
const bool reschedule = affects_render_schedule(event); const bool reschedule = affects_render_schedule(event);
std::optional<Web_Response> response; std::optional<Web_Response> response;
{ {
auto lock = acquire_foreground_lock(); auto lock = acquire_scene_lock();
response = std::visit( response = std::visit(
[this](const auto& value) -> std::optional<Web_Response> { [this](const auto& value) -> std::optional<Web_Response> {
using T = std::decay_t<decltype(value)>; using T = std::decay_t<decltype(value)>;
@@ -484,7 +497,7 @@ struct Gallery_Plot_Session::Impl : std::enable_shared_from_this<Gallery_Plot_Se
}, },
event); event);
if (reschedule) if (reschedule)
arm_automatic_render_locked(); arm_automatic_render();
} }
return response; return response;
} }