This is an archive of the discontinued LLVM Phabricator instance.

[lldb] [MainLoopPosix] Fix crash upon adding lots of pending callbacks
ClosedPublic

Authored by mgorny on Oct 8 2022, 9:34 AM.

Details

Summary

If lots of pending callbacks are added while the main loop has exited
already, the trigger pipe buffer fills in, causing the write to fail
and the related assertion to fail. To avoid this, add a boolean member
indicating whether the callbacks have been triggered already.
If the trigger was done, avoid writing to the pipe until loops proceeds
to run them and resets the variable.

Besides fixing the issue, this also avoids writing to the pipe multiple
times if callbacks are added faster than the loop is able to process
them. Previously, this would lead to the loop performing multiple read
iterations from pipe unnecessarily.

Diff Detail

Event Timeline

mgorny created this revision.Oct 8 2022, 9:34 AM
Herald added a project: Restricted Project. · View Herald TranscriptOct 8 2022, 9:34 AM
mgorny requested review of this revision.Oct 8 2022, 9:34 AM

I never expected we would have so many callbacks that we'd have to worry about this, but yes, this is one way to fix the problem. Another (slightly simpler, but also less performant) would be to make the write pipe nonblocking, and do not treat EAGAIN as an error.

lldb/source/Host/posix/MainLoopPosix.cpp
408

I /think/ this is not right. The other thread can wake up as soon as the Write call is done, and can proceed to clear the done flag before we are able to set it. If we set it afterwards, then we we suppress all subsequent writes, and the callbacks would never run. If we set the flag before we make the Write call, then it should be ok -- though the flag should probably have a different name then.

I never expected we would have so many callbacks that we'd have to worry about this, but yes, this is one way to fix the problem. Another (slightly simpler, but also less performant) would be to make the write pipe nonblocking, and do not treat EAGAIN as an error.

Well, I've only hit this in a pathological case (sending a lot of small packets without checking whether the loop in the communication thread didn't exit) but I've figured out we want some protection anyway.

The pipe logic uses select helper anyway, so it does return a timeout error if the buffer is full. However, I've figured that it'd be better to avoid polluting the buffer with tons of "interrupts" if only one is really meaningful.

mgorny added inline comments.Oct 14 2022, 9:01 AM
lldb/source/Host/posix/MainLoopPosix.cpp
408

I think you are correct. Will update that soonish.

mgorny updated this revision to Diff 467834.Oct 14 2022, 10:36 AM

Move and rename the bool.

mgorny marked an inline comment as done.Oct 14 2022, 10:37 AM
labath accepted this revision.Oct 17 2022, 6:54 AM
labath added inline comments.
lldb/source/Host/posix/MainLoopPosix.cpp
399–402

Maybe something like if (m_triggering.exchange(true)) return; ?
This version should work as well, but it may save someone from wondering what will happen if two threads execute this concurrently.

This revision is now accepted and ready to land.Oct 17 2022, 6:54 AM
mgorny marked an inline comment as done.Oct 17 2022, 8:11 AM

Thanks. I'll do a fresh test run on the updated version and push if it passes.

lldb/source/Host/posix/MainLoopPosix.cpp
399–402

Sure, I'll try that.

This revision was automatically updated to reflect the committed changes.
mgorny marked an inline comment as done.
Herald added a project: Restricted Project. · View Herald TranscriptOct 17 2022, 8:48 AM