Jim is looking into adding support for interrupts to the SB API. Part of that work requires clearing the interrupt bit once we return from the SB API. Luckily, this is already tracked by the API instrumentation. This patch adds the necessary interruption boilerplate to hook it up with the SB API instrumentation.
Details
Diff Detail
Event Timeline
I don't know how this is intended to be used, but I find it a rather heavyweight approach towards implementing, well... anything. The idea that something as innocuous as SBAddress::IsValid() will end up iterating through _all_ SBDebugger objects seems wrong, regardless of whether it has a measurable performance impact (and I'd be surprised if it doesn't). I'd hope that the interrupter can at least know which debugger it is trying to interrupt.
It's also not clear how something like this can be used to reliably interrupt an activity, as it's prone to all sorts of race conditions. (If the interuptee has not started the blocking call yet, then the interrupt request can be "eaten" by an innocuous IsValid call, whereas if it has already finished, then the interrupt request can remain pending and interrupt a totally unrelated call).
Overall, I think it would be good to have a RFC on this.
I've added you to D133410, as I think it demonstrates very well the difficulties in getting two threads to talk to one another. This code has been here since forever (and may have contributed to your desire to introduce interrupts), but we've only found the problem now, when Michał added some unit tests for this class.
To be clear, I'm not trying to implement a preemptive interrupt using these callbacks. There are so many places where lldb is doing work that you really can't interrupt - e.g. we can't drop symbol parsing and then pick it up again later - that that doesn't really even make sense.
I started out with the goal of extending the current InterruptCommand/WasInterrupted style of voluntary interruption to more places in lldb. A voluntary "I'm doing something in a loop that can be interrupted, so I'll check for interruption at the top of the loop" mechanism seems like the best fit for lldb, so that structure makes sense. But we only check for interruption a few place in the "target modules" commands. It would be useful to have other places check - e.g. if you did a bt and there were 40000 frames and you don't want to wait for us to print them all out... So I was going to look into adding more interrupt checks.
But then it seemed a shame that setting this interrupt only works when you are running command line commands, there's no reason this can't work for SB API calls as well, then UI's could also allow this sort of interruption. If the UI code is doing something in a loop, then it's up to the UI code to handle interrupting that operation. So all I'm trying to do is set the interrupt flag for use while the currently executing SB API is in flight, then turning it off when that call exits.
The debugger is the one setting the interrupt flag, so we always know who is sending the interrupt. The tricky bit is how to turn off the "WasInterrupted" flag after the API that was in flight when the flag was set finishes. That requires as a first step the ability to mark the "top level SB API" boundaries, and that's what Jonas was showing me how to do here. With these callbacks, some parts of this tracking will be straightforward, like by tracking enter and leave I can drop the interrupt flag immediately if there are no API's currently running. And if there were only one API in flight, turning the flag off at the right time is straight forward.
But you are right, there are tricky bits around handling two SB API's in flight on different threads. If they are for the same debugger, we probably do want to interrupt both. However, you don't want a short operation on thread A to turn off the interrupt flag before a long operation on thread B gets a chance to check it. So we'll probably want a counter and not just a flag. Also, it doesn't make sense for one Debugger's interrupt to interrupt the actions of another, so somehow we're going to have segregate API calls based on debugger, which I don't know how to do yet.
Anyway, this patch was mainly Jonas showing me how to get hook points for the enter and leave point for the top-level SB API calls so I could start to play with how to do this. TTTT I thought it would be more complex than this, and so it seemed worthwhile to add the general facility downstream. This is sufficiently straightforward, however, that I am fine with keeping it locally while I play with it.
This isn't really a new feature, it's just extending lldb's current voluntary interrupt mechanism to work that initiates from the SB API as well as from the command line. But once I get a better idea of how to do this, depending on how tricky it ends up being I'll either put up a patch or an RFC.
I agree with all that.
So all I'm trying to do is set the interrupt flag for use while the currently executing SB API is in flight, then turning it off when that call exits.
The problem is that, even with a single SB call, it's very hard to tell the difference between "I am doing X" vs. "I am about to do X" vs. "I have just completed X (but haven't told anyone about it)". And I don't see a way to do that reliably through any kind of automatic API boundary tracking.
The debugger is the one setting the interrupt flag, so we always know who is sending the interrupt. The tricky bit is how to turn off the "WasInterrupted" flag after the API that was in flight when the flag was set finishes.
Maybe the solution is to not (automatically) turn off the flag -- but put it in the hands of the user instead? If the interrupt flag was sticky (with explicit SetInterrupt/ClearInterrupt actions), then one can handle all of the scenarios above fairly easily.
It doesn't matter that whether SetInterrupt is called before the blocking call or not -- the call is going to be interrupted anyway. And one can explicitly clear the interrupt flag after the blocking call returns (either successfully or because it was interrupted).
I didn't see it as our job to make this distinction. If an IDE is about to do X or is just done with X and the user hits the IDE's pause button, then it's up to the IDE to pause whatever it was doing before and after SB API calls. We're not going to be able to interrupt anything that goes on between SB API calls anyway, so I thought it would be easier for UI's to reason about if all we did was interrupt in the current in-flight API call, which we should be able to use boundary tracking for.
The debugger is the one setting the interrupt flag, so we always know who is sending the interrupt. The tricky bit is how to turn off the "WasInterrupted" flag after the API that was in flight when the flag was set finishes.
Maybe the solution is to not (automatically) turn off the flag -- but put it in the hands of the user instead? If the interrupt flag was sticky (with explicit SetInterrupt/ClearInterrupt actions), then one can handle all of the scenarios above fairly easily.
It doesn't matter that whether SetInterrupt is called before the blocking call or not -- the call is going to be interrupted anyway. And one can explicitly clear the interrupt flag after the blocking call returns (either successfully or because it was interrupted).
That's an interesting thought. If we did that we'd have to separate the command line interrupt flags from the SB API ones w.r.t. setting/unsetting: a sticky flag is not the behavior you want for commands, but then check both in WasInterrupted. I can't see a reason why we'd need to distinguish the two sources of interrupt at the point where we go to check. We'd also have to never set the flag ourselves, having UI's have to spam turning this off would be super annoying, so this would have to be strictly controlled from outside lldb. But that should be okay, if we want this behavior in the driver we can implement it outside the lldb library in the driver. That just leaves me with the problem (even more acute with a sticky flag) of not interrupting API calls for the Debugger that didn't issue the interrupt.