Waiting for a possibly tight sequence of events

I have an odd use case (simulation) where a quite common scenario is that a coroutine wants to monitor some sequence of events in an environment, and react when a particular pattern is detected. Typically we observe a sequence of three things A, B and C happening, and then inject something into the simulation. The simulation happens in a thread different from trio, so if I write await wait_a(); await wait_b(); await wait_c(); then there’s a race (perhaps B happens soon after A, before wait_b() intercepts it).

Do you have any recommendations on how to solve this cleanly? I can think of three approaces: (1) produce a trace of all A B C events, feed that to a channel, and let wait_{abc} await the next matching element in that channel, or (2) solve the problem in the simulation thread without coroutines, e.g. create a DSL to express a compound event “sequence of [A, B, C]” and await that, or (3) integrate a trio event loop into the simulation thread somehow so we can guarantee an early wakeup.

1 works but gives lots of boilerplate (need to list which events the channel should start/stop tracing); 2 sort-of-works but you lose the elegance of coroutines, 3 feels highly hazardous.