Skip to content

Commit 8ff01f8

Browse files
committed
[lldb] Print a message when background tasks take a while to complete
When terminating the debugger, we wait for all background tasks to complete. Given that there's no way to interrupt those treads, this can take a while. When that happens, the debugger appears to hang at exit. The above situation is unfortunately not uncommon when background downloading of dSYMs is enabled (`symbols.auto-download background`). Even when calling dsymForUUID with a reasonable timeout, it can take a while to complete. This patch improves the user experience by registering a callback and printing a message when we're waiting on the thread pool for more than a second. Both the timeout and the callback is configurable. Fixing the underlying issue is a much harder problem. Neither C++ threads no pthread support interrupting threads, so it would have to be a cooperative interruption mechanism, similar to how we deal with this for HostThreads. If we go that route, we should do it at the thread pool level so that it applies to all background tasks. A cooperative interruption mechanism alone solve the dsymForUUID. It's a blocking call to an external binary, so we'd have to monitor for interrupts and kill the execution from yet another thread.
1 parent 354401f commit 8ff01f8

File tree

7 files changed

+57
-2
lines changed

7 files changed

+57
-2
lines changed

lldb/include/lldb/API/SBDebugger.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -331,6 +331,13 @@ class LLDB_API SBDebugger {
331331
void SetDestroyCallback(lldb::SBDebuggerDestroyCallback destroy_callback,
332332
void *baton);
333333

334+
/// Register a callback that is called when destroying the thread pool exceeds
335+
/// the given timeout.
336+
static void
337+
SetThreadPoolTimeoutCallback(uint64_t timeout_milliseconds,
338+
lldb::SBDebuggerTimeoutCallback timeout_callback,
339+
void *baton);
340+
334341
#ifndef SWIG
335342
LLDB_DEPRECATED_FIXME("Use DispatchInput(const void *, size_t)",
336343
"DispatchInput(const void *, size_t)")

lldb/include/lldb/API/SBDefines.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -138,6 +138,7 @@ typedef bool (*SBBreakpointHitCallback)(void *baton, SBProcess &process,
138138

139139
typedef void (*SBDebuggerDestroyCallback)(lldb::user_id_t debugger_id,
140140
void *baton);
141+
typedef void (*SBDebuggerTimeoutCallback)(void *baton);
141142

142143
typedef SBError (*SBPlatformLocateModuleCallback)(
143144
void *baton, const SBModuleSpec &module_spec, SBFileSpec &module_file_spec,

lldb/include/lldb/Core/Debugger.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -250,6 +250,11 @@ class Debugger : public std::enable_shared_from_this<Debugger>,
250250

251251
void SetLoggingCallback(lldb::LogOutputCallback log_callback, void *baton);
252252

253+
static void
254+
SetThreadPoolTimeoutCallback(std::chrono::milliseconds timeout_milliseconds,
255+
lldb::TimeoutCallback timeout_callback,
256+
void *baton);
257+
253258
// Properties Functions
254259
enum StopDisassemblyType {
255260
eStopDisassemblyTypeNever = 0,

lldb/include/lldb/lldb-types.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,7 @@ typedef int pipe_t; // Host pipe type
6969
#define LLDB_INVALID_HOST_THREAD ((lldb::thread_t)NULL)
7070
#define LLDB_INVALID_PIPE ((lldb::pipe_t)-1)
7171

72+
typedef void (*TimeoutCallback)(void *baton);
7273
typedef void (*LogOutputCallback)(const char *, void *baton);
7374
typedef bool (*CommandOverrideCallback)(void *baton, const char **argv);
7475
typedef bool (*ExpressionCancelCallback)(ExpressionEvaluationPhase phase,

lldb/source/API/SBDebugger.cpp

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1695,6 +1695,14 @@ void SBDebugger::SetDestroyCallback(
16951695
}
16961696
}
16971697

1698+
void SBDebugger::SetThreadPoolTimeoutCallback(
1699+
uint64_t timeout_milliseconds,
1700+
lldb::SBDebuggerTimeoutCallback timeout_callback, void *baton) {
1701+
LLDB_INSTRUMENT_VA(timeout_milliseconds, timeout_callback, baton);
1702+
Debugger::SetThreadPoolTimeoutCallback(
1703+
std::chrono::milliseconds(timeout_milliseconds), timeout_callback, baton);
1704+
}
1705+
16981706
SBTrace
16991707
SBDebugger::LoadTraceFromFile(SBError &error,
17001708
const SBFileSpec &trace_description_file) {

lldb/source/Core/Debugger.cpp

Lines changed: 29 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -103,7 +103,14 @@ static std::recursive_mutex *g_debugger_list_mutex_ptr =
103103
nullptr; // NOTE: intentional leak to avoid issues with C++ destructor chain
104104
static Debugger::DebuggerList *g_debugger_list_ptr =
105105
nullptr; // NOTE: intentional leak to avoid issues with C++ destructor chain
106+
107+
/// Thread Pool
108+
/// @{
106109
static llvm::ThreadPool *g_thread_pool = nullptr;
110+
static std::chrono::milliseconds g_timeout_milliseconds;
111+
static lldb::TimeoutCallback g_timeout_callback = nullptr;
112+
static void *g_timeout_callback_baton = nullptr;
113+
/// @}
107114

108115
static constexpr OptionEnumValueElement g_show_disassembly_enum_values[] = {
109116
{
@@ -623,8 +630,20 @@ void Debugger::Terminate() {
623630
}
624631

625632
if (g_thread_pool) {
626-
// The destructor will wait for all the threads to complete.
627-
delete g_thread_pool;
633+
// Delete the thread pool in a different thread so we can set a timeout.
634+
std::future<void> future = std::async(std::launch::async, []() {
635+
// The destructor will wait for all the threads to complete.
636+
delete g_thread_pool;
637+
});
638+
639+
if (g_timeout_callback) {
640+
if (future.wait_for(g_timeout_milliseconds) ==
641+
std::future_status::timeout)
642+
g_timeout_callback(g_timeout_callback_baton);
643+
}
644+
645+
// Wait for the asynchronous task to complete.
646+
future.wait();
628647
}
629648

630649
if (g_debugger_list_ptr && g_debugger_list_mutex_ptr) {
@@ -2195,3 +2214,11 @@ llvm::ThreadPool &Debugger::GetThreadPool() {
21952214
"Debugger::GetThreadPool called before Debugger::Initialize");
21962215
return *g_thread_pool;
21972216
}
2217+
2218+
void Debugger::SetThreadPoolTimeoutCallback(
2219+
std::chrono::milliseconds timeout_milliseconds,
2220+
lldb::TimeoutCallback timeout_callback, void *baton) {
2221+
g_timeout_milliseconds = timeout_milliseconds;
2222+
g_timeout_callback = timeout_callback;
2223+
g_timeout_callback_baton = baton;
2224+
}

lldb/tools/driver/Driver.cpp

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,10 @@ static void reset_stdin_termios() {
9696
}
9797
}
9898

99+
static void thread_pool_timeout_callback(void *) {
100+
fprintf(stderr, "Waiting for background tasks to complete...");
101+
}
102+
99103
Driver::Driver()
100104
: SBBroadcaster("Driver"), m_debugger(SBDebugger::Create(false)) {
101105
// We want to be able to handle CTRL+D in the terminal to have it terminate
@@ -816,6 +820,8 @@ int main(int argc, char const *argv[]) {
816820
}
817821
}
818822

823+
SBDebugger::SetThreadPoolTimeoutCallback(
824+
/*timeout_milliseconds=*/1000, &thread_pool_timeout_callback, nullptr);
819825
SBDebugger::Terminate();
820826
return exit_code;
821827
}

0 commit comments

Comments
 (0)