[lldb] [llvm] [lldb-dap] Add multi-session support with shared debugger instances (PR #163653)

John Harrison via llvm-commits llvm-commits at lists.llvm.org
Mon Oct 20 10:47:04 PDT 2025


================
@@ -0,0 +1,169 @@
+//===-- DAPSessionManager.cpp ----------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+#include "DAPSessionManager.h"
+#include "DAP.h"
+#include "lldb/API/SBBroadcaster.h"
+#include "lldb/API/SBEvent.h"
+#include "lldb/API/SBTarget.h"
+#include "lldb/Host/MainLoopBase.h"
+#include "llvm/Support/Threading.h"
+#include "llvm/Support/WithColor.h"
+
+#include <chrono>
+#include <mutex>
+
+namespace lldb_dap {
+
+ManagedEventThread::ManagedEventThread(lldb::SBBroadcaster broadcaster,
+                                       std::thread t)
+    : m_broadcaster(broadcaster), m_event_thread(std::move(t)) {}
+
+ManagedEventThread::~ManagedEventThread() {
+  if (m_event_thread.joinable()) {
+    m_broadcaster.BroadcastEventByType(eBroadcastBitStopEventThread);
+    m_event_thread.join();
+  }
+}
+
+DAPSessionManager &DAPSessionManager::GetInstance() {
+  static std::once_flag initialized;
+  static DAPSessionManager *instance =
+      nullptr; // NOTE: intentional leak to avoid issues with C++ destructor
+               // chain
+
+  std::call_once(initialized, []() { instance = new DAPSessionManager(); });
+
+  return *instance;
+}
+
+void DAPSessionManager::RegisterSession(lldb_private::MainLoop *loop,
+                                        DAP *dap) {
+  std::lock_guard<std::mutex> lock(m_sessions_mutex);
+  m_active_sessions[loop] = dap;
+}
+
+void DAPSessionManager::UnregisterSession(lldb_private::MainLoop *loop) {
+  std::unique_lock<std::mutex> lock(m_sessions_mutex);
+  m_active_sessions.erase(loop);
+
+  // Clean up shared resources when the last session exits.
+  if (m_active_sessions.empty())
+    CleanupSharedResources();
+
+  std::notify_all_at_thread_exit(m_sessions_condition, std::move(lock));
+}
+
+std::vector<DAP *> DAPSessionManager::GetActiveSessions() {
+  std::lock_guard<std::mutex> lock(m_sessions_mutex);
+  std::vector<DAP *> sessions;
+  for (const auto &[loop, dap] : m_active_sessions)
+    if (dap)
+      sessions.emplace_back(dap);
+  return sessions;
+}
+
+void DAPSessionManager::DisconnectAllSessions() {
+  std::lock_guard<std::mutex> lock(m_sessions_mutex);
+  m_client_failed = false;
+  for (auto [loop, dap] : m_active_sessions) {
+    if (dap) {
+      if (llvm::Error error = dap->Disconnect()) {
+        m_client_failed = true;
+        llvm::WithColor::error() << "DAP client disconnected failed: "
+                                 << llvm::toString(std::move(error)) << "\n";
+      }
+      loop->AddPendingCallback(
+          [](lldb_private::MainLoopBase &loop) { loop.RequestTermination(); });
+    }
+  }
+}
+
+llvm::Error DAPSessionManager::WaitForAllSessionsToDisconnect() {
+  std::unique_lock<std::mutex> lock(m_sessions_mutex);
+  m_sessions_condition.wait(lock, [this] { return m_active_sessions.empty(); });
+
+  // Check if any disconnection failed and return appropriate error.
+  if (m_client_failed)
+    return llvm::make_error<llvm::StringError>(
+        "disconnecting all clients failed", llvm::inconvertibleErrorCode());
+
+  return llvm::Error::success();
+}
+
+std::shared_ptr<ManagedEventThread>
+DAPSessionManager::GetEventThreadForDebugger(lldb::SBDebugger debugger,
+                                             DAP *requesting_dap) {
+  lldb::user_id_t debugger_id = debugger.GetID();
+  std::lock_guard<std::mutex> lock(m_sessions_mutex);
+
+  // Try to use shared event thread, if it exists.
+  if (auto it = m_debugger_event_threads.find(debugger_id);
+      it != m_debugger_event_threads.end()) {
+    if (std::shared_ptr<ManagedEventThread> thread_sp = it->second.lock())
+      return thread_sp;
+    // Our weak pointer has expired.
+    m_debugger_event_threads.erase(it);
+  }
+
+  // Create a new event thread and store it.
+  auto new_thread_sp = std::make_shared<ManagedEventThread>(
+      requesting_dap->broadcaster,
+      std::thread(&DAP::EventThread, requesting_dap));
+  m_debugger_event_threads[debugger_id] = new_thread_sp;
+  return new_thread_sp;
+}
+
+void DAPSessionManager::StoreTargetById(uint32_t target_id,
+                                        lldb::SBTarget target) {
+  std::lock_guard<std::mutex> lock(m_sessions_mutex);
+  m_target_map[target_id] = target;
+}
----------------
ashgti wrote:

Do we need to make any assumptions about the target? E.g. should we assert that its paused? Is it valid for the DAP to continue using the SBTarget? If not, we may want this to take a `DAP &dap` param and remove the target from the DAP instance.

https://github.com/llvm/llvm-project/pull/163653


More information about the llvm-commits mailing list