Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 15 additions & 2 deletions lldb/source/Host/posix/MainLoopPosix.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,11 @@ class MainLoopPosix::RunImpl {

#if HAVE_SYS_EVENT_H
std::vector<struct kevent> in_events;
enum FDState : bool {
eFDStateReading = false,
eFDStateEOF = true,
};
std::map<int, FDState> eof_fds;
struct kevent out_events[4];
int num_events = -1;

Expand All @@ -123,8 +128,12 @@ MainLoopPosix::RunImpl::RunImpl(MainLoopPosix &loop) : loop(loop) {
Status MainLoopPosix::RunImpl::Poll() {
in_events.resize(loop.m_read_fds.size());
unsigned i = 0;
for (auto &fd : loop.m_read_fds)
EV_SET(&in_events[i++], fd.first, EVFILT_READ, EV_ADD, 0, 0, 0);
for (auto &fd : loop.m_read_fds) {
if (eof_fds.find(fd.first) == eof_fds.end())
eof_fds[fd.first] = eFDStateReading;
EV_SET(&in_events[i++], fd.first, EVFILT_READ,
eof_fds[fd.first] == eFDStateEOF ? EV_DELETE : EV_ADD, 0, 0, 0);
}

num_events =
kevent(loop.m_kqueue, in_events.data(), in_events.size(), out_events,
Expand All @@ -149,6 +158,10 @@ void MainLoopPosix::RunImpl::ProcessReadEvents() {
switch (out_events[i].filter) {
case EVFILT_READ:
loop.ProcessReadObject(out_events[i].ident);
// A read event with `data == 0` indicates the file has reached EOF and is
// closed. Remember this to prevent triggering the callback again.
if (out_events[i].data == 0)
eof_fds[out_events[i].ident] = eFDStateEOF;
break;
default:
llvm_unreachable("Unknown event");
Expand Down
53 changes: 51 additions & 2 deletions lldb/unittests/Host/MainLoopTest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,52 @@ TEST_F(MainLoopTest, ReadPipeObject) {
ASSERT_EQ(1u, callback_count);
}

TEST_F(MainLoopTest, ReadAcrossMultipleRuns) {
Pipe pipe;

ASSERT_TRUE(pipe.CreateNew().Success());

MainLoop loop;

char X = 'X';
size_t len = sizeof(X);
ASSERT_THAT_EXPECTED(pipe.Write(&X, len), llvm::HasValue(1));

Status error;
auto handle = loop.RegisterReadObject(
std::make_shared<NativeFile>(pipe.GetReadFileDescriptor(),
File::eOpenOptionReadOnly, false),
make_callback(), error);
ASSERT_TRUE(error.Success());
ASSERT_TRUE(handle);

// Callback invoked once per 'Run' since data is not consumed.
ASSERT_TRUE(loop.Run().Success());
ASSERT_TRUE(loop.Run().Success());
ASSERT_EQ(2u, callback_count);

// Consume buffer, so next 'Run' should NOT trigger the handle.
char buf[10];
ASSERT_THAT_EXPECTED(pipe.Read(buf, sizeof(buf)), llvm::HasValue(1));
loop.AddCallback([](auto &loop) { loop.RequestTermination(); },
std::chrono::milliseconds(10));

// This run operation should NOT trigger the read handle, since the data has
// already been consumed.
ASSERT_TRUE(loop.Run().Success());
ASSERT_EQ(2u, callback_count);

// Close the write side of the pair to ensure we have a callback for the
// hangup.
pipe.CloseWriteFileDescriptor();
ASSERT_TRUE(loop.Run().Success());
ASSERT_EQ(3u, callback_count);

// The file handle is still registered, so it will trigger the handle again.
ASSERT_TRUE(loop.Run().Success());
ASSERT_EQ(4u, callback_count);
}

TEST_F(MainLoopTest, MultipleReadsPipeObject) {
Pipe pipe;

Expand Down Expand Up @@ -501,12 +547,15 @@ TEST_F(MainLoopTest, DetectsEOF) {

Status error;
MainLoop loop;
auto handle =
loop.RegisterReadObject(conn->GetReadObject(), make_callback(), error);
auto handle = loop.RegisterReadObject(
conn->GetReadObject(), [&](auto &loop) { callback_count++; }, error);
loop.AddCallback([](MainLoopBase &loop) { loop.RequestTermination(); },
std::chrono::milliseconds(100));
ASSERT_TRUE(error.Success());
term.CloseSecondaryFileDescriptor();

ASSERT_TRUE(loop.Run().Success());
// Ensure we only receive one event for the EOF event.
ASSERT_EQ(1u, callback_count);
}

Expand Down
Loading