We have gotten reports of an occasional deadlock on shutdown. The
Driver::MainLoop thread is shutting down, and gets stuck here:
Thread 0x3c017c
start
main
Driver::MainLoop()
lldb::SBDebugger::RunCommandInterpreter(bool, bool)
lldb_private::CommandInterpreter::RunCommandInterpreter(lldb_private::CommandInterpreterRunOptions&)
lldb_private::Debugger::RunIOHandlers()
lldb_private::Debugger::PopIOHandler(std::__1::shared_ptr<lldb_private::IOHandler>
const&)
lldb_private::Editline::Cancel()
lldb_private::LockableStreamFile::Lock()
std::__1::recursive_mutex::lock()
It has acquired the IO Handler list lock (in PopIOHandler) and is stuck
trying to get the debugger output stream lock.
Meanwhile, the event-handler thread is doing:
Thread 0x3c01d3
thread_start
_pthread_start
lldb_private::HostThreadMacOSX::ThreadCreateTrampoline(void*)
lldb_private::HostNativeThreadBase::ThreadCreateTrampoline(void*)
std::__1::__function::__func<lldb_private::Debugger::StartEventHandlerThread()::$_0,
void* ()>::operator()()
lldb_private::Debugger::DefaultEventHandler()
lldb_private::Statusline::~Statusline()
lldb_private::Statusline::UpdateScrollWindow(lldb_private::Statusline::ScrollWindowMode)
lldb_private::Debugger::RefreshIOHandler()
std::__1::recursive_mutex::lock()
UpdateScrollWindow gets the debugger output stream lock, and sends some
data to the output, then it calls RefreshIOHandler while still holding
the Debugger output stream lock. That's the problem, since if it gets
that lock before Editline::Cancel() completes, we'll get this deadlock.
The solution is simple, there's no reason why UpdateScrollWindow should
hold the debugger output lock when it calls RefreshIOHandler. If that
refresh ends up needing to write to the debugger output, it can take the
lock more narrowly at that point. This fixed the deadlock because after
yielding the output lock, the second thread waits on the first to get
the IO Handler lock. Meanwhile the first thread can now acquire the
debugger output lock and finish the Cancel, and exit PopIOHandler which
will allow the second thread to make progress in turn.
I couldn't figure out any way to test this...
5.2 KiB
5.2 KiB