Introduce a MainLoop class and switch llgs to use it
Summary:
This is the first part of our effort to make llgs single threaded. Currently, llgs consists of
about three threads and the synchronisation between them is a major source of latency when
debugging linux and android applications.
In order to be able to go single threaded, we must have the ability to listen for events from
multiple sources (primarily, client commands coming over the network and debug events from the
inferior) and perform necessary actions. For this reason I introduce the concept of a MainLoop.
A main loop has the ability to register callback's which will be invoked upon receipt of certain
events. MainLoopPosix has the ability to listen for file descriptors and signals.
For the moment, I have merely made the GDBRemoteCommunicationServerLLGS class use MainLoop
instead of waiting on the network socket directly, but the other threads still remain. In the
followup patches I indend to migrate NativeProcessLinux to this class and remove the remaining
threads.
Reviewers: ovyalov, clayborg, amccarth, zturner, emaste
Subscribers: tberghammer, lldb-commits
Differential Revision: http://reviews.llvm.org/D11066
llvm-svn: 242018
2015-07-13 18:44:55 +08:00
|
|
|
//===-- MainLoopPosix.cpp ---------------------------------------*- C++ -*-===//
|
|
|
|
//
|
|
|
|
// The LLVM Compiler Infrastructure
|
|
|
|
//
|
|
|
|
// This file is distributed under the University of Illinois Open Source
|
|
|
|
// License. See LICENSE.TXT for details.
|
|
|
|
//
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
|
|
|
|
#include "lldb/Host/posix/MainLoopPosix.h"
|
|
|
|
|
|
|
|
#include <vector>
|
|
|
|
|
|
|
|
#include "lldb/Core/Error.h"
|
|
|
|
|
|
|
|
using namespace lldb;
|
|
|
|
using namespace lldb_private;
|
|
|
|
|
|
|
|
static sig_atomic_t g_signal_flags[NSIG];
|
|
|
|
|
|
|
|
static void
|
|
|
|
SignalHandler(int signo, siginfo_t *info, void *)
|
|
|
|
{
|
|
|
|
assert(signo < NSIG);
|
|
|
|
g_signal_flags[signo] = 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
MainLoopPosix::~MainLoopPosix()
|
|
|
|
{
|
|
|
|
assert(m_read_fds.size() == 0);
|
|
|
|
assert(m_signals.size() == 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
MainLoopPosix::ReadHandleUP
|
|
|
|
MainLoopPosix::RegisterReadObject(const IOObjectSP &object_sp, const Callback &callback, Error &error)
|
|
|
|
{
|
|
|
|
if (!object_sp || !object_sp->IsValid())
|
|
|
|
{
|
|
|
|
error.SetErrorString("IO object is not valid.");
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
|
|
|
|
const bool inserted = m_read_fds.insert({object_sp->GetWaitableHandle(), callback}).second;
|
|
|
|
if (! inserted)
|
|
|
|
{
|
|
|
|
error.SetErrorStringWithFormat("File descriptor %d already monitored.",
|
|
|
|
object_sp->GetWaitableHandle());
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
|
|
|
|
return CreateReadHandle(object_sp);
|
|
|
|
}
|
|
|
|
|
|
|
|
// We shall block the signal, then install the signal handler. The signal will be unblocked in
|
|
|
|
// the Run() function to check for signal delivery.
|
|
|
|
MainLoopPosix::SignalHandleUP
|
|
|
|
MainLoopPosix::RegisterSignal(int signo, const Callback &callback, Error &error)
|
|
|
|
{
|
|
|
|
if (m_signals.find(signo) != m_signals.end())
|
|
|
|
{
|
|
|
|
error.SetErrorStringWithFormat("Signal %d already monitored.", signo);
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
|
|
|
|
SignalInfo info;
|
|
|
|
info.callback = callback;
|
|
|
|
struct sigaction new_action;
|
|
|
|
new_action.sa_sigaction = &SignalHandler;
|
|
|
|
new_action.sa_flags = SA_SIGINFO;
|
|
|
|
sigemptyset(&new_action.sa_mask);
|
|
|
|
sigaddset(&new_action.sa_mask, signo);
|
|
|
|
|
|
|
|
sigset_t old_set;
|
|
|
|
if (int ret = pthread_sigmask(SIG_BLOCK, &new_action.sa_mask, &old_set))
|
|
|
|
{
|
|
|
|
error.SetErrorStringWithFormat("pthread_sigmask failed with error %d\n", ret);
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
|
|
|
|
info.was_blocked = sigismember(&old_set, signo);
|
|
|
|
if (sigaction(signo, &new_action, &info.old_action) == -1)
|
|
|
|
{
|
|
|
|
error.SetErrorToErrno();
|
|
|
|
if (!info.was_blocked)
|
|
|
|
pthread_sigmask(SIG_UNBLOCK, &new_action.sa_mask, nullptr);
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
|
|
|
|
m_signals.insert({signo, info});
|
|
|
|
g_signal_flags[signo] = 0;
|
|
|
|
|
|
|
|
return SignalHandleUP(new SignalHandle(*this, signo));
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
2015-07-17 18:08:38 +08:00
|
|
|
MainLoopPosix::UnregisterReadObject(IOObject::WaitableHandle handle)
|
Introduce a MainLoop class and switch llgs to use it
Summary:
This is the first part of our effort to make llgs single threaded. Currently, llgs consists of
about three threads and the synchronisation between them is a major source of latency when
debugging linux and android applications.
In order to be able to go single threaded, we must have the ability to listen for events from
multiple sources (primarily, client commands coming over the network and debug events from the
inferior) and perform necessary actions. For this reason I introduce the concept of a MainLoop.
A main loop has the ability to register callback's which will be invoked upon receipt of certain
events. MainLoopPosix has the ability to listen for file descriptors and signals.
For the moment, I have merely made the GDBRemoteCommunicationServerLLGS class use MainLoop
instead of waiting on the network socket directly, but the other threads still remain. In the
followup patches I indend to migrate NativeProcessLinux to this class and remove the remaining
threads.
Reviewers: ovyalov, clayborg, amccarth, zturner, emaste
Subscribers: tberghammer, lldb-commits
Differential Revision: http://reviews.llvm.org/D11066
llvm-svn: 242018
2015-07-13 18:44:55 +08:00
|
|
|
{
|
2015-07-17 18:08:38 +08:00
|
|
|
bool erased = m_read_fds.erase(handle);
|
Introduce a MainLoop class and switch llgs to use it
Summary:
This is the first part of our effort to make llgs single threaded. Currently, llgs consists of
about three threads and the synchronisation between them is a major source of latency when
debugging linux and android applications.
In order to be able to go single threaded, we must have the ability to listen for events from
multiple sources (primarily, client commands coming over the network and debug events from the
inferior) and perform necessary actions. For this reason I introduce the concept of a MainLoop.
A main loop has the ability to register callback's which will be invoked upon receipt of certain
events. MainLoopPosix has the ability to listen for file descriptors and signals.
For the moment, I have merely made the GDBRemoteCommunicationServerLLGS class use MainLoop
instead of waiting on the network socket directly, but the other threads still remain. In the
followup patches I indend to migrate NativeProcessLinux to this class and remove the remaining
threads.
Reviewers: ovyalov, clayborg, amccarth, zturner, emaste
Subscribers: tberghammer, lldb-commits
Differential Revision: http://reviews.llvm.org/D11066
llvm-svn: 242018
2015-07-13 18:44:55 +08:00
|
|
|
(void) erased;
|
|
|
|
assert(erased);
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
MainLoopPosix::UnregisterSignal(int signo)
|
|
|
|
{
|
|
|
|
// We undo the actions of RegisterSignal on a best-effort basis.
|
|
|
|
auto it = m_signals.find(signo);
|
|
|
|
assert(it != m_signals.end());
|
|
|
|
|
|
|
|
sigaction(signo, &it->second.old_action, nullptr);
|
|
|
|
|
|
|
|
sigset_t set;
|
|
|
|
sigemptyset(&set);
|
|
|
|
sigaddset(&set, signo);
|
|
|
|
pthread_sigmask(it->second.was_blocked ? SIG_BLOCK : SIG_UNBLOCK, &set, nullptr);
|
|
|
|
|
|
|
|
m_signals.erase(it);
|
|
|
|
}
|
|
|
|
|
|
|
|
Error
|
|
|
|
MainLoopPosix::Run()
|
|
|
|
{
|
|
|
|
std::vector<int> signals;
|
|
|
|
sigset_t sigmask;
|
|
|
|
std::vector<int> read_fds;
|
|
|
|
fd_set read_fd_set;
|
|
|
|
m_terminate_request = false;
|
|
|
|
|
|
|
|
// run until termination or until we run out of things to listen to
|
|
|
|
while (! m_terminate_request && (!m_read_fds.empty() || !m_signals.empty()))
|
|
|
|
{
|
|
|
|
// To avoid problems with callbacks changing the things we're supposed to listen to, we
|
|
|
|
// will store the *real* list of events separately.
|
|
|
|
signals.clear();
|
|
|
|
read_fds.clear();
|
|
|
|
FD_ZERO(&read_fd_set);
|
|
|
|
int nfds = 0;
|
|
|
|
|
|
|
|
if (int ret = pthread_sigmask(SIG_SETMASK, nullptr, &sigmask))
|
|
|
|
return Error("pthread_sigmask failed with error %d\n", ret);
|
|
|
|
|
|
|
|
for (const auto &fd: m_read_fds)
|
|
|
|
{
|
|
|
|
read_fds.push_back(fd.first);
|
|
|
|
FD_SET(fd.first, &read_fd_set);
|
|
|
|
nfds = std::max(nfds, fd.first+1);
|
|
|
|
}
|
|
|
|
|
|
|
|
for (const auto &sig: m_signals)
|
|
|
|
{
|
|
|
|
signals.push_back(sig.first);
|
|
|
|
sigdelset(&sigmask, sig.first);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (pselect(nfds, &read_fd_set, nullptr, nullptr, nullptr, &sigmask) == -1 && errno != EINTR)
|
|
|
|
return Error(errno, eErrorTypePOSIX);
|
|
|
|
|
|
|
|
for (int sig: signals)
|
|
|
|
{
|
|
|
|
if (g_signal_flags[sig] == 0)
|
|
|
|
continue; // No signal
|
|
|
|
g_signal_flags[sig] = 0;
|
|
|
|
|
|
|
|
auto it = m_signals.find(sig);
|
|
|
|
if (it == m_signals.end())
|
|
|
|
continue; // Signal must have gotten unregistered in the meantime
|
|
|
|
|
|
|
|
it->second.callback(*this); // Do the work
|
|
|
|
|
|
|
|
if (m_terminate_request)
|
|
|
|
return Error();
|
|
|
|
}
|
|
|
|
|
|
|
|
for (int fd: read_fds)
|
|
|
|
{
|
|
|
|
if (!FD_ISSET(fd, &read_fd_set))
|
|
|
|
continue; // Not ready
|
|
|
|
|
|
|
|
auto it = m_read_fds.find(fd);
|
|
|
|
if (it == m_read_fds.end())
|
|
|
|
continue; // File descriptor must have gotten unregistered in the meantime
|
|
|
|
|
|
|
|
it->second(*this); // Do the work
|
|
|
|
|
|
|
|
if (m_terminate_request)
|
|
|
|
return Error();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return Error();
|
|
|
|
}
|
|
|
|
|
|
|
|
|