Skip to content

Commit

Permalink
[lldb] Don't exit the main loop when in runs out of things to listen …
Browse files Browse the repository at this point in the history
…on (llvm#112565)

This behavior made sense in the beginning as the class was completely
single threaded, so if the source count ever reached zero, there was no
way to add new ones. In https://reviews.llvm.org/D131160, the class
gained the ability to add events (callbacks) from other threads, which
means that is no longer the case (and indeed, one possible use case for
this class -- acting as a sort of arbiter for multiple threads wanting
to run code while making sure it runs serially -- has this class sit in
an empty Run call most of the time). I'm not aware of us having a use
for such a thing right now, but one of my tests in another patch turned
into something similar by accident.

Another problem with the current approach is that, in a
distributed/dynamic setup (multiple things using the main loop without a
clear coordinator), one can never be sure whether unregistering a
specific event will terminate the loop (it depends on whether there are
other listeners). We had this problem in lldb-platform.cpp, where we had
to add an additional layer of synchronization to avoid premature
termination. We can remove this if we can rely on the loop terminating
only when we tell it to.
  • Loading branch information
labath authored Oct 17, 2024
1 parent 4897fc4 commit 98b419c
Show file tree
Hide file tree
Showing 4 changed files with 21 additions and 33 deletions.
5 changes: 1 addition & 4 deletions lldb/source/Host/posix/MainLoopPosix.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -365,10 +365,7 @@ Status MainLoopPosix::Run() {
Status error;
RunImpl impl(*this);

// run until termination or until we run out of things to listen to
// (m_read_fds will always contain m_trigger_pipe fd, so check for > 1)
while (!m_terminate_request &&
(m_read_fds.size() > 1 || !m_signals.empty())) {
while (!m_terminate_request) {
error = impl.Poll();
if (error.Fail())
return error;
Expand Down
4 changes: 1 addition & 3 deletions lldb/source/Host/windows/MainLoopWindows.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -116,9 +116,7 @@ Status MainLoopWindows::Run() {

Status error;

// run until termination or until we run out of things to listen to
while (!m_terminate_request && !m_read_fds.empty()) {

while (!m_terminate_request) {
llvm::Expected<size_t> signaled_event = Poll();
if (!signaled_event)
return Status::FromError(signaled_event.takeError());
Expand Down
27 changes: 10 additions & 17 deletions lldb/tools/lldb-server/lldb-platform.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -260,8 +260,7 @@ static void client_handle(GDBRemoteCommunicationServerPlatform &platform,
static Status spawn_process(const char *progname, const Socket *conn_socket,
uint16_t gdb_port, const lldb_private::Args &args,
const std::string &log_file,
const StringRef log_channels, MainLoop &main_loop,
std::promise<void> &child_exited) {
const StringRef log_channels, MainLoop &main_loop) {
Status error;
SharedSocket shared_socket(conn_socket, error);
if (error.Fail())
Expand Down Expand Up @@ -301,12 +300,10 @@ static Status spawn_process(const char *progname, const Socket *conn_socket,
if (g_server)
launch_info.SetMonitorProcessCallback([](lldb::pid_t, int, int) {});
else
launch_info.SetMonitorProcessCallback(
[&child_exited, &main_loop](lldb::pid_t, int, int) {
main_loop.AddPendingCallback(
[](MainLoopBase &loop) { loop.RequestTermination(); });
child_exited.set_value();
});
launch_info.SetMonitorProcessCallback([&main_loop](lldb::pid_t, int, int) {
main_loop.AddPendingCallback(
[](MainLoopBase &loop) { loop.RequestTermination(); });
});

// Copy the current environment.
launch_info.GetEnvironment() = Host::GetEnvironment();
Expand Down Expand Up @@ -550,27 +547,24 @@ int main_platform(int argc, char *argv[]) {
return socket_error;
}

std::promise<void> child_exited;
MainLoop main_loop;
{
llvm::Expected<std::vector<MainLoopBase::ReadHandleUP>> platform_handles =
platform_sock->Accept(
main_loop, [progname, gdbserver_port, &inferior_arguments, log_file,
log_channels, &main_loop, &child_exited,
log_channels, &main_loop,
&platform_handles](std::unique_ptr<Socket> sock_up) {
printf("Connection established.\n");
Status error = spawn_process(
progname, sock_up.get(), gdbserver_port, inferior_arguments,
log_file, log_channels, main_loop, child_exited);
Status error = spawn_process(progname, sock_up.get(),
gdbserver_port, inferior_arguments,
log_file, log_channels, main_loop);
if (error.Fail()) {
Log *log = GetLog(LLDBLog::Platform);
LLDB_LOGF(log, "spawn_process failed: %s", error.AsCString());
WithColor::error()
<< "spawn_process failed: " << error.AsCString() << "\n";
if (!g_server) {
if (!g_server)
main_loop.RequestTermination();
child_exited.set_value();
}
}
if (!g_server)
platform_handles->clear();
Expand All @@ -592,7 +586,6 @@ int main_platform(int argc, char *argv[]) {

main_loop.Run();
}
child_exited.get_future().get();

fprintf(stderr, "lldb-server exiting...\n");

Expand Down
18 changes: 9 additions & 9 deletions lldb/unittests/Host/MainLoopTest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -194,9 +194,6 @@ TEST_F(MainLoopTest, PendingCallbackTrigger) {
add_callback2.set_value();
});
Status error;
auto socket_handle = loop.RegisterReadObject(
socketpair[1], [](MainLoopBase &) {}, error);
ASSERT_TRUE(socket_handle);
ASSERT_THAT_ERROR(error.ToError(), llvm::Succeeded());
bool callback2_called = false;
std::thread callback2_adder([&]() {
Expand All @@ -212,15 +209,18 @@ TEST_F(MainLoopTest, PendingCallbackTrigger) {
ASSERT_TRUE(callback2_called);
}

// Regression test for assertion failure if a lot of callbacks end up
// being queued after loop exits.
TEST_F(MainLoopTest, PendingCallbackAfterLoopExited) {
TEST_F(MainLoopTest, ManyPendingCallbacks) {
MainLoop loop;
Status error;
ASSERT_TRUE(loop.Run().Success());
// Try to fill the pipe buffer in.
// Try to fill up the pipe buffer and make sure bad things don't happen. This
// is a regression test for the case where writing to the interrupt pipe
// caused a deadlock when the pipe filled up (either because the main loop was
// not running, because it was slow, or because it was busy/blocked doing
// something else).
for (int i = 0; i < 65536; ++i)
loop.AddPendingCallback([&](MainLoopBase &loop) {});
loop.AddPendingCallback(
[&](MainLoopBase &loop) { loop.RequestTermination(); });
ASSERT_TRUE(loop.Run().Success());
}

#ifdef LLVM_ON_UNIX
Expand Down

0 comments on commit 98b419c

Please sign in to comment.