[lldb] Fix memory monitor shutdown on Linux using eventfd (#178083)
The current linux implementation has a 1 second timeout when polling for
memory pressure. lldb-dap may take up to an extra 1 second to shutdown.
Use an event file descriptor to immediately stop the memory monitor
thread.
Fixes #150220
(cherry picked from commit a5ffce0fae)
This commit is contained in:
parent
fb10824fec
commit
ff0568f7b8
2 changed files with 104 additions and 32 deletions
|
|
@ -11,6 +11,7 @@
|
|||
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <utility>
|
||||
|
||||
namespace lldb_private {
|
||||
|
||||
|
|
@ -18,7 +19,7 @@ class MemoryMonitor {
|
|||
public:
|
||||
using Callback = std::function<void()>;
|
||||
|
||||
MemoryMonitor(Callback callback) : m_callback(callback) {}
|
||||
MemoryMonitor(Callback callback) : m_callback(std::move(callback)) {}
|
||||
virtual ~MemoryMonitor() = default;
|
||||
|
||||
/// MemoryMonitor is not copyable.
|
||||
|
|
|
|||
|
|
@ -13,7 +13,6 @@
|
|||
#include "lldb/Utility/Log.h"
|
||||
#include "llvm/ADT/ScopeExit.h"
|
||||
#include "llvm/Support/Error.h"
|
||||
#include <atomic>
|
||||
#include <cstddef>
|
||||
#include <cstdio>
|
||||
#include <cstring>
|
||||
|
|
@ -21,46 +20,113 @@
|
|||
#if defined(__linux__)
|
||||
#include <fcntl.h>
|
||||
#include <poll.h>
|
||||
#include <sys/eventfd.h>
|
||||
#include <sys/poll.h>
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
|
||||
#if defined(_WIN32)
|
||||
#include <atomic>
|
||||
#include <windows.h>
|
||||
#endif
|
||||
|
||||
using namespace lldb_private;
|
||||
|
||||
class MemoryMonitorPoll : public MemoryMonitor {
|
||||
#if defined(__linux__)
|
||||
class MemoryMonitorLinux : public MemoryMonitor {
|
||||
public:
|
||||
using MemoryMonitor::MemoryMonitor;
|
||||
|
||||
explicit MemoryMonitorLinux(Callback callback)
|
||||
: MemoryMonitor(std::move(callback)),
|
||||
m_stop_fd(::eventfd(0, EFD_NONBLOCK)) {}
|
||||
|
||||
~MemoryMonitorLinux() {
|
||||
if (m_memory_monitor_thread.IsJoinable())
|
||||
m_memory_monitor_thread.Join(nullptr);
|
||||
if (m_stop_fd != 1)
|
||||
::close(m_stop_fd);
|
||||
}
|
||||
|
||||
void Start() override {
|
||||
if (m_stop_fd < 0) {
|
||||
LLDB_LOG_ERROR(
|
||||
GetLog(LLDBLog::Host),
|
||||
llvm::errorCodeToError(llvm::errnoAsErrorCode()),
|
||||
"failed to create stop file descriptor for memory monitor: {0}");
|
||||
return;
|
||||
}
|
||||
|
||||
llvm::Expected<HostThread> memory_monitor_thread =
|
||||
ThreadLauncher::LaunchThread("memory.monitor",
|
||||
[this] { return MonitorThread(); });
|
||||
if (memory_monitor_thread) {
|
||||
m_memory_monitor_thread = *memory_monitor_thread;
|
||||
} else {
|
||||
LLDB_LOG_ERROR(GetLog(LLDBLog::Host), memory_monitor_thread.takeError(),
|
||||
"failed to launch host thread: {0}");
|
||||
}
|
||||
}
|
||||
|
||||
void Stop() override {
|
||||
if (m_memory_monitor_thread.IsJoinable()) {
|
||||
if (m_stop_fd != -1)
|
||||
::eventfd_write(m_stop_fd, 1);
|
||||
m_memory_monitor_thread.Join(nullptr);
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
lldb::thread_result_t MonitorThread() {
|
||||
constexpr size_t pressure_idx = 0;
|
||||
constexpr size_t stop_idx = 1;
|
||||
constexpr size_t fd_count = 2;
|
||||
std::array<pollfd, fd_count> pfds{};
|
||||
|
||||
// Setup stop file descriptor.
|
||||
pfds[stop_idx].fd = m_stop_fd;
|
||||
pfds[stop_idx].events = POLLIN;
|
||||
|
||||
// Setup pressure file descriptor.
|
||||
pfds[pressure_idx].fd =
|
||||
::open("/proc/pressure/memory", O_RDWR | O_NONBLOCK);
|
||||
if (pfds[pressure_idx].fd < 0)
|
||||
return {};
|
||||
pfds[pressure_idx].events = POLLPRI;
|
||||
|
||||
llvm::scope_exit cleanup([&]() { ::close(pfds[pressure_idx].fd); });
|
||||
|
||||
// Detect a 50ms stall in a 2 second time window.
|
||||
constexpr llvm::StringRef trigger = "some 50000 2000000";
|
||||
if (::write(pfds[pressure_idx].fd, trigger.data(), trigger.size() + 1) < 0)
|
||||
return {};
|
||||
|
||||
while (true) {
|
||||
constexpr int timeout_infinite = -1;
|
||||
const int n = ::poll(pfds.data(), pfds.size(), timeout_infinite);
|
||||
if (n > 0) {
|
||||
// Handle stop event.
|
||||
if (pfds[stop_idx].revents & (POLLIN | POLLERR))
|
||||
return {};
|
||||
|
||||
if (pfds[pressure_idx].revents & POLLERR)
|
||||
return {};
|
||||
if (pfds[pressure_idx].revents & POLLPRI)
|
||||
m_callback();
|
||||
}
|
||||
}
|
||||
return {};
|
||||
}
|
||||
int m_stop_fd = -1;
|
||||
HostThread m_memory_monitor_thread;
|
||||
};
|
||||
#elif defined(_WIN32)
|
||||
|
||||
class MemoryMonitorWindows : public MemoryMonitor {
|
||||
public:
|
||||
using MemoryMonitor::MemoryMonitor;
|
||||
|
||||
lldb::thread_result_t MonitorThread() {
|
||||
#if defined(__linux__)
|
||||
struct pollfd fds;
|
||||
fds.fd = open("/proc/pressure/memory", O_RDWR | O_NONBLOCK);
|
||||
if (fds.fd < 0)
|
||||
return {};
|
||||
fds.events = POLLPRI;
|
||||
|
||||
llvm::scope_exit cleanup([&]() { close(fds.fd); });
|
||||
|
||||
// Detect a 50ms stall in a 2 second time window.
|
||||
const char trig[] = "some 50000 2000000";
|
||||
if (write(fds.fd, trig, strlen(trig) + 1) < 0)
|
||||
return {};
|
||||
|
||||
while (!m_done) {
|
||||
int n = poll(&fds, 1, g_timeout);
|
||||
if (n > 0) {
|
||||
if (fds.revents & POLLERR)
|
||||
return {};
|
||||
if (fds.revents & POLLPRI)
|
||||
m_callback();
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined(_WIN32)
|
||||
HANDLE low_memory_notification =
|
||||
CreateMemoryResourceNotification(LowMemoryResourceNotification);
|
||||
if (!low_memory_notification)
|
||||
|
|
@ -72,8 +138,6 @@ public:
|
|||
m_callback();
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
|
|
@ -101,9 +165,16 @@ private:
|
|||
std::atomic<bool> m_done = false;
|
||||
HostThread m_memory_monitor_thread;
|
||||
};
|
||||
#endif
|
||||
|
||||
#if !defined(__APPLE__)
|
||||
std::unique_ptr<MemoryMonitor> MemoryMonitor::Create(Callback callback) {
|
||||
return std::make_unique<MemoryMonitorPoll>(callback);
|
||||
#if defined(__linux__)
|
||||
return std::make_unique<MemoryMonitorLinux>(std::move(callback));
|
||||
#elif defined(_WIN32)
|
||||
return std::make_unique<MemoryMonitorWindows>(std::move(callback));
|
||||
#else
|
||||
return nullptr;
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
|
|
|
|||
Loading…
Reference in a new issue