This is an archive of the discontinued LLVM Phabricator instance.

[lldb] [Core] Reimplement Communication::ReadThread using MainLoop
Needs ReviewPublic

Authored by mgorny on Aug 20 2022, 1:09 AM.

Details

Summary

Use MainLoop to implement Communication::ReadThread() instead of
blocking reads. Most importantly, this makes it possible to use
the newly implemented TriggerPendingCallbacks() mechanism to immediately
interrupt waiting for new data. This in turn will make it possible to
enable the possibility of writing to the descriptor using the same
thread.

Sponsored by: The FreeBSD Foundation

Diff Detail

Event Timeline

mgorny created this revision.Aug 20 2022, 1:09 AM
Herald added a project: Restricted Project. · View Herald TranscriptAug 20 2022, 1:09 AM
Herald added a subscriber: arichardson. · View Herald Transcript
mgorny planned changes to this revision.Aug 20 2022, 2:54 AM

Unfortunately, many tests are failing or timing out, so need to investigate more.

mgorny updated this revision to Diff 454331.Aug 21 2022, 11:13 AM

Update Communication::SynchronizeWithReadThread(), fixing tests.

mgorny updated this revision to Diff 455291.Aug 24 2022, 11:04 AM

Rebase/update for the new error passing mechanism.

labath added inline comments.Aug 26 2022, 6:52 AM
lldb/source/Core/Communication.cpp
427

Have you considered putting this code (some version of it) inside InterruptRead? Basically replacing the select call inside BytesAvailable with something MainLoop-based ?

mgorny updated this revision to Diff 455951.Aug 26 2022, 10:15 AM

Rebase, move StopReadThread() test here.

mgorny added inline comments.Aug 28 2022, 10:43 PM
lldb/source/Core/Communication.cpp
427

To be honest, I've been considering removing InterruptRead() entirely, now that the read loop is triggered by available input rather than read-with-timeout. However, it's still used by editline support.

That said, I'm not sure what's your idea, given that Connection does not have awareness of Communication that's using it. I suppose I could pass the MainLoop instance as a parameter but we'd still have to maintain a separate version for editline support, and we only have a single caller here.

labath added inline comments.Aug 31 2022, 6:39 AM
lldb/source/Core/Communication.cpp
427

To be honest, I've been considering removing InterruptRead() entirely, now that the read loop is triggered by available input rather than read-with-timeout. However, it's still used by editline support.

If we could do that, then it would be even better. And it looks like it should be possible to use a MainLoop instance inside the Editline class, instead of the built-in interruption support.

That said, I'm not sure what's your idea, given that Connection does not have awareness of Communication that's using it. I suppose I could pass the MainLoop instance as a parameter but we'd still have to maintain a separate version for editline support, and we only have a single caller here.

I don't claim to have thought this out completely, but I was imagining that the MainLoop instance would be internal to the Connection class. The external interface of the Connection class would remain unchanged (so the Communication class would not need to change), and the InterruptRead function would be implemented using the MainLoop functionality.

mgorny added inline comments.Sep 1 2022, 4:25 AM
lldb/source/Core/Communication.cpp
427

That said, I'm not sure what's your idea, given that Connection does not have awareness of Communication that's using it. I suppose I could pass the MainLoop instance as a parameter but we'd still have to maintain a separate version for editline support, and we only have a single caller here.

I don't claim to have thought this out completely, but I was imagining that the MainLoop instance would be internal to the Connection class. The external interface of the Connection class would remain unchanged (so the Communication class would not need to change), and the InterruptRead function would be implemented using the MainLoop functionality.

So basically make ConnectionFileDescriptor::BytesAvailable() use a main loop to wait for some event, and make ConnectionFileDescriptor::InterruptRead() interrupt that?

I suppose both options are feasible but I don't have sufficient foresight to tell which one is better. That said, I have no clue about ConnectionGenericFileWindows and I feel that using MainLoop would make it possible to reduce code duplication between it and CFD. Though I'd preferred that someone with access to a Windows environment done that.

labath added inline comments.Sep 5 2022, 6:19 AM
lldb/source/Core/Communication.cpp
427

So basically make ConnectionFileDescriptor::BytesAvailable() use a main loop to wait for some event, and make ConnectionFileDescriptor::InterruptRead() interrupt that?

Correct.

I suppose both options are feasible but I don't have sufficient foresight to tell which one is better. That said, I have no clue about ConnectionGenericFileWindows and I feel that using MainLoop would make it possible to reduce code duplication between it and CFD. Though I'd preferred that someone with access to a Windows environment done that.

That is my hope as well, but it's not going to be very easy, since the ReadFile (used by CGFW) api requires you to actually attempt to read some data in order to wait for the result) . That said, I'm not sure this really speaks in favour of one approach over the other. If e.g. we are able to use the same MainLoop code for both CFD and CGFW, then we could probably move that code over to a common base class.

What I would like to avoid is to increase the number of places that do selects. So, if you can port editline code to something mainloop-based and remove the InterruptRead, then that's fine by me, and we can figure out what to do with windows later.

mgorny updated this revision to Diff 458016.Sep 5 2022, 8:40 AM

Rebase on top of D131160.

mgorny added inline comments.Sep 6 2022, 1:08 AM
lldb/source/Core/Communication.cpp
427

What I would like to avoid is to increase the number of places that do selects. So, if you can port editline code to something mainloop-based and remove the InterruptRead, then that's fine by me, and we can figure out what to do with windows later.

Well, I suppose I'll add that to my TODO and maybe I'll manage to get to it before I retire ;-). Also note that the proposed gdb-remote interrupt changes rely on InterruptRead(), so I'd have to change that as well. However, I think it's better to focus on multiprocess first, as that definitely will influence the final shape of how we do async there.