From 47cf1af7a51bd90a0d1ddf16601ffad35a354a3e Mon Sep 17 00:00:00 2001 From: wyc <1104749580@qq.com> Date: Mon, 17 Aug 2026 19:43:37 +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 | 1 + .../src/renderive/scene/base/Scene_Base.cpp | 158 +++++++++++++++++- debug.ps1 | 113 +++++++++++++ web_server/app/Gallery_Plot_Session.cpp | 58 ++++++- 4 files changed, 326 insertions(+), 4 deletions(-) create mode 100644 debug.ps1 diff --git a/.gitignore b/.gitignore index 1bc30c5..58fbd2d 100644 --- a/.gitignore +++ b/.gitignore @@ -10,3 +10,4 @@ /output/ /webapp_gallery/.playwright-cli/ /logs/ +/deadlock_capture/ diff --git a/Kernel/src/renderive/scene/base/Scene_Base.cpp b/Kernel/src/renderive/scene/base/Scene_Base.cpp index dac678b..b22a4eb 100644 --- a/Kernel/src/renderive/scene/base/Scene_Base.cpp +++ b/Kernel/src/renderive/scene/base/Scene_Base.cpp @@ -3,12 +3,17 @@ #include "Scene_Base.inl" #include #include +#include +#include #include #include #include #include #include #include +#include +#include +#include #include #include #include @@ -58,6 +63,33 @@ Scene_Render_Result scene_render_error( } ::renderive::error::unexpected("unknown render graph execution error"); } +bool scene_render_trace_enabled() { + static const bool enabled = [] { + const char* value = std::getenv("RENDERIVE_SCENE_RENDER_TRACE"); + return value && std::string_view(value) == "1"; + }(); + return enabled; +} +template +void scene_render_trace(Writer&& writer) { + if (!scene_render_trace_enabled()) return; + const auto now = std::chrono::duration_cast( + std::chrono::steady_clock::now().time_since_epoch()).count(); + const auto& runtime = renderive::scheduling::detail::OneTBB_Runtime::instance(); + const auto worker_id = runtime.current_worker_id(); + const auto statistics = runtime.statistics(); + std::osyncstream output(std::cerr); + output << "[renderive.scene.trace] time_ns=" << now + << " thread=" << std::this_thread::get_id() + << " arena_index="; + if (worker_id == std::numeric_limits::max()) output << "external"; + else output << worker_id; + output << " active_workers=" << statistics.active_workers + << " active_external_threads=" << statistics.active_external_threads + << " concurrency=" << statistics.concurrency; + writer(output); + output << '\n'; +} } class Scene_Base::Impl::Execution_Context { public: @@ -93,6 +125,11 @@ public: const std::size_t queued = queued_.fetch_add(1, std::memory_order_relaxed) + 1; update_peak(peak_queued_, queued); if (!operations_.try_push(std::move(operation))) ::renderive::error::unexpected("scene execution queue rejected admitted operation"); + scene_render_trace([this, queued](auto& output) { + output << " event=execution_submit" + << " scene=" << &scene_.scene() + << " queued=" << queued; + }); schedule(); } void wait() { @@ -137,9 +174,19 @@ private: return; continue; } - queued_.fetch_sub(1, std::memory_order_relaxed); + const std::size_t queued = queued_.fetch_sub(1, std::memory_order_relaxed) - 1; slots_.release(); + scene_render_trace([this, queued](auto& output) { + output << " event=execution_operation_begin" + << " scene=" << &scene_.scene() + << " queued=" << queued; + }); operation(); + scene_render_trace([this, queued](auto& output) { + output << " event=execution_operation_end" + << " scene=" << &scene_.scene() + << " queued=" << queued; + }); } } static constexpr std::ptrdiff_t default_capacity = 64; @@ -653,7 +700,17 @@ Scene_Render_Result Scene_Base::Impl::submit_render(Abstract_Frame* frame) { task->snapshot, task->compiled_plan->plan }; ++pending_operations_; + const std::size_t pending_operations = pending_operations_; task_lock.unlock(); + scene_render_trace([this, &task, pending_operations](auto& output) { + output << " event=render_submit" + << " scene=" << owner_ + << " completion=" << task->completion.get() + << " sequence=" << task->snapshot->render_sequence + << " pending_operations=" << pending_operations + << " active_execution_scene=" << active_execution_scene_ + << " active_observer_scene=" << active_submitted_observer_scene_; + }); Scene_Base* previous_observer_scene = std::exchange(active_submitted_observer_scene_, owner_); try { @@ -677,6 +734,13 @@ Scene_Render_Result Scene_Base::Impl::submit_render(Abstract_Frame* frame) { } active_submitted_observer_scene_ = previous_observer_scene; try { + scene_render_trace([this, &task](auto& output) { + output << " event=render_task_queue" + << " scene=" << owner_ + << " completion=" << task->completion.get() + << " sequence=" << task->snapshot->render_sequence + << " active_execution_scene=" << active_execution_scene_; + }); execution_context_->submit([this, task = std::move(task)] { execute_render_task(task); complete_pending_operation(); @@ -707,15 +771,40 @@ Scene_Render_Result Scene_Base::Impl::submit_render(Abstract_Frame* frame) { Scene_Render_Result Scene_Base::wait_for_render() { auto& implementation = d_func(); if (implementation.is_render_execution_context() || - Impl::active_submitted_observer_scene_ == this) + Impl::active_submitted_observer_scene_ == this) { + scene_render_trace([this](auto& output) { + output << " event=wait_bypass" + << " scene=" << this + << " active_execution_scene=" << Impl::active_execution_scene_ + << " active_observer_scene=" << Impl::active_submitted_observer_scene_; + }); return Scene_Render_Result::none; + } std::unique_lock lock(implementation.task_mutex_); const auto completion = implementation.current_completion_; if (!completion) return Scene_Render_Result::none; + scene_render_trace([this, &implementation, &completion](auto& output) { + output << " event=wait_begin" + << " scene=" << this + << " completion=" << completion.get() + << " sequence=" << implementation.render_sequence_ + << " completed=" << completion->completed + << " pending_operations=" << implementation.pending_operations_ + << " active_execution_scene=" << Impl::active_execution_scene_ + << " active_observer_scene=" << Impl::active_submitted_observer_scene_; + }); implementation.render_completed_.wait( lock, [&completion] { return completion->completed; }); + scene_render_trace([this, &implementation, &completion](auto& output) { + output << " event=wait_end" + << " scene=" << this + << " completion=" << completion.get() + << " sequence=" << implementation.render_sequence_ + << " pending_operations=" << implementation.pending_operations_ + << " active_execution_scene=" << Impl::active_execution_scene_; + }); std::exception_ptr exception; if (implementation.pending_exception_) exception = std::exchange(implementation.pending_exception_, {}); else { @@ -765,10 +854,23 @@ Scene_Base::Edit_Operation Scene_Base::Impl::enqueue_renderable_edit( return operation; } void Scene_Base::Impl::complete_pending_operation() { + std::size_t pending_operations; + const void* completion; + std::uint64_t render_sequence; { std::lock_guard lock(task_mutex_); - --pending_operations_; + pending_operations = --pending_operations_; + completion = current_completion_.get(); + render_sequence = render_sequence_; } + scene_render_trace([this, pending_operations, completion, render_sequence](auto& output) { + output << " event=pending_operation_complete" + << " scene=" << owner_ + << " completion=" << completion + << " sequence=" << render_sequence + << " pending_operations=" << pending_operations + << " active_execution_scene=" << active_execution_scene_; + }); render_completed_.notify_all(); } void Scene_Base::Impl::record_pending_exception(std::exception_ptr exception) { @@ -1158,9 +1260,30 @@ std::unique_lock Scene_Base::Impl::lock_render_idle() { if (is_render_execution_context()) ::renderive::error::unexpected("render-idle operation is not allowed during render execution"); if (active_submitted_observer_scene_ == owner_) ::renderive::error::unexpected("render-idle operation is not allowed during render submission observation"); std::unique_lock lock(task_mutex_); + const bool waited = pending_operations_ != 0; + if (waited) { + scene_render_trace([this](auto& output) { + output << " event=idle_wait_begin" + << " scene=" << owner_ + << " completion=" << current_completion_.get() + << " sequence=" << render_sequence_ + << " pending_operations=" << pending_operations_ + << " active_execution_scene=" << active_execution_scene_; + }); + } render_completed_.wait(lock, [this] { return pending_operations_ == 0; }); + if (waited) { + scene_render_trace([this](auto& output) { + output << " event=idle_wait_end" + << " scene=" << owner_ + << " completion=" << current_completion_.get() + << " sequence=" << render_sequence_ + << " pending_operations=" << pending_operations_ + << " active_execution_scene=" << active_execution_scene_; + }); + } return lock; } bool Scene_Base::Impl::is_render_execution_context() const noexcept { @@ -1220,8 +1343,16 @@ Scene_Edit_Error Scene_Base::Impl::execute_renderable_edit( } } void Scene_Base::Impl::execute_render_task(std::shared_ptr task) { + Scene_Base* previous_execution_scene = active_execution_scene_; Render_Execution_Scope scope(*this); const auto& snapshot = *task->snapshot; + scene_render_trace([this, &task, &snapshot, previous_execution_scene](auto& output) { + output << " event=render_task_begin" + << " scene=" << owner_ + << " completion=" << task->completion.get() + << " sequence=" << snapshot.render_sequence + << " parent_execution_scene=" << previous_execution_scene; + }); Scene_Render_Result error{}; std::exception_ptr exception; bool render_graph_entered{}; @@ -1232,7 +1363,19 @@ void Scene_Base::Impl::execute_render_task(std::shared_ptr task) { task->snapshot, task->compiled_plan->plan }); render_graph_entered = true; + scene_render_trace([this, &task, &snapshot](auto& output) { + output << " event=render_graph_begin" + << " scene=" << owner_ + << " completion=" << task->completion.get() + << " sequence=" << snapshot.render_sequence; + }); error = execute_render_graph(*task); + scene_render_trace([this, &task, &snapshot](auto& output) { + output << " event=render_graph_end" + << " scene=" << owner_ + << " completion=" << task->completion.get() + << " sequence=" << snapshot.render_sequence; + }); const auto event = error == Scene_Render_Result::none ? Observation_Event::render_completed : Observation_Event::render_cancelled; @@ -1259,12 +1402,21 @@ void Scene_Base::Impl::execute_render_task(std::shared_ptr task) { std::move(exception), std::current_exception()); } } + std::size_t pending_operations; { std::lock_guard lock(task_mutex_); task->completion->error = error; task->completion->exception = exception; task->completion->completed = true; + pending_operations = pending_operations_; } + scene_render_trace([this, &task, &snapshot, pending_operations](auto& output) { + output << " event=render_task_complete" + << " scene=" << owner_ + << " completion=" << task->completion.get() + << " sequence=" << snapshot.render_sequence + << " pending_operations=" << pending_operations; + }); render_completed_.notify_all(); } std::shared_ptr Scene_Base::Impl::compile_render_plan( diff --git a/debug.ps1 b/debug.ps1 new file mode 100644 index 0000000..5ba4d09 --- /dev/null +++ b/debug.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..f04e3c2 100644 --- a/web_server/app/Gallery_Plot_Session.cpp +++ b/web_server/app/Gallery_Plot_Session.cpp @@ -10,15 +10,21 @@ #include #include #include +#include #include +#include #include +#include #include +#include #include #include #include #include +#include #include #include +#include namespace renderive::web { namespace { Gallery_View_Input gallery_input(const Event& event) { @@ -133,6 +139,56 @@ trantor::EventLoop* automatic_render_loop() { return thread.getLoop(); } } +namespace detail { +class Automatic_Render_Executor final { +public: + Automatic_Render_Executor() { + const std::size_t worker_count = renderive::scheduling::scheduler_concurrency(); + workers_.reserve(worker_count); + for (std::size_t index = 0; index < worker_count; ++index) { + workers_.emplace_back([this](std::stop_token stop) { + run(stop); + }); + } + } + ~Automatic_Render_Executor() { + for (auto& worker : workers_) + worker.request_stop(); + condition_.notify_all(); + } + void enqueue(std::function task) { + { + std::lock_guard lock(mutex_); + tasks_.push_back(std::move(task)); + } + condition_.notify_one(); + } +private: + void run(std::stop_token stop) { + for (;;) { + std::function task; + { + std::unique_lock lock(mutex_); + if (!condition_.wait(lock, stop, [this] { + return !tasks_.empty(); + })) + return; + task = std::move(tasks_.front()); + tasks_.pop_front(); + } + task(); + } + } + std::mutex mutex_; + std::condition_variable_any condition_; + std::deque> tasks_; + std::vector workers_; +}; +Automatic_Render_Executor& automatic_render_executor() { + static Automatic_Render_Executor executor; + return executor; +} +} struct Gallery_Plot_Session::Impl : std::enable_shared_from_this { using Clock = std::chrono::steady_clock; explicit Impl(bool enable_automatic_low_latency) @@ -247,7 +303,7 @@ struct Gallery_Plot_Session::Impl : std::enable_shared_from_thisrun_automatic_render(generation); }); } catch (...) {