blob: 3fbdc26fc4cdab0bb3dee748f2b5dbe7b7a279a3 [file] [log] [blame] [edit]
//===----------------------------------------------------------------------===//
//
// 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 "EventHelper.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);
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(EventThread, debugger, requesting_dap->broadcaster,
requesting_dap->m_client_name,
std::ref(requesting_dap->log)));
m_debugger_event_threads[debugger_id] = new_thread_sp;
return new_thread_sp;
}
DAP *DAPSessionManager::FindDAPForTarget(lldb::SBTarget target) {
std::lock_guard<std::mutex> lock(m_sessions_mutex);
for (const auto &[loop, dap] : m_active_sessions)
if (dap && dap->target.IsValid() && dap->target == target)
return dap;
return nullptr;
}
void DAPSessionManager::ReleaseExpiredEventThreads() {
std::lock_guard<std::mutex> lock(m_sessions_mutex);
for (auto it = m_debugger_event_threads.begin();
it != m_debugger_event_threads.end();) {
// Check if the weak_ptr has expired (no DAP instances are using it
// anymore).
if (it->second.expired()) {
it = m_debugger_event_threads.erase(it);
} else {
++it;
}
}
}
} // namespace lldb_dap