I realized after trying to reproduce this more, that I was just misunderstanding our logs and how gradle works. The issue was not that multiple doFirst actions were running in parallel, it’s just that there is no guarantee about ordering. doFirst actions run independently from other task actions. So multiple doFirst actions from different tasks can run, and then other task actions will run, even if only one is allowed to run at a time. It would be convenient to have concept of chained actions that must run together, but it doesn’t work that way.
So I don’t think there is a gradle bug here, just me not understanding how gradle works, and inheriting some code that had a hacky work around to avoid how gradle works wrt to doFirst actions and the rest of the actions for that task.
I was able to refactor all of this code to avoid the issue by moving more of the code to a build service so that the service is responsible for listening on the external emulator logcat resource, rather than trying to have tasks synchronize the adding and removing of listeners. There was another unrelated blocker why it wasn’t implemented that way in the first place, but I found a way around it.
Thanks for the responses.