From b37134bb36bd8211dfd5f2c5509acc8708976a0b Mon Sep 17 00:00:00 2001 From: wyc <1104749580@qq.com> Date: Mon, 17 Aug 2026 17:46:58 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BF=AE=E5=A4=8Dwebserver=E6=AD=BB=E9=94=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 2 + mutex_error.ps1 | 113 ++++++++++++++++++++++++ web_server/app/Gallery_Plot_Session.cpp | 109 +++++++++++++---------- 3 files changed, 176 insertions(+), 48 deletions(-) create mode 100644 mutex_error.ps1 diff --git a/.gitignore b/.gitignore index 1bc30c5..72c7597 100644 --- a/.gitignore +++ b/.gitignore @@ -10,3 +10,5 @@ /output/ /webapp_gallery/.playwright-cli/ /logs/ +/deadlock_capture/ +/*.zip diff --git a/mutex_error.ps1 b/mutex_error.ps1 new file mode 100644 index 0000000..5ba4d09 --- /dev/null +++ b/mutex_error.ps1 @@ -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." \ No newline at end of file diff --git a/web_server/app/Gallery_Plot_Session.cpp b/web_server/app/Gallery_Plot_Session.cpp index ac52070..e4dbcf6 100644 --- a/web_server/app/Gallery_Plot_Session.cpp +++ b/web_server/app/Gallery_Plot_Session.cpp @@ -4,7 +4,6 @@ #include "common/Gallery_Scene_Interface.h" #include "render_2D/Gallery_Scene2D.h" #include "render_3D/Gallery_Scene3D.h" -#include #include #include #include @@ -204,24 +203,31 @@ struct Gallery_Plot_Session::Impl : std::enable_shared_from_thisset_client_metrics(performance); return true; } - [[nodiscard]] std::unique_lock acquire_foreground_lock() { - return std::unique_lock(mutex); + [[nodiscard]] std::unique_lock acquire_scene_lock() { + return std::unique_lock(scene_mutex); } - void disarm_automatic_render() { + void disarm_automatic_render_locked() { ++automatic_timer_revision; if (automatic_timer == trantor::InvalidTimerId) return; automatic_render_loop()->invalidateTimer(automatic_timer); 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) return; - disarm_automatic_render(); - if (!scene || !scene->can_render_automatically() || render_task_pending) + const bool can_render = scene && scene->can_render_automatically(); + const auto interval = can_render + ? std::chrono::nanoseconds(std::max(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; - const auto interval = std::chrono::nanoseconds( - std::max(1, scene->kernel_refresh_interval_ns())); const auto deadline = last_render_started ? *last_render_started + interval : Clock::now(); const auto delay = std::max(Clock::duration::zero(), deadline - Clock::now()); const std::uint64_t revision = automatic_timer_revision; @@ -235,46 +241,46 @@ struct Gallery_Plot_Session::Impl : std::enable_shared_from_thiscan_render_automatically() || render_task_pending) + if (!automatic_low_latency || automatic_render_running) return; - render_task_pending = true; + automatic_render_running = true; generation = scene_generation; } - auto self = shared_from_this(); - 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(); - } + run_automatic_render(generation); } void run_automatic_render(std::uint64_t generation) { const auto started = Clock::now(); try { - std::lock_guard lock(mutex); - if (generation == scene_generation && scene && scene->can_render_automatically()) { - const auto error = scene->render_latest_frame(); - if (error == Gallery_Render_Result::none) - last_render_started = started; + auto scene_lock = acquire_scene_lock(); + bool current_generation{}; + { + std::lock_guard lock(state_mutex); + current_generation = generation == scene_generation; } - render_task_pending = false; - arm_automatic_render_locked(); + if (current_generation && scene && scene->can_render_automatically()) { + 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 (...) { - std::lock_guard lock(mutex); - render_task_pending = false; + std::lock_guard lock(state_mutex); + automatic_render_running = false; automatic_render_exception = ::renderive::error::capture( "running automatic gallery render", std::current_exception()); - disarm_automatic_render(); + disarm_automatic_render_locked(); } } static bool affects_render_schedule(const Web_Event& event) { @@ -297,11 +303,12 @@ struct Gallery_Plot_Session::Impl : std::enable_shared_from_this scene; - std::mutex mutex; + std::mutex scene_mutex; + std::mutex state_mutex; std::optional last_render_started; std::uint64_t scene_generation{}; bool automatic_low_latency{}; - bool render_task_pending{}; + bool automatic_render_running{}; std::exception_ptr automatic_render_exception; std::uint64_t session_id{}; trantor::TimerId automatic_timer{trantor::InvalidTimerId}; @@ -317,9 +324,12 @@ struct Gallery_Plot_Session::Impl : std::enable_shared_from_thiscase_id, open->frame_mode, automatic_low_latency); - ++scene_generation; - last_render_started.reset(); - arm_automatic_render_locked(); + { + std::lock_guard lock(state_mutex); + ++scene_generation; + last_render_started.reset(); + } + arm_automatic_render(); return Web_Response{ Web_Response_Type::Json, 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_thiscase_id(); const auto mode = scene->frame_mode(); scene = make_gallery_scene(session_id, id, mode, automatic_low_latency); - ++scene_generation; - last_render_started.reset(); - arm_automatic_render_locked(); + { + std::lock_guard lock(state_mutex); + ++scene_generation; + last_render_started.reset(); + } + arm_automatic_render(); return Web_Response{ Web_Response_Type::Json, Gallery_Protocol::case_json_from_controls(id, scene->controls().dump(), @@ -428,7 +441,7 @@ struct Gallery_Plot_Session::Impl : std::enable_shared_from_this pixels; { - auto lock = acquire_foreground_lock(); + auto lock = acquire_scene_lock(); if (!scene) return std::nullopt; const auto encode_started = std::chrono::steady_clock::now(); @@ -446,7 +459,7 @@ struct Gallery_Plot_Session::Impl : std::enable_shared_from_this response; { - auto lock = acquire_foreground_lock(); + auto lock = acquire_scene_lock(); response = std::visit( [this](const auto& value) -> std::optional { using T = std::decay_t; @@ -484,7 +497,7 @@ struct Gallery_Plot_Session::Impl : std::enable_shared_from_this