When a scripted frame provider calls back into the thread's frame machinery (e.g. via HandleCommand or EvaluateExpression), two problems arise: 1. GetStackFrameList() re-enters the SyntheticStackFrameList construction, causing infinite recursion. 2. ClearStackFrames() tries to read-lock the StackFrameList's shared_mutex that is already write-locked by GetFramesUpTo, causing a deadlock. This patch fixes those issues by tracking when a provider is actively fetching frames via a per-host-thread map (m_provider_frames_by_thread) keyed by HostThread. The map is pushed/popped in SyntheticStackFrameList::FetchFramesUpTo before calling into the provider. GetStackFrameList() checks it to route re-entrant calls: - The provider's own host thread gets the parent frame list, preventing circular dependency when get_frame_at_index calls back into GetFrameAtIndex. - The private state thread also gets the parent frame list, preventing deadlock when a provider calls EvaluateExpression (which needs the private state thread to process events). - Other host threads proceed normally and block on the frame list mutex until the provider finishes, getting the correct synthetic result. ClearStackFrames() returns early if any provider is active, since the frame state is shared and tearing it down while a provider is mid-construction is both unnecessary and unsafe. rdar://171558394 Signed-off-by: Med Ismail Bennani <ismail@bennani.ma>
68 lines
2.1 KiB
C++
68 lines
2.1 KiB
C++
//===-- HostThread.cpp ----------------------------------------------------===//
|
|
//
|
|
// 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 "lldb/Host/HostThread.h"
|
|
#include "lldb/Host/HostNativeThread.h"
|
|
|
|
using namespace lldb;
|
|
using namespace lldb_private;
|
|
|
|
HostThread::HostThread() : m_native_thread(new HostNativeThread) {}
|
|
|
|
HostThread::HostThread(lldb::thread_t thread)
|
|
: m_native_thread(new HostNativeThread(thread)) {}
|
|
|
|
Status HostThread::Join(lldb::thread_result_t *result) {
|
|
return m_native_thread->Join(result);
|
|
}
|
|
|
|
Status HostThread::Cancel() { return m_native_thread->Cancel(); }
|
|
|
|
void HostThread::Reset() { return m_native_thread->Reset(); }
|
|
|
|
lldb::thread_t HostThread::Release() { return m_native_thread->Release(); }
|
|
|
|
bool HostThread::IsJoinable() const { return m_native_thread->IsJoinable(); }
|
|
|
|
HostNativeThread &HostThread::GetNativeThread() {
|
|
return static_cast<HostNativeThread &>(*m_native_thread);
|
|
}
|
|
|
|
const HostNativeThread &HostThread::GetNativeThread() const {
|
|
return static_cast<const HostNativeThread &>(*m_native_thread);
|
|
}
|
|
|
|
lldb::thread_result_t HostThread::GetResult() const {
|
|
return m_native_thread->GetResult();
|
|
}
|
|
|
|
bool HostThread::EqualsThread(lldb::thread_t thread) const {
|
|
return m_native_thread->EqualsThread(thread);
|
|
}
|
|
|
|
bool HostThread::EqualsThread(const HostThread &thread) const {
|
|
return m_native_thread->EqualsThread(
|
|
thread.GetNativeThread().GetSystemHandle());
|
|
}
|
|
|
|
bool HostThread::HasThread() const {
|
|
if (!m_native_thread)
|
|
return false;
|
|
return m_native_thread->GetSystemHandle() != LLDB_INVALID_HOST_THREAD;
|
|
}
|
|
|
|
unsigned llvm::DenseMapInfo<HostThread>::getHashValue(const HostThread &val) {
|
|
return DenseMapInfo<thread_t>::getHashValue(
|
|
val.GetNativeThread().GetSystemHandle());
|
|
}
|
|
|
|
bool llvm::DenseMapInfo<HostThread>::isEqual(const HostThread &lhs,
|
|
const HostThread &rhs) {
|
|
return lhs.EqualsThread(rhs);
|
|
}
|