jimingham wrote: Your solution is going in the right direction, but is trying to solve a problem "after the fact" that we can solve better by preventing the public stop events from making their way to the non-primary listener before the primary listener gets a chance to fully process them.
Process event listeners are a bit special because we have to keep the "process event state" in sync with the value returned from Process::GetState. It would be hard to figure out the meaning of events if for instance you could still have the "stopped" event sitting unfetched in the event queue, but the process state were shown as stopped. To ensure this, we designate one of the listeners (the one passed into the Process constructor) as the "primary process event listener". That one gets sent the event first, and we're supposed to only change the process state when the primary listener has finished handling the event. Note, while a listener that hijacks the process event broadcaster gets designated as the primary event listener while it is hijacking the broadcaster, the converse is not true, the primary listener does not have to be hijacking the process event stream. In fact, it's usually just the listener you passed in when you created the Process. So why doesn't that all just work? The problem is that when lldb goes to run the DoOnRemoval callback for the Process event (that's what calls your python breakpoint code, for instance), the code needs to see the process state as stopped - so that any commands will execute in the right context. Currently, the only way to do that is to switch the public state to stopped. But as you correctly analyzed, if you set the public state to stopped while we're in the middle of handling the stop, then any other code that's waiting on the state change will trigger, possibly before the breakpoint has decided whether to stop or not, which is incorrect. To really honor the contract above, we need to make sure the stop event doesn't get delivered to the non-primary listeners till the stop is fully handled. We already have code to deal with a slightly different aspect of this. The other reason that process events are a bit special is that we know that to implement a "user level operation" like source line stepping, the debugger often has to stop and start numerous times. Those stops have no meaning at the user level, so we want to suppress them. The way do that is to have a two stage handling of process events and their corresponding "state". First the "private" side of lldb fetches the event (usually on the private state thread), and sets the "private state" to stopped without changing the "public state". Then we run the thread plan logic is run to decide whether we are indeed going to stop to the user. Then we rebroadcast the event to the primary process listener - and as a result set the "public state" to stopped. But the code that runs the "Thread Plans" on the private state thread needs to see the state as stopped. We implement this by making Process::GetState smart about who is using it. If you look at Process::GetState, you will see it doesn't just return the public state. That's because all the thread plan logic has to run on the Private State Thread before it is appropriate to set the public state to stopped. Instead, when code running on the "Private State Thread" asks for the process state, it will get the private state. But code running on any other thread will still see the public state. That's done by checking "CurrentThreadIsPrivateStateThread" in Process::GetState. I think the correct solution to this problem is to add the thread that's going to run DoOnRemoval for the primary process event listener to the private state thread so that both of them will see the private state in Process::GetState. Then we only flip the public state when DoOnRemoval is all the way done . That way - like with the private event handling - the code doing the handling will see the right process state, so it will function correctly. And then only when the callback has completed will the public state get switched (causing the event to be broadcast to the other clients waiting on the change in public state.) That way the thread that is running the breakpoint command will run uninterrupted but in the correct context without any of the other threads waiting on the process knowing about the stop too early. And that way the non-primary process event listeners will be purely advisory as they are supposed to be. https://github.com/llvm/llvm-project/pull/144919 _______________________________________________ lldb-commits mailing list lldb-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits