[Lldb-commits] [lldb] [LLDB] Fix deadlock in module callback when running in parallel (PR #168425)
Jacob Lalonde via lldb-commits
lldb-commits at lists.llvm.org
Mon Nov 17 11:10:20 PST 2025
https://github.com/Jlalond created https://github.com/llvm/llvm-project/pull/168425
When the target is being created, the target list acquires the mutex for the duration of the target creation process. However if a module callback is enabled and is being called in parallel there exists an opportunity to deadlock if the callback calls into targetlist. I've created a minimum repro [here](https://gist.github.com/Jlalond/2557e06fa09825f338eca08b1d21884f).
This looks like a straight forward fix, where `CreateTargetInternal` doesn't access any state directly, and instead calls methods which they themselves are thread-safe. So I've moved the lock to when we update the list with the created target. I'm not sure if this is a comprehensive fix, but it does fix my above example and in my (albeit limited) testing, doesn't cause any strange change in behavior.
>From 8bda2cf729c17fde16af0c18086803d6d6d54b55 Mon Sep 17 00:00:00 2001
From: Jacob Lalonde <jalalonde at fb.com>
Date: Mon, 17 Nov 2025 10:51:50 -0800
Subject: [PATCH] Move lock guard before AddTargetInternal instead of holding
the mutex for the entire target creation and causing a deadlock with the
module callback when running in parallel.
---
lldb/source/Target/TargetList.cpp | 14 ++++++++++++--
1 file changed, 12 insertions(+), 2 deletions(-)
diff --git a/lldb/source/Target/TargetList.cpp b/lldb/source/Target/TargetList.cpp
index 2e03bc1e38ea0..af0d24d7b1d0a 100644
--- a/lldb/source/Target/TargetList.cpp
+++ b/lldb/source/Target/TargetList.cpp
@@ -48,11 +48,16 @@ Status TargetList::CreateTarget(Debugger &debugger,
LoadDependentFiles load_dependent_files,
const OptionGroupPlatform *platform_options,
TargetSP &target_sp) {
- std::lock_guard<std::recursive_mutex> guard(m_target_list_mutex);
+ // Create Target Internal does not modify and state
+ // directly and instead calls into methods which
+ // they themselves are thread-safe. We do this so
+ // the load module call back doesn't cause a re-entry
+ // dead-lock when creating the target.
auto result = TargetList::CreateTargetInternal(
debugger, user_exe_path, triple_str, load_dependent_files,
platform_options, target_sp);
+ std::lock_guard<std::recursive_mutex> guard(m_target_list_mutex);
if (target_sp && result.Success())
AddTargetInternal(target_sp, /*do_select*/ true);
return result;
@@ -63,11 +68,16 @@ Status TargetList::CreateTarget(Debugger &debugger,
const ArchSpec &specified_arch,
LoadDependentFiles load_dependent_files,
PlatformSP &platform_sp, TargetSP &target_sp) {
- std::lock_guard<std::recursive_mutex> guard(m_target_list_mutex);
+ // Create Target Internal does not modify and state
+ // directly and instead calls into methods which
+ // they themselves are thread-safe. We do this so
+ // the load module call back doesn't cause a re-entry
+ // dead-lock when creating the target.
auto result = TargetList::CreateTargetInternal(
debugger, user_exe_path, specified_arch, load_dependent_files,
platform_sp, target_sp);
+ std::lock_guard<std::recursive_mutex> guard(m_target_list_mutex);
if (target_sp && result.Success())
AddTargetInternal(target_sp, /*do_select*/ true);
return result;
More information about the lldb-commits
mailing list